Skip to content

Commit 8a60d0a

Browse files
authored
Rollup merge of #101339 - the8472:ci-randomize-debug, r=Mark-Simulacrum
enable -Zrandomize-layout in debug CI builds This builds rustc/libs/tools with `-Zrandomize-layout` on *-debug CI runners. Only a handful of tests and asserts break with that enabled, which is promising. One test was fixable, the rest is dealt with by disabling them through new cargo features or compiletest directives. The config.toml flag `rust.randomize-layout` defaults to false, so it has to be explicitly enabled for now.
2 parents 009e738 + c218c75 commit 8a60d0a

File tree

29 files changed

+103
-10
lines changed

29 files changed

+103
-10
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3569,6 +3569,7 @@ dependencies = [
35693569
"rustc_hir_pretty",
35703570
"rustc_hir_typeck",
35713571
"rustc_incremental",
3572+
"rustc_index",
35723573
"rustc_infer",
35733574
"rustc_interface",
35743575
"rustc_lint",

compiler/rustc/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
3030
jemalloc = ['dep:jemalloc-sys']
3131
llvm = ['rustc_driver_impl/llvm']
3232
max_level_info = ['rustc_driver_impl/max_level_info']
33+
rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']
3334
rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler']
3435
# tidy-alphabetical-end

compiler/rustc_abi/src/layout.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -968,8 +968,8 @@ fn univariant<
968968
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
969969
let mut max_repr_align = repr.align;
970970
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
971-
let optimize = !repr.inhibit_struct_field_reordering();
972-
if optimize && fields.len() > 1 {
971+
let optimize_field_order = !repr.inhibit_struct_field_reordering();
972+
if optimize_field_order && fields.len() > 1 {
973973
let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
974974
let optimizing = &mut inverse_memory_index.raw[..end];
975975
let fields_excluding_tail = &fields.raw[..end];
@@ -1176,7 +1176,7 @@ fn univariant<
11761176
// If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0.
11771177
// Field 5 would be the first element, so memory_index is i:
11781178
// Note: if we didn't optimize, it's already right.
1179-
let memory_index = if optimize {
1179+
let memory_index = if optimize_field_order {
11801180
inverse_memory_index.invert_bijective_mapping()
11811181
} else {
11821182
debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices()));
@@ -1189,6 +1189,9 @@ fn univariant<
11891189
}
11901190
let mut layout_of_single_non_zst_field = None;
11911191
let mut abi = Abi::Aggregate { sized };
1192+
1193+
let optimize_abi = !repr.inhibit_newtype_abi_optimization();
1194+
11921195
// Try to make this a Scalar/ScalarPair.
11931196
if sized && size.bytes() > 0 {
11941197
// We skip *all* ZST here and later check if we are good in terms of alignment.
@@ -1205,7 +1208,7 @@ fn univariant<
12051208
match field.abi {
12061209
// For plain scalars, or vectors of them, we can't unpack
12071210
// newtypes for `#[repr(C)]`, as that affects C ABIs.
1208-
Abi::Scalar(_) | Abi::Vector { .. } if optimize => {
1211+
Abi::Scalar(_) | Abi::Vector { .. } if optimize_abi => {
12091212
abi = field.abi;
12101213
}
12111214
// But scalar pairs are Rust-specific and get

compiler/rustc_abi/src/lib.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ bitflags! {
4343
const IS_SIMD = 1 << 1;
4444
const IS_TRANSPARENT = 1 << 2;
4545
// Internal only for now. If true, don't reorder fields.
46+
// On its own it does not prevent ABI optimizations.
4647
const IS_LINEAR = 1 << 3;
47-
// If true, the type's layout can be randomized using
48-
// the seed stored in `ReprOptions.field_shuffle_seed`
48+
// If true, the type's crate has opted into layout randomization.
49+
// Other flags can still inhibit reordering and thus randomization.
50+
// The seed stored in `ReprOptions.field_shuffle_seed`.
4951
const RANDOMIZE_LAYOUT = 1 << 4;
5052
// Any of these flags being set prevent field reordering optimisation.
51-
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits()
53+
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
5254
| ReprFlags::IS_SIMD.bits()
5355
| ReprFlags::IS_LINEAR.bits();
56+
const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits();
5457
}
5558
}
5659

@@ -139,10 +142,14 @@ impl ReprOptions {
139142
self.c() || self.int.is_some()
140143
}
141144

145+
pub fn inhibit_newtype_abi_optimization(&self) -> bool {
146+
self.flags.intersects(ReprFlags::ABI_UNOPTIMIZABLE)
147+
}
148+
142149
/// Returns `true` if this `#[repr()]` guarantees a fixed field order,
143150
/// e.g. `repr(C)` or `repr(<int>)`.
144151
pub fn inhibit_struct_field_reordering(&self) -> bool {
145-
self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some()
152+
self.flags.intersects(ReprFlags::FIELD_ORDER_UNOPTIMIZABLE) || self.int.is_some()
146153
}
147154

148155
/// Returns `true` if this type is valid for reordering and `-Z randomize-layout`

compiler/rustc_driver_impl/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ rustc_hir_analysis = { path = "../rustc_hir_analysis" }
2323
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
2424
rustc_hir_typeck = { path = "../rustc_hir_typeck" }
2525
rustc_incremental = { path = "../rustc_incremental" }
26+
rustc_index = { path = "../rustc_index" }
2627
rustc_infer = { path = "../rustc_infer" }
2728
rustc_interface = { path = "../rustc_interface" }
2829
rustc_lint = { path = "../rustc_lint" }
@@ -72,6 +73,10 @@ ctrlc = "3.4.4"
7273
# tidy-alphabetical-start
7374
llvm = ['rustc_interface/llvm']
7475
max_level_info = ['rustc_log/max_level_info']
76+
rustc_randomized_layouts = [
77+
'rustc_index/rustc_randomized_layouts',
78+
'rustc_middle/rustc_randomized_layouts'
79+
]
7580
rustc_use_parallel_compiler = [
7681
'rustc_data_structures/rustc_use_parallel_compiler',
7782
'rustc_interface/rustc_use_parallel_compiler',

compiler/rustc_index/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ nightly = [
2020
"dep:rustc_macros",
2121
"rustc_index_macros/nightly",
2222
]
23+
rustc_randomized_layouts = []
2324
# tidy-alphabetical-end

compiler/rustc_index/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,19 @@ pub use vec::IndexVec;
3333
///
3434
/// </div>
3535
#[macro_export]
36+
#[cfg(not(feature = "rustc_randomized_layouts"))]
3637
macro_rules! static_assert_size {
3738
($ty:ty, $size:expr) => {
3839
const _: [(); $size] = [(); ::std::mem::size_of::<$ty>()];
3940
};
4041
}
42+
43+
#[macro_export]
44+
#[cfg(feature = "rustc_randomized_layouts")]
45+
macro_rules! static_assert_size {
46+
($ty:ty, $size:expr) => {
47+
// no effect other than using the statements.
48+
// struct sizes are not deterministic under randomized layouts
49+
const _: (usize, usize) = ($size, ::std::mem::size_of::<$ty>());
50+
};
51+
}

compiler/rustc_middle/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ tracing = "0.1"
4040

4141
[features]
4242
# tidy-alphabetical-start
43+
rustc_randomized_layouts = []
4344
rustc_use_parallel_compiler = ["dep:rustc-rayon-core"]
4445
# tidy-alphabetical-end

compiler/rustc_middle/src/query/plumbing.rs

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ macro_rules! define_callbacks {
337337
// Ensure that values grow no larger than 64 bytes by accident.
338338
// Increase this limit if necessary, but do try to keep the size low if possible
339339
#[cfg(target_pointer_width = "64")]
340+
#[cfg(not(feature = "rustc_randomized_layouts"))]
340341
const _: () = {
341342
if mem::size_of::<Value<'static>>() > 64 {
342343
panic!("{}", concat!(

compiler/rustc_middle/src/ty/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
3535
use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
3636
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
3737
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
38+
use rustc_hir::LangItem;
3839
use rustc_index::IndexVec;
3940
use rustc_macros::{
4041
extension, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable,
@@ -1570,8 +1571,15 @@ impl<'tcx> TyCtxt<'tcx> {
15701571
flags.insert(ReprFlags::RANDOMIZE_LAYOUT);
15711572
}
15721573

1574+
// box is special, on the one hand the compiler assumes an ordered layout, with the pointer
1575+
// always at offset zero. On the other hand we want scalar abi optimizations.
1576+
let is_box = self.is_lang_item(did.to_def_id(), LangItem::OwnedBox);
1577+
15731578
// This is here instead of layout because the choice must make it into metadata.
1574-
if !self.consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) {
1579+
if is_box
1580+
|| !self
1581+
.consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did)))
1582+
{
15751583
flags.insert(ReprFlags::IS_LINEAR);
15761584
}
15771585

config.example.toml

+3
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@
519519
# are disabled statically" because `max_level_info` is enabled, set this value to `true`.
520520
#debug-logging = rust.debug-assertions (boolean)
521521

522+
# Whether or not to build rustc, tools and the libraries with randomized type layout
523+
#randomize-layout = false
524+
522525
# Whether or not overflow checks are enabled for the compiler and standard
523526
# library.
524527
#

library/alloc/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,5 @@ check-cfg = [
5252
'cfg(no_global_oom_handling)',
5353
'cfg(no_rc)',
5454
'cfg(no_sync)',
55+
'cfg(randomized_layouts)',
5556
]

library/alloc/src/collections/btree/node/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn test_partial_eq() {
9090

9191
#[test]
9292
#[cfg(target_arch = "x86_64")]
93-
#[cfg_attr(miri, ignore)] // We'd like to run Miri with layout randomization
93+
#[cfg_attr(any(miri, randomized_layouts), ignore)] // We'd like to run Miri with layout randomization
9494
fn test_sizes() {
9595
assert_eq!(core::mem::size_of::<LeafNode<(), ()>>(), 16);
9696
assert_eq!(core::mem::size_of::<LeafNode<i64, i64>>(), 16 + CAPACITY * 2 * 8);

src/bootstrap/src/core/build_steps/test.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
18101810
if builder.config.rust_optimize_tests {
18111811
cmd.arg("--optimize-tests");
18121812
}
1813+
if builder.config.rust_randomize_layout {
1814+
cmd.arg("--rust-randomized-layout");
1815+
}
18131816
if builder.config.cmd.only_modified() {
18141817
cmd.arg("--only-modified");
18151818
}

src/bootstrap/src/core/builder.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,15 @@ impl<'a> Builder<'a> {
16181618
rustflags.arg("-Csymbol-mangling-version=legacy");
16191619
}
16201620

1621+
// FIXME: the following components don't build with `-Zrandomize-layout` yet:
1622+
// - wasm-component-ld, due to the `wast`crate
1623+
// - rust-analyzer, due to the rowan crate
1624+
// so we exclude entire categories of steps here due to lack of fine-grained control over
1625+
// rustflags.
1626+
if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
1627+
rustflags.arg("-Zrandomize-layout");
1628+
}
1629+
16211630
// Enable compile-time checking of `cfg` names, values and Cargo `features`.
16221631
//
16231632
// Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
@@ -2193,6 +2202,9 @@ impl<'a> Builder<'a> {
21932202
rustflags.arg("-Zvalidate-mir");
21942203
rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
21952204
}
2205+
if self.config.rust_randomize_layout {
2206+
rustflags.arg("--cfg=randomized_layouts");
2207+
}
21962208
// Always enable inlining MIR when building the standard library.
21972209
// Without this flag, MIR inlining is disabled when incremental compilation is enabled.
21982210
// That causes some mir-opt tests which inline functions from the standard library to

src/bootstrap/src/core/config/config.rs

+7
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ pub struct Config {
280280
pub rust_codegen_backends: Vec<String>,
281281
pub rust_verify_llvm_ir: bool,
282282
pub rust_thin_lto_import_instr_limit: Option<u32>,
283+
pub rust_randomize_layout: bool,
283284
pub rust_remap_debuginfo: bool,
284285
pub rust_new_symbol_mangling: Option<bool>,
285286
pub rust_profile_use: Option<String>,
@@ -1090,6 +1091,7 @@ define_config! {
10901091
codegen_units: Option<u32> = "codegen-units",
10911092
codegen_units_std: Option<u32> = "codegen-units-std",
10921093
debug_assertions: Option<bool> = "debug-assertions",
1094+
randomize_layout: Option<bool> = "randomize-layout",
10931095
debug_assertions_std: Option<bool> = "debug-assertions-std",
10941096
overflow_checks: Option<bool> = "overflow-checks",
10951097
overflow_checks_std: Option<bool> = "overflow-checks-std",
@@ -1181,6 +1183,7 @@ impl Config {
11811183
backtrace: true,
11821184
rust_optimize: RustOptimize::Bool(true),
11831185
rust_optimize_tests: true,
1186+
rust_randomize_layout: false,
11841187
submodules: None,
11851188
docs: true,
11861189
docs_minification: true,
@@ -1640,6 +1643,7 @@ impl Config {
16401643
backtrace,
16411644
incremental,
16421645
parallel_compiler,
1646+
randomize_layout,
16431647
default_linker,
16441648
channel,
16451649
description,
@@ -1729,6 +1733,7 @@ impl Config {
17291733
set(&mut config.lld_mode, lld_mode);
17301734
set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker);
17311735

1736+
config.rust_randomize_layout = randomize_layout.unwrap_or_default();
17321737
config.llvm_tools_enabled = llvm_tools.unwrap_or(true);
17331738
config.rustc_parallel =
17341739
parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly");
@@ -2900,6 +2905,7 @@ fn check_incompatible_options_for_ci_rustc(
29002905
let Rust {
29012906
// Following options are the CI rustc incompatible ones.
29022907
optimize,
2908+
randomize_layout,
29032909
debug_logging,
29042910
debuginfo_level_rustc,
29052911
llvm_tools,
@@ -2964,6 +2970,7 @@ fn check_incompatible_options_for_ci_rustc(
29642970
// otherwise, we just print a warning with `warn` macro.
29652971

29662972
err!(current_rust_config.optimize, optimize);
2973+
err!(current_rust_config.randomize_layout, randomize_layout);
29672974
err!(current_rust_config.debug_logging, debug_logging);
29682975
err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc);
29692976
err!(current_rust_config.rpath, rpath);

src/bootstrap/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,9 @@ impl Build {
678678
if self.config.rustc_parallel {
679679
features.push("rustc_use_parallel_compiler");
680680
}
681+
if self.config.rust_randomize_layout {
682+
features.push("rustc_randomized_layouts");
683+
}
681684

682685
// If debug logging is on, then we want the default for tracing:
683686
// https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26

src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ ENV RUST_CONFIGURE_ARGS \
5050
--build=x86_64-unknown-linux-gnu \
5151
--llvm-root=/usr/lib/llvm-17 \
5252
--enable-llvm-link-shared \
53+
--set rust.randomize-layout=true \
5354
--set rust.thin-lto-import-instr-limit=10
5455

5556
COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/

src/tools/compiletest/src/command-list.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
136136
"min-llvm-version",
137137
"min-system-llvm-version",
138138
"needs-asm-support",
139+
"needs-deterministic-layouts",
139140
"needs-dlltool",
140141
"needs-dynamic-linking",
141142
"needs-force-clang-based-tests",

src/tools/compiletest/src/common.rs

+3
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ pub struct Config {
274274
/// Flags to pass to the compiler when building for the target
275275
pub target_rustcflags: Vec<String>,
276276

277+
/// Whether the compiler and stdlib has been built with randomized struct layouts
278+
pub rust_randomized_layout: bool,
279+
277280
/// Whether tests should be optimized by default. Individual test-suites and test files may
278281
/// override this setting.
279282
pub optimize_tests: bool,

src/tools/compiletest/src/header/needs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ pub(super) fn handle_needs(
134134
condition: config.target_cfg().relocation_model == "pic",
135135
ignore_reason: "ignored on targets without PIC relocation model",
136136
},
137+
Need {
138+
name: "needs-deterministic-layouts",
139+
condition: !config.rust_randomized_layout,
140+
ignore_reason: "ignored when randomizing layouts",
141+
},
137142
Need {
138143
name: "needs-wasmtime",
139144
condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")),

src/tools/compiletest/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
9999
)
100100
.optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
101101
.optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
102+
.optflag(
103+
"",
104+
"rust-randomized-layout",
105+
"set this when rustc/stdlib were compiled with randomized layouts",
106+
)
102107
.optflag("", "optimize-tests", "run tests with optimizations enabled")
103108
.optflag("", "verbose", "run tests verbosely, showing all output")
104109
.optflag(
@@ -286,6 +291,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
286291
host_rustcflags: matches.opt_strs("host-rustcflags"),
287292
target_rustcflags: matches.opt_strs("target-rustcflags"),
288293
optimize_tests: matches.opt_present("optimize-tests"),
294+
rust_randomized_layout: matches.opt_present("rust-randomized-layout"),
289295
target,
290296
host: opt_str2(matches.opt_str("host")),
291297
cdb,

tests/codegen/issues/issue-86106.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ only-64bit llvm appears to use stores instead of memset on 32bit
22
//@ compile-flags: -C opt-level=3 -Z merge-functions=disabled
3+
//@ needs-deterministic-layouts
34

45
// The below two functions ensure that both `String::new()` and `"".to_string()`
56
// produce the identical code.

tests/codegen/mem-replace-big-type.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
//@ compile-flags: -C no-prepopulate-passes -Zinline-mir=no
77
//@ ignore-debug: precondition checks in ptr::read make them a bad candidate for MIR inlining
8+
//@ needs-deterministic-layouts
89

910
#![crate_type = "lib"]
1011

tests/codegen/slice-iter-nonnull.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ compile-flags: -O
2+
//@ needs-deterministic-layouts
23
#![crate_type = "lib"]
34
#![feature(exact_size_is_empty)]
45

tests/codegen/vecdeque-drain.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Check that draining at the front or back doesn't copy memory.
22

33
//@ compile-flags: -O
4+
//@ needs-deterministic-layouts
45
//@ ignore-debug: FIXME: checks for call detect scoped noalias metadata
56

67
#![crate_type = "lib"]

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@needs-deterministic-layouts
12
// Verify that we do not ICE when printing an invalid constant.
23
// EMIT_MIR_FOR_EACH_BIT_WIDTH
34
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY

0 commit comments

Comments
 (0)