Skip to content

Commit a75617c

Browse files
committed
Foo<T> != Foo<U> under layout randomization
previously field ordering was using the same seed for all instances of Foo, now we pass seed values through the layout tree so that not only the struct itself affects layout but also its fields
1 parent a580b5c commit a75617c

File tree

5 files changed

+114
-2
lines changed

5 files changed

+114
-2
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
119119
.chain(Niche::from_scalar(dl, Size::ZERO, a))
120120
.max_by_key(|niche| niche.available(dl));
121121

122+
let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes());
123+
122124
LayoutData {
123125
variants: Variants::Single { index: VariantIdx::new(0) },
124126
fields: FieldsShape::Arbitrary {
@@ -131,6 +133,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
131133
size,
132134
max_repr_align: None,
133135
unadjusted_abi_align: align.abi,
136+
randomization_seed: combined_seed,
134137
}
135138
}
136139

@@ -223,6 +226,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
223226
size: Size::ZERO,
224227
max_repr_align: None,
225228
unadjusted_abi_align: dl.i8_align.abi,
229+
randomization_seed: 0,
226230
}
227231
}
228232

@@ -385,6 +389,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
385389
return Err(LayoutCalculatorError::EmptyUnion);
386390
};
387391

392+
let combined_seed = only_variant
393+
.iter()
394+
.map(|v| v.randomization_seed)
395+
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
396+
388397
Ok(LayoutData {
389398
variants: Variants::Single { index: only_variant_idx },
390399
fields: FieldsShape::Union(union_field_count),
@@ -394,6 +403,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
394403
size: size.align_to(align.abi),
395404
max_repr_align,
396405
unadjusted_abi_align,
406+
randomization_seed: combined_seed,
397407
})
398408
}
399409

@@ -650,6 +660,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
650660
BackendRepr::Memory { sized: true }
651661
};
652662

663+
let combined_seed = variant_layouts
664+
.iter()
665+
.map(|v| v.randomization_seed)
666+
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
667+
653668
let layout = LayoutData {
654669
variants: Variants::Multiple {
655670
tag: niche_scalar,
@@ -671,6 +686,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
671686
align,
672687
max_repr_align,
673688
unadjusted_abi_align,
689+
randomization_seed: combined_seed,
674690
};
675691

676692
Some(TmpLayout { layout, variants: variant_layouts })
@@ -961,6 +977,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
961977

962978
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
963979

980+
let combined_seed = layout_variants
981+
.iter()
982+
.map(|v| v.randomization_seed)
983+
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
984+
964985
let tagged_layout = LayoutData {
965986
variants: Variants::Multiple {
966987
tag,
@@ -978,6 +999,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
978999
size,
9791000
max_repr_align,
9801001
unadjusted_abi_align,
1002+
randomization_seed: combined_seed,
9811003
};
9821004

9831005
let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
@@ -1029,6 +1051,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10291051
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
10301052
let mut max_repr_align = repr.align;
10311053
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
1054+
let field_seed =
1055+
fields.raw.iter().fold(0u64, |acc, f| acc.wrapping_add(f.randomization_seed));
10321056
let optimize_field_order = !repr.inhibit_struct_field_reordering();
10331057
if optimize_field_order && fields.len() > 1 {
10341058
let end =
@@ -1046,8 +1070,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10461070
use rand::seq::SliceRandom;
10471071
// `ReprOptions.field_shuffle_seed` is a deterministic seed we can use to randomize field
10481072
// ordering.
1049-
let mut rng =
1050-
rand_xoshiro::Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed);
1073+
let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64(
1074+
field_seed.wrapping_add(repr.field_shuffle_seed),
1075+
);
10511076

10521077
// Shuffle the ordering of the fields.
10531078
optimizing.shuffle(&mut rng);
@@ -1344,6 +1369,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
13441369
unadjusted_abi_align
13451370
};
13461371

1372+
// a transparent struct only has a single field, so its seed should be the same as the one we pass forward
1373+
let seed = if repr.transparent() {
1374+
field_seed
1375+
} else {
1376+
field_seed.wrapping_add(repr.field_shuffle_seed)
1377+
};
1378+
13471379
Ok(LayoutData {
13481380
variants: Variants::Single { index: VariantIdx::new(0) },
13491381
fields: FieldsShape::Arbitrary { offsets, memory_index },
@@ -1353,6 +1385,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
13531385
size,
13541386
max_repr_align,
13551387
unadjusted_abi_align,
1388+
randomization_seed: seed,
13561389
})
13571390
}
13581391

compiler/rustc_abi/src/lib.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,18 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
17191719
/// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment
17201720
/// in some cases.
17211721
pub unadjusted_abi_align: Align,
1722+
1723+
/// The randomization seed based on this type's own repr and its fields.
1724+
///
1725+
/// Since randomization is toggled on a per-crate basis even crates that do not have randomization
1726+
/// enabled should still calculate a seed so that downstream uses can use it to distinguish different
1727+
/// types.
1728+
///
1729+
/// For every T and U for which we do not guarantee that a repr(Rust) `Foo<T>` can be coerced or
1730+
/// transmuted to `Foo<U>` we aim to create probalistically distinct seeds so that Foo can choose
1731+
/// to reorder its fields based on that information. The current implementation is a conservative
1732+
/// approximation of this goal.
1733+
pub randomization_seed: u64,
17221734
}
17231735

17241736
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
@@ -1739,6 +1751,30 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
17391751
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
17401752
let size = scalar.size(cx);
17411753
let align = scalar.align(cx);
1754+
1755+
let range = scalar.valid_range(cx);
1756+
1757+
// All primitive types for which we don't have subtype coercions should get a distinct seed,
1758+
// so that types wrapping them can use randomization to arrive at distinct layouts.
1759+
//
1760+
// Some type information is already lost at this point, so as an approximation we derive
1761+
// the seed from what remains. For example on 64-bit targets usize and u64 can no longer
1762+
// be distinguished.
1763+
let randomization_seed = size
1764+
.bytes()
1765+
.wrapping_add(
1766+
match scalar.primitive() {
1767+
Primitive::Int(_, true) => 1,
1768+
Primitive::Int(_, false) => 2,
1769+
Primitive::Float(_) => 3,
1770+
Primitive::Pointer(_) => 4,
1771+
} << 32,
1772+
)
1773+
// distinguishes references from pointers
1774+
.wrapping_add((range.start as u64).rotate_right(16))
1775+
// distinguishes char from u32 and bool from u8
1776+
.wrapping_add((range.end as u64).rotate_right(16));
1777+
17421778
LayoutData {
17431779
variants: Variants::Single { index: VariantIdx::new(0) },
17441780
fields: FieldsShape::Primitive,
@@ -1748,6 +1784,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
17481784
align,
17491785
max_repr_align: None,
17501786
unadjusted_abi_align: align.abi,
1787+
randomization_seed,
17511788
}
17521789
}
17531790
}
@@ -1770,6 +1807,7 @@ where
17701807
variants,
17711808
max_repr_align,
17721809
unadjusted_abi_align,
1810+
ref randomization_seed,
17731811
} = self;
17741812
f.debug_struct("Layout")
17751813
.field("size", size)
@@ -1780,6 +1818,7 @@ where
17801818
.field("variants", variants)
17811819
.field("max_repr_align", max_repr_align)
17821820
.field("unadjusted_abi_align", unadjusted_abi_align)
1821+
.field("randomization_seed", randomization_seed)
17831822
.finish()
17841823
}
17851824
}

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,7 @@ where
770770
size: Size::ZERO,
771771
max_repr_align: None,
772772
unadjusted_abi_align: tcx.data_layout.i8_align.abi,
773+
randomization_seed: 0,
773774
})
774775
}
775776

compiler/rustc_ty_utils/src/layout.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ fn layout_of_uncached<'tcx>(
347347
size,
348348
max_repr_align: None,
349349
unadjusted_abi_align: element.align.abi,
350+
randomization_seed: element.randomization_seed.wrapping_add(count),
350351
})
351352
}
352353
ty::Slice(element) => {
@@ -360,6 +361,8 @@ fn layout_of_uncached<'tcx>(
360361
size: Size::ZERO,
361362
max_repr_align: None,
362363
unadjusted_abi_align: element.align.abi,
364+
// adding a randomly chosen value to distinguish slices
365+
randomization_seed: element.randomization_seed.wrapping_add(0x2dcba99c39784102),
363366
})
364367
}
365368
ty::Str => tcx.mk_layout(LayoutData {
@@ -371,6 +374,8 @@ fn layout_of_uncached<'tcx>(
371374
size: Size::ZERO,
372375
max_repr_align: None,
373376
unadjusted_abi_align: dl.i8_align.abi,
377+
// another random value
378+
randomization_seed: 0xc1325f37d127be22,
374379
}),
375380

376381
// Odd unit types.
@@ -542,6 +547,7 @@ fn layout_of_uncached<'tcx>(
542547
align,
543548
max_repr_align: None,
544549
unadjusted_abi_align: align.abi,
550+
randomization_seed: e_ly.randomization_seed.wrapping_add(e_len),
545551
})
546552
}
547553

@@ -999,6 +1005,9 @@ fn coroutine_layout<'tcx>(
9991005
BackendRepr::Memory { sized: true }
10001006
};
10011007

1008+
// this is similar to how ReprOptions populates its field_shuffle_seed
1009+
let def_hash = tcx.def_path_hash(def_id).0.to_smaller_hash().as_u64();
1010+
10021011
let layout = tcx.mk_layout(LayoutData {
10031012
variants: Variants::Multiple {
10041013
tag,
@@ -1019,6 +1028,7 @@ fn coroutine_layout<'tcx>(
10191028
align,
10201029
max_repr_align: None,
10211030
unadjusted_abi_align: align.abi,
1031+
randomization_seed: def_hash,
10221032
});
10231033
debug!("coroutine layout ({:?}): {:#?}", ty, layout);
10241034
Ok(layout)

tests/ui/layout/randomize.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ build-pass
2+
//@ revisions: normal randomize-layout
3+
//@ [randomize-layout]compile-flags: -Zrandomize-layout
4+
5+
#![crate_type = "lib"]
6+
7+
struct Foo<T>(u32, T, u8);
8+
9+
struct Wrapper<T>(T);
10+
11+
#[repr(transparent)]
12+
struct TransparentWrapper(u16);
13+
14+
const _: () = {
15+
// behavior of the current implementation, not guaranteed
16+
#[cfg(not(randomize_layout))]
17+
assert!(std::mem::offset_of!(Foo::<u16>, 1) == std::mem::offset_of!(Foo::<Wrapper<u16>>, 1));
18+
19+
// under randomization Foo<T> != Foo<U>
20+
#[cfg(randomize_layout)]
21+
assert!(std::mem::offset_of!(Foo::<u16>, 1) != std::mem::offset_of!(Foo::<Wrapper<u16>>, 1));
22+
23+
// but repr(transparent) should make them the same again.
24+
// maybe not strictly guaranteed? but UCG has been leaning in that direction at least
25+
#[cfg(randomize_layout)]
26+
assert!(
27+
std::mem::offset_of!(Foo::<u16>, 1) == std::mem::offset_of!(Foo::<TransparentWrapper>, 1)
28+
);
29+
};

0 commit comments

Comments
 (0)