Skip to content

Commit a1ea768

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 eff958c commit a1ea768

File tree

2 files changed

+78
-25
lines changed

2 files changed

+78
-25
lines changed

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+45-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(), &mut Vec::new(), 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, &mut Vec::new(), transform_ty_options);
189189
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
190190
}
191191

@@ -766,11 +766,12 @@ fn transform_predicates<'tcx>(
766766
fn transform_args<'tcx>(
767767
tcx: TyCtxt<'tcx>,
768768
args: GenericArgsRef<'tcx>,
769+
parents: &mut Vec<Ty<'tcx>>,
769770
options: TransformTyOptions,
770771
) -> GenericArgsRef<'tcx> {
771772
let args = args.iter().map(|arg| match arg.unpack() {
772773
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
773-
GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
774+
GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(),
774775
_ => arg,
775776
});
776777
tcx.mk_args_from_iter(args)
@@ -780,9 +781,12 @@ fn transform_args<'tcx>(
780781
// c_void types into unit types unconditionally, generalizes pointers if
781782
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
782783
// TransformTyOptions::NORMALIZE_INTEGERS option is set.
783-
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
784-
let mut ty = ty;
785-
784+
fn transform_ty<'tcx>(
785+
tcx: TyCtxt<'tcx>,
786+
mut ty: Ty<'tcx>,
787+
parents: &mut Vec<Ty<'tcx>>,
788+
options: TransformTyOptions,
789+
) -> Ty<'tcx> {
786790
match ty.kind() {
787791
ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {}
788792

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

844848
ty::Tuple(tys) => {
845-
ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options)));
849+
ty = Ty::new_tup_from_iter(
850+
tcx,
851+
tys.iter().map(|ty| transform_ty(tcx, ty, parents, options)),
852+
);
846853
}
847854

848855
ty::Array(ty0, len) => {
849856
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
850857

851-
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
858+
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, parents, options), len);
852859
}
853860

854861
ty::Slice(ty0) => {
855-
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options));
862+
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, parents, options));
856863
}
857864

858865
ty::Adt(adt_def, args) => {
@@ -861,7 +868,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
861868
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
862869
{
863870
ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
864-
} else if adt_def.repr().transparent() && adt_def.is_struct() {
871+
} else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty)
872+
{
865873
// Don't transform repr(transparent) types with an user-defined CFI encoding to
866874
// preserve the user-defined CFI encoding.
867875
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
@@ -880,38 +888,48 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
880888
// Generalize any repr(transparent) user-defined type that is either a pointer
881889
// or reference, and either references itself or any other type that contains or
882890
// references itself, to avoid a reference cycle.
891+
892+
// If the self reference is not through a pointer, for example, due
893+
// to using `PhantomData`, need to skip normalizing it if we hit it again.
894+
parents.push(ty);
883895
if ty0.is_any_ptr() && ty0.contains(ty) {
884896
ty = transform_ty(
885897
tcx,
886898
ty0,
899+
parents,
887900
options | TransformTyOptions::GENERALIZE_POINTERS,
888901
);
889902
} else {
890-
ty = transform_ty(tcx, ty0, options);
903+
ty = transform_ty(tcx, ty0, parents, options);
891904
}
905+
parents.pop();
892906
} else {
893907
// Transform repr(transparent) types without non-ZST field into ()
894908
ty = Ty::new_unit(tcx);
895909
}
896910
} else {
897-
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, options));
911+
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, parents, options));
898912
}
899913
}
900914

901915
ty::FnDef(def_id, args) => {
902-
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options));
916+
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, parents, options));
903917
}
904918

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

909923
ty::CoroutineClosure(def_id, args) => {
910-
ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options));
924+
ty = Ty::new_coroutine_closure(
925+
tcx,
926+
*def_id,
927+
transform_args(tcx, args, parents, options),
928+
);
911929
}
912930

913931
ty::Coroutine(def_id, args) => {
914-
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options));
932+
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, parents, options));
915933
}
916934

917935
ty::Ref(region, ty0, ..) => {
@@ -923,9 +941,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
923941
}
924942
} else {
925943
if ty.is_mutable_ptr() {
926-
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options));
944+
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
927945
} else {
928-
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options));
946+
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
929947
}
930948
}
931949
}
@@ -939,9 +957,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
939957
}
940958
} else {
941959
if ty.is_mutable_ptr() {
942-
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options));
960+
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, parents, options));
943961
} else {
944-
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options));
962+
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, parents, options));
945963
}
946964
}
947965
}
@@ -954,9 +972,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
954972
.skip_binder()
955973
.inputs()
956974
.iter()
957-
.map(|ty| transform_ty(tcx, *ty, options))
975+
.map(|ty| transform_ty(tcx, *ty, parents, options))
958976
.collect();
959-
let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
977+
let output = transform_ty(tcx, fn_sig.skip_binder().output(), parents, options);
960978
ty = Ty::new_fn_ptr(
961979
tcx,
962980
ty::Binder::bind_with_vars(
@@ -986,6 +1004,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
9861004
ty = transform_ty(
9871005
tcx,
9881006
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
1007+
parents,
9891008
options,
9901009
);
9911010
}
@@ -1036,7 +1055,7 @@ pub fn typeid_for_fnabi<'tcx>(
10361055
// Encode the return type
10371056
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
10381057
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
1039-
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
1058+
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &mut Vec::new(), transform_ty_options);
10401059
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10411060

10421061
// Encode the parameter types
@@ -1048,7 +1067,7 @@ pub fn typeid_for_fnabi<'tcx>(
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, &mut Vec::new(), transform_ty_options);
10521071
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10531072
}
10541073
if !pushed_arg {
@@ -1061,7 +1080,8 @@ 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 =
1084+
transform_ty(tcx, fn_abi.args[n].layout.ty, &mut Vec::new(), transform_ty_options);
10651085
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
10661086
}
10671087

tests/ui/sanitizer/cfi-self-ref.rs

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

0 commit comments

Comments
 (0)