Skip to content

Commit 415e2c4

Browse files
committed
CFI: Support self_cell-like recursion
Current `transform_ty` attempts to avoid cycles when normalizing `#[repr(transparent)]` types to their interior, but runs afoul of this pattern used in `self_cell`: ``` struct X<T> { x: u8, p: PhantomData<T>, } #[repr(transparent)] struct Y(X<Y>); ``` When attempting to normalize Y, it will still cycle indefinitely. By using a types-visited list, this will instead get expanded exactly one layer deep to X<Y>, and then stop, not attempting to normalize `Y` any further.
1 parent aaad579 commit 415e2c4

File tree

2 files changed

+75
-25
lines changed

2 files changed

+75
-25
lines changed

Diff for: compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+44-25
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,14 @@ fn encode_fnsig<'tcx>(
178178
// Encode the return type
179179
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
180180
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
181-
let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
181+
let ty = transform_ty(tcx, fn_sig.output(), &[], transform_ty_options);
182182
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
183183

184184
// Encode the parameter types
185185
let tys = fn_sig.inputs();
186186
if !tys.is_empty() {
187187
for ty in tys {
188-
let ty = transform_ty(tcx, *ty, transform_ty_options);
188+
let ty = transform_ty(tcx, *ty, &[], transform_ty_options);
189189
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
190190
}
191191

@@ -770,11 +770,12 @@ fn transform_predicates<'tcx>(
770770
fn transform_args<'tcx>(
771771
tcx: TyCtxt<'tcx>,
772772
args: GenericArgsRef<'tcx>,
773+
parents: &[Ty<'tcx>],
773774
options: TransformTyOptions,
774775
) -> GenericArgsRef<'tcx> {
775776
let args = args.iter().map(|arg| match arg.unpack() {
776777
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
777-
GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
778+
GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(),
778779
_ => arg,
779780
});
780781
tcx.mk_args_from_iter(args)
@@ -784,9 +785,12 @@ fn transform_args<'tcx>(
784785
// c_void types into unit types unconditionally, generalizes pointers if
785786
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
786787
// TransformTyOptions::NORMALIZE_INTEGERS option is set.
787-
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
788-
let mut ty = ty;
789-
788+
fn transform_ty<'tcx>(
789+
tcx: TyCtxt<'tcx>,
790+
mut ty: Ty<'tcx>,
791+
parents: &[Ty<'tcx>],
792+
options: TransformTyOptions,
793+
) -> Ty<'tcx> {
790794
match ty.kind() {
791795
ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {}
792796

@@ -846,17 +850,20 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
846850
_ if ty.is_unit() => {}
847851

848852
ty::Tuple(tys) => {
849-
ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options)));
853+
ty = Ty::new_tup_from_iter(
854+
tcx,
855+
tys.iter().map(|ty| transform_ty(tcx, ty, &parents, options)),
856+
);
850857
}
851858

852859
ty::Array(ty0, len) => {
853860
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
854861

855-
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
862+
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, &parents, options), len);
856863
}
857864

858865
ty::Slice(ty0) => {
859-
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options));
866+
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, &parents, options));
860867
}
861868

862869
ty::Adt(adt_def, args) => {
@@ -865,7 +872,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
865872
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
866873
{
867874
ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
868-
} else if adt_def.repr().transparent() && adt_def.is_struct() {
875+
} else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty)
876+
{
869877
// Don't transform repr(transparent) types with an user-defined CFI encoding to
870878
// preserve the user-defined CFI encoding.
871879
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
@@ -884,38 +892,48 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
884892
// Generalize any repr(transparent) user-defined type that is either a pointer
885893
// or reference, and either references itself or any other type that contains or
886894
// references itself, to avoid a reference cycle.
895+
896+
// If the self reference is not through a pointer, for example, due
897+
// to using `PhantomData`, need to skip normalizing it if we hit it again.
898+
let mut parents = parents.to_vec();
899+
parents.push(ty);
887900
if ty0.is_any_ptr() && ty0.contains(ty) {
888901
ty = transform_ty(
889902
tcx,
890903
ty0,
904+
&parents,
891905
options | TransformTyOptions::GENERALIZE_POINTERS,
892906
);
893907
} else {
894-
ty = transform_ty(tcx, ty0, options);
908+
ty = transform_ty(tcx, ty0, &parents, options);
895909
}
896910
} else {
897911
// Transform repr(transparent) types without non-ZST field into ()
898912
ty = Ty::new_unit(tcx);
899913
}
900914
} else {
901-
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, options));
915+
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, &parents, options));
902916
}
903917
}
904918

905919
ty::FnDef(def_id, args) => {
906-
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options));
920+
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, &parents, options));
907921
}
908922

909923
ty::Closure(def_id, args) => {
910-
ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options));
924+
ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, &parents, options));
911925
}
912926

913927
ty::CoroutineClosure(def_id, args) => {
914-
ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options));
928+
ty = Ty::new_coroutine_closure(
929+
tcx,
930+
*def_id,
931+
transform_args(tcx, args, &parents, options),
932+
);
915933
}
916934

917935
ty::Coroutine(def_id, args) => {
918-
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options));
936+
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, &parents, options));
919937
}
920938

921939
ty::Ref(region, ty0, ..) => {
@@ -927,9 +945,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
927945
}
928946
} else {
929947
if ty.is_mutable_ptr() {
930-
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options));
948+
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, &parents, options));
931949
} else {
932-
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options));
950+
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, &parents, options));
933951
}
934952
}
935953
}
@@ -943,9 +961,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
943961
}
944962
} else {
945963
if ty.is_mutable_ptr() {
946-
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options));
964+
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, &parents, options));
947965
} else {
948-
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options));
966+
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, &parents, options));
949967
}
950968
}
951969
}
@@ -958,9 +976,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
958976
.skip_binder()
959977
.inputs()
960978
.iter()
961-
.map(|ty| transform_ty(tcx, *ty, options))
979+
.map(|ty| transform_ty(tcx, *ty, &parents, options))
962980
.collect();
963-
let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
981+
let output = transform_ty(tcx, fn_sig.skip_binder().output(), &parents, options);
964982
ty = Ty::new_fn_ptr(
965983
tcx,
966984
ty::Binder::bind_with_vars(
@@ -990,6 +1008,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
9901008
ty = transform_ty(
9911009
tcx,
9921010
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
1011+
&parents,
9931012
options,
9941013
);
9951014
}
@@ -1040,15 +1059,15 @@ pub fn typeid_for_fnabi<'tcx>(
10401059
// Encode the return type
10411060
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
10421061
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
1043-
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
1062+
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &[], transform_ty_options);
10441063
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10451064

10461065
// Encode the parameter types
10471066
if !fn_abi.c_variadic {
10481067
let mut pushed_arg = false;
10491068
for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
10501069
pushed_arg = true;
1051-
let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
1070+
let ty = transform_ty(tcx, arg.layout.ty, &[], transform_ty_options);
10521071
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10531072
}
10541073
if !pushed_arg {
@@ -1061,7 +1080,7 @@ pub fn typeid_for_fnabi<'tcx>(
10611080
if fn_abi.args[n].mode == PassMode::Ignore {
10621081
continue;
10631082
}
1064-
let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
1083+
let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, &[], transform_ty_options);
10651084
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10661085
}
10671086

Diff for: tests/ui/sanitizer/cfi-self-ref.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Check that encoding self-referential types works with #[repr(transparent)]
2+
3+
//@ needs-sanitizer-cfi
4+
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
5+
//@ compile-flags: -C codegen-units=1 -C opt-level=0
6+
//@ build-pass
7+
8+
use std::marker::PhantomData;
9+
10+
struct X<T> {
11+
x: u8,
12+
p: PhantomData<T>,
13+
}
14+
15+
#[repr(transparent)]
16+
struct Y(X<Y>);
17+
18+
trait Fooable {
19+
fn foo(&self, y: Y);
20+
}
21+
22+
struct Bar;
23+
24+
impl Fooable for Bar {
25+
fn foo(&self, _: Y) {}
26+
}
27+
28+
fn main() {
29+
let x = &Bar as &dyn Fooable;
30+
x.foo(Y(X {x: 0, p: PhantomData}));
31+
}

0 commit comments

Comments
 (0)