Skip to content

Commit 37df275

Browse files
authoredOct 1, 2021
Rollup merge of #87868 - Kixiron:packing-on-the-pounds, r=eddyb
Added -Z randomize-layout flag An implementation of #77316, it currently randomly shuffles the fields of `repr(rust)` types based on their `DefPathHash` r? ``@eddyb``
2 parents ed93759 + 09f1542 commit 37df275

File tree

6 files changed

+93
-19
lines changed

6 files changed

+93
-19
lines changed
 

‎Cargo.lock

+16-5
Original file line numberDiff line numberDiff line change
@@ -1660,7 +1660,7 @@ checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f"
16601660
dependencies = [
16611661
"bitmaps",
16621662
"rand_core 0.5.1",
1663-
"rand_xoshiro",
1663+
"rand_xoshiro 0.4.0",
16641664
"sized-chunks",
16651665
"typenum",
16661666
"version_check",
@@ -2256,7 +2256,7 @@ dependencies = [
22562256
"libc",
22572257
"log",
22582258
"measureme",
2259-
"rand 0.8.3",
2259+
"rand 0.8.4",
22602260
"rustc-workspace-hack",
22612261
"rustc_version",
22622262
"shell-escape",
@@ -2852,9 +2852,9 @@ dependencies = [
28522852

28532853
[[package]]
28542854
name = "rand"
2855-
version = "0.8.3"
2855+
version = "0.8.4"
28562856
source = "registry+https://github.com/rust-lang/crates.io-index"
2857-
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
2857+
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
28582858
dependencies = [
28592859
"libc",
28602860
"rand_chacha 0.3.0",
@@ -2945,6 +2945,15 @@ dependencies = [
29452945
"rand_core 0.5.1",
29462946
]
29472947

2948+
[[package]]
2949+
name = "rand_xoshiro"
2950+
version = "0.6.0"
2951+
source = "registry+https://github.com/rust-lang/crates.io-index"
2952+
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
2953+
dependencies = [
2954+
"rand_core 0.6.2",
2955+
]
2956+
29482957
[[package]]
29492958
name = "rayon"
29502959
version = "1.3.1"
@@ -4087,6 +4096,8 @@ dependencies = [
40874096
"either",
40884097
"gsgdt",
40894098
"polonius-engine",
4099+
"rand 0.8.4",
4100+
"rand_xoshiro 0.6.0",
40904101
"rustc-rayon-core",
40914102
"rustc_apfloat",
40924103
"rustc_arena",
@@ -5097,7 +5108,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
50975108
dependencies = [
50985109
"cfg-if 1.0.0",
50995110
"libc",
5100-
"rand 0.8.3",
5111+
"rand 0.8.4",
51015112
"redox_syscall",
51025113
"remove_dir_all",
51035114
"winapi",

‎compiler/rustc_middle/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ chalk-ir = "0.55.0"
3232
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
3333
rustc_session = { path = "../rustc_session" }
3434
rustc_type_ir = { path = "../rustc_type_ir" }
35+
rand = "0.8.4"
36+
rand_xoshiro = "0.6.0"

‎compiler/rustc_middle/src/ty/layout.rs

+35-13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ use std::iter;
2424
use std::num::NonZeroUsize;
2525
use std::ops::Bound;
2626

27+
use rand::{seq::SliceRandom, SeedableRng};
28+
use rand_xoshiro::Xoshiro128StarStar;
29+
2730
pub fn provide(providers: &mut ty::query::Providers) {
2831
*providers =
2932
ty::query::Providers { layout_of, fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers };
@@ -324,6 +327,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
324327

325328
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
326329

330+
// `ReprOptions.layout_seed` is a deterministic seed that we can use to
331+
// randomize field ordering with
332+
let mut rng = Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed);
333+
327334
let optimize = !repr.inhibit_struct_field_reordering_opt();
328335
if optimize {
329336
let end =
@@ -332,20 +339,35 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
332339
let field_align = |f: &TyAndLayout<'_>| {
333340
if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi }
334341
};
335-
match kind {
336-
StructKind::AlwaysSized | StructKind::MaybeUnsized => {
337-
optimizing.sort_by_key(|&x| {
338-
// Place ZSTs first to avoid "interesting offsets",
339-
// especially with only one or two non-ZST fields.
340-
let f = &fields[x as usize];
341-
(!f.is_zst(), cmp::Reverse(field_align(f)))
342-
});
343-
}
344-
StructKind::Prefixed(..) => {
345-
// Sort in ascending alignment so that the layout stay optimal
346-
// regardless of the prefix
347-
optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
342+
343+
// If `-Z randomize-layout` was enabled for the type definition we can shuffle
344+
// the field ordering to try and catch some code making assumptions about layouts
345+
// we don't guarantee
346+
if repr.can_randomize_type_layout() {
347+
// Shuffle the ordering of the fields
348+
optimizing.shuffle(&mut rng);
349+
350+
// Otherwise we just leave things alone and actually optimize the type's fields
351+
} else {
352+
match kind {
353+
StructKind::AlwaysSized | StructKind::MaybeUnsized => {
354+
optimizing.sort_by_key(|&x| {
355+
// Place ZSTs first to avoid "interesting offsets",
356+
// especially with only one or two non-ZST fields.
357+
let f = &fields[x as usize];
358+
(!f.is_zst(), cmp::Reverse(field_align(f)))
359+
});
360+
}
361+
362+
StructKind::Prefixed(..) => {
363+
// Sort in ascending alignment so that the layout stays optimal
364+
// regardless of the prefix
365+
optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
366+
}
348367
}
368+
369+
// FIXME(Kixiron): We can always shuffle fields within a given alignment class
370+
// regardless of the status of `-Z randomize-layout`
349371
}
350372
}
351373

‎compiler/rustc_middle/src/ty/mod.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,9 @@ bitflags! {
14911491
const IS_LINEAR = 1 << 3;
14921492
// If true, don't expose any niche to type's context.
14931493
const HIDE_NICHE = 1 << 4;
1494+
// If true, the type's layout can be randomized using
1495+
// the seed stored in `ReprOptions.layout_seed`
1496+
const RANDOMIZE_LAYOUT = 1 << 5;
14941497
// Any of these flags being set prevent field reordering optimisation.
14951498
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
14961499
ReprFlags::IS_SIMD.bits |
@@ -1505,6 +1508,14 @@ pub struct ReprOptions {
15051508
pub align: Option<Align>,
15061509
pub pack: Option<Align>,
15071510
pub flags: ReprFlags,
1511+
/// The seed to be used for randomizing a type's layout
1512+
///
1513+
/// Note: This could technically be a `[u8; 16]` (a `u128`) which would
1514+
/// be the "most accurate" hash as it'd encompass the item and crate
1515+
/// hash without loss, but it does pay the price of being larger.
1516+
/// Everything's a tradeoff, a `u64` seed should be sufficient for our
1517+
/// purposes (primarily `-Z randomize-layout`)
1518+
pub field_shuffle_seed: u64,
15081519
}
15091520

15101521
impl ReprOptions {
@@ -1513,6 +1524,11 @@ impl ReprOptions {
15131524
let mut size = None;
15141525
let mut max_align: Option<Align> = None;
15151526
let mut min_pack: Option<Align> = None;
1527+
1528+
// Generate a deterministically-derived seed from the item's path hash
1529+
// to allow for cross-crate compilation to actually work
1530+
let field_shuffle_seed = tcx.def_path_hash(did).0.to_smaller_hash();
1531+
15161532
for attr in tcx.get_attrs(did).iter() {
15171533
for r in attr::find_repr_attrs(&tcx.sess, attr) {
15181534
flags.insert(match r {
@@ -1541,33 +1557,45 @@ impl ReprOptions {
15411557
}
15421558
}
15431559

1560+
// If `-Z randomize-layout` was enabled for the type definition then we can
1561+
// consider performing layout randomization
1562+
if tcx.sess.opts.debugging_opts.randomize_layout {
1563+
flags.insert(ReprFlags::RANDOMIZE_LAYOUT);
1564+
}
1565+
15441566
// This is here instead of layout because the choice must make it into metadata.
15451567
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) {
15461568
flags.insert(ReprFlags::IS_LINEAR);
15471569
}
1548-
ReprOptions { int: size, align: max_align, pack: min_pack, flags }
1570+
1571+
Self { int: size, align: max_align, pack: min_pack, flags, field_shuffle_seed }
15491572
}
15501573

15511574
#[inline]
15521575
pub fn simd(&self) -> bool {
15531576
self.flags.contains(ReprFlags::IS_SIMD)
15541577
}
1578+
15551579
#[inline]
15561580
pub fn c(&self) -> bool {
15571581
self.flags.contains(ReprFlags::IS_C)
15581582
}
1583+
15591584
#[inline]
15601585
pub fn packed(&self) -> bool {
15611586
self.pack.is_some()
15621587
}
1588+
15631589
#[inline]
15641590
pub fn transparent(&self) -> bool {
15651591
self.flags.contains(ReprFlags::IS_TRANSPARENT)
15661592
}
1593+
15671594
#[inline]
15681595
pub fn linear(&self) -> bool {
15691596
self.flags.contains(ReprFlags::IS_LINEAR)
15701597
}
1598+
15711599
#[inline]
15721600
pub fn hide_niche(&self) -> bool {
15731601
self.flags.contains(ReprFlags::HIDE_NICHE)
@@ -1594,9 +1622,17 @@ impl ReprOptions {
15941622
return true;
15951623
}
15961624
}
1625+
15971626
self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some()
15981627
}
15991628

1629+
/// Returns `true` if this type is valid for reordering and `-Z randomize-layout`
1630+
/// was enabled for its declaration crate
1631+
pub fn can_randomize_type_layout(&self) -> bool {
1632+
!self.inhibit_struct_field_reordering_opt()
1633+
&& self.flags.contains(ReprFlags::RANDOMIZE_LAYOUT)
1634+
}
1635+
16001636
/// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations.
16011637
pub fn inhibit_union_abi_opt(&self) -> bool {
16021638
self.c()

‎compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,8 @@ options! {
12461246
"enable queries of the dependency graph for regression testing (default: no)"),
12471247
query_stats: bool = (false, parse_bool, [UNTRACKED],
12481248
"print some statistics about the query system (default: no)"),
1249+
randomize_layout: bool = (false, parse_bool, [TRACKED],
1250+
"randomize the layout of types (default: no)"),
12491251
relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
12501252
"whether ELF relocations can be relaxed"),
12511253
relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],

‎src/tools/tidy/src/deps.rs

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
167167
"rand_hc",
168168
"rand_pcg",
169169
"rand_xorshift",
170+
"rand_xoshiro",
170171
"redox_syscall",
171172
"regex",
172173
"regex-automata",

0 commit comments

Comments
 (0)
Please sign in to comment.