Skip to content

Commit 7d0cc5f

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 6d3db55 commit 7d0cc5f

File tree

5 files changed

+89
-2
lines changed

5 files changed

+89
-2
lines changed

compiler/rustc_abi/src/layout.rs

+35-2
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

+14
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,9 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
16871687
/// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment
16881688
/// in some cases.
16891689
pub unadjusted_abi_align: Align,
1690+
1691+
/// The randomization seed based on this type's own repr and its fields'.
1692+
pub randomization_seed: u64,
16901693
}
16911694

16921695
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
@@ -1707,6 +1710,14 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
17071710
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
17081711
let size = scalar.size(cx);
17091712
let align = scalar.align(cx);
1713+
1714+
let seed_extra = match scalar.primitive() {
1715+
Primitive::Int(_, true) => 1,
1716+
Primitive::Int(_, false) => 2,
1717+
Primitive::Float(_) => 3,
1718+
Primitive::Pointer(_) => 4,
1719+
};
1720+
17101721
LayoutData {
17111722
variants: Variants::Single { index: VariantIdx::new(0) },
17121723
fields: FieldsShape::Primitive,
@@ -1716,6 +1727,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
17161727
align,
17171728
max_repr_align: None,
17181729
unadjusted_abi_align: align.abi,
1730+
randomization_seed: size.bytes().wrapping_add(seed_extra << 32),
17191731
}
17201732
}
17211733
}
@@ -1738,6 +1750,7 @@ where
17381750
variants,
17391751
max_repr_align,
17401752
unadjusted_abi_align,
1753+
ref randomization_seed,
17411754
} = self;
17421755
f.debug_struct("Layout")
17431756
.field("size", size)
@@ -1748,6 +1761,7 @@ where
17481761
.field("variants", variants)
17491762
.field("max_repr_align", max_repr_align)
17501763
.field("unadjusted_abi_align", unadjusted_abi_align)
1764+
.field("randomization_seed", randomization_seed)
17511765
.finish()
17521766
}
17531767
}

compiler/rustc_middle/src/ty/layout.rs

+1
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

+10
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

+29
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)