diff options
| -rw-r--r-- | compiler/rustc_abi/src/layout.rs | 37 | ||||
| -rw-r--r-- | compiler/rustc_abi/src/lib.rs | 39 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/layout.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_ty_utils/src/layout.rs | 10 | ||||
| -rw-r--r-- | tests/ui/layout/randomize.rs | 29 |
5 files changed, 114 insertions, 2 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 226a46f605c..3b897c1cb3e 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -119,6 +119,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { .chain(Niche::from_scalar(dl, Size::ZERO, a)) .max_by_key(|niche| niche.available(dl)); + let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes()); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { @@ -131,6 +133,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { size, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: combined_seed, } } @@ -223,6 +226,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, } } @@ -385,6 +389,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { return Err(LayoutCalculatorError::EmptyUnion); }; + let combined_seed = only_variant + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + Ok(LayoutData { variants: Variants::Single { index: only_variant_idx }, fields: FieldsShape::Union(union_field_count), @@ -394,6 +403,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { size: size.align_to(align.abi), max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }) } @@ -650,6 +660,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { BackendRepr::Memory { sized: true } }; + let combined_seed = variant_layouts + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let layout = LayoutData { variants: Variants::Multiple { tag: niche_scalar, @@ -671,6 +686,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { align, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; Some(TmpLayout { layout, variants: variant_layouts }) @@ -961,6 +977,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag); + let combined_seed = layout_variants + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let tagged_layout = LayoutData { variants: Variants::Multiple { tag, @@ -978,6 +999,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { size, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants }; @@ -1029,6 +1051,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect(); + let field_seed = + fields.raw.iter().fold(0u64, |acc, f| acc.wrapping_add(f.randomization_seed)); let optimize_field_order = !repr.inhibit_struct_field_reordering(); if optimize_field_order && fields.len() > 1 { let end = @@ -1046,8 +1070,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { use rand::seq::SliceRandom; // `ReprOptions.field_shuffle_seed` is a deterministic seed we can use to randomize field // ordering. - let mut rng = - rand_xoshiro::Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed); + let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64( + field_seed.wrapping_add(repr.field_shuffle_seed), + ); // Shuffle the ordering of the fields. optimizing.shuffle(&mut rng); @@ -1344,6 +1369,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { unadjusted_abi_align }; + // a transparent struct only has a single field, so its seed should be the same as the one we pass forward + let seed = if repr.transparent() { + field_seed + } else { + field_seed.wrapping_add(repr.field_shuffle_seed) + }; + Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { offsets, memory_index }, @@ -1353,6 +1385,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { size, max_repr_align, unadjusted_abi_align, + randomization_seed: seed, }) } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7fa869a509c..8ad33749f34 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1719,6 +1719,18 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> { /// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment /// in some cases. pub unadjusted_abi_align: Align, + + /// The randomization seed based on this type's own repr and its fields. + /// + /// Since randomization is toggled on a per-crate basis even crates that do not have randomization + /// enabled should still calculate a seed so that downstream uses can use it to distinguish different + /// types. + /// + /// For every T and U for which we do not guarantee that a repr(Rust) `Foo<T>` can be coerced or + /// transmuted to `Foo<U>` we aim to create probalistically distinct seeds so that Foo can choose + /// to reorder its fields based on that information. The current implementation is a conservative + /// approximation of this goal. + pub randomization_seed: u64, } impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { @@ -1739,6 +1751,30 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); let align = scalar.align(cx); + + let range = scalar.valid_range(cx); + + // All primitive types for which we don't have subtype coercions should get a distinct seed, + // so that types wrapping them can use randomization to arrive at distinct layouts. + // + // Some type information is already lost at this point, so as an approximation we derive + // the seed from what remains. For example on 64-bit targets usize and u64 can no longer + // be distinguished. + let randomization_seed = size + .bytes() + .wrapping_add( + match scalar.primitive() { + Primitive::Int(_, true) => 1, + Primitive::Int(_, false) => 2, + Primitive::Float(_) => 3, + Primitive::Pointer(_) => 4, + } << 32, + ) + // distinguishes references from pointers + .wrapping_add((range.start as u64).rotate_right(16)) + // distinguishes char from u32 and bool from u8 + .wrapping_add((range.end as u64).rotate_right(16)); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Primitive, @@ -1748,6 +1784,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed, } } } @@ -1770,6 +1807,7 @@ where variants, max_repr_align, unadjusted_abi_align, + ref randomization_seed, } = self; f.debug_struct("Layout") .field("size", size) @@ -1780,6 +1818,7 @@ where .field("variants", variants) .field("max_repr_align", max_repr_align) .field("unadjusted_abi_align", unadjusted_abi_align) + .field("randomization_seed", randomization_seed) .finish() } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6e6da6de749..8d4d127607d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -770,6 +770,7 @@ where size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: tcx.data_layout.i8_align.abi, + randomization_seed: 0, }) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 9f138cf1275..fae787f9a40 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -347,6 +347,7 @@ fn layout_of_uncached<'tcx>( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: element.randomization_seed.wrapping_add(count), }) } ty::Slice(element) => { @@ -360,6 +361,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + // adding a randomly chosen value to distinguish slices + randomization_seed: element.randomization_seed.wrapping_add(0x2dcba99c39784102), }) } ty::Str => tcx.mk_layout(LayoutData { @@ -371,6 +374,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + // another random value + randomization_seed: 0xc1325f37d127be22, }), // Odd unit types. @@ -542,6 +547,7 @@ fn layout_of_uncached<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: e_ly.randomization_seed.wrapping_add(e_len), }) } @@ -999,6 +1005,9 @@ fn coroutine_layout<'tcx>( BackendRepr::Memory { sized: true } }; + // this is similar to how ReprOptions populates its field_shuffle_seed + let def_hash = tcx.def_path_hash(def_id).0.to_smaller_hash().as_u64(); + let layout = tcx.mk_layout(LayoutData { variants: Variants::Multiple { tag, @@ -1019,6 +1028,7 @@ fn coroutine_layout<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: def_hash, }); debug!("coroutine layout ({:?}): {:#?}", ty, layout); Ok(layout) diff --git a/tests/ui/layout/randomize.rs b/tests/ui/layout/randomize.rs new file mode 100644 index 00000000000..be895252a1b --- /dev/null +++ b/tests/ui/layout/randomize.rs @@ -0,0 +1,29 @@ +//@ build-pass +//@ revisions: normal randomize-layout +//@ [randomize-layout]compile-flags: -Zrandomize-layout + +#![crate_type = "lib"] + +struct Foo<T>(u32, T, u8); + +struct Wrapper<T>(T); + +#[repr(transparent)] +struct TransparentWrapper(u16); + +const _: () = { + // behavior of the current implementation, not guaranteed + #[cfg(not(randomize_layout))] + assert!(std::mem::offset_of!(Foo::<u16>, 1) == std::mem::offset_of!(Foo::<Wrapper<u16>>, 1)); + + // under randomization Foo<T> != Foo<U> + #[cfg(randomize_layout)] + assert!(std::mem::offset_of!(Foo::<u16>, 1) != std::mem::offset_of!(Foo::<Wrapper<u16>>, 1)); + + // but repr(transparent) should make them the same again. + // maybe not strictly guaranteed? but UCG has been leaning in that direction at least + #[cfg(randomize_layout)] + assert!( + std::mem::offset_of!(Foo::<u16>, 1) == std::mem::offset_of!(Foo::<TransparentWrapper>, 1) + ); +}; |
