Skip to content

Commit 17f5098

Browse files
authored
Rollup merge of rust-lang#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 a460185 + c218c75 commit 17f5098

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
@@ -512,6 +512,9 @@
512512
# are disabled statically" because `max_level_info` is enabled, set this value to `true`.
513513
#debug-logging = rust.debug-assertions (boolean)
514514

515+
# Whether or not to build rustc, tools and the libraries with randomized type layout
516+
#randomize-layout = false
517+
515518
# Whether or not overflow checks are enabled for the compiler and standard
516519
# library.
517520
#

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
@@ -1614,6 +1614,15 @@ impl<'a> Builder<'a> {
16141614
rustflags.arg("-Csymbol-mangling-version=legacy");
16151615
}
16161616

1617+
// FIXME: the following components don't build with `-Zrandomize-layout` yet:
1618+
// - wasm-component-ld, due to the `wast`crate
1619+
// - rust-analyzer, due to the rowan crate
1620+
// so we exclude entire categories of steps here due to lack of fine-grained control over
1621+
// rustflags.
1622+
if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc {
1623+
rustflags.arg("-Zrandomize-layout");
1624+
}
1625+
16171626
// Enable compile-time checking of `cfg` names, values and Cargo `features`.
16181627
//
16191628
// Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like
@@ -2189,6 +2198,9 @@ impl<'a> Builder<'a> {
21892198
rustflags.arg("-Zvalidate-mir");
21902199
rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}"));
21912200
}
2201+
if self.config.rust_randomize_layout {
2202+
rustflags.arg("--cfg=randomized_layouts");
2203+
}
21922204
// Always enable inlining MIR when building the standard library.
21932205
// Without this flag, MIR inlining is disabled when incremental compilation is enabled.
21942206
// 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>,
@@ -1088,6 +1089,7 @@ define_config! {
10881089
codegen_units: Option<u32> = "codegen-units",
10891090
codegen_units_std: Option<u32> = "codegen-units-std",
10901091
debug_assertions: Option<bool> = "debug-assertions",
1092+
randomize_layout: Option<bool> = "randomize-layout",
10911093
debug_assertions_std: Option<bool> = "debug-assertions-std",
10921094
overflow_checks: Option<bool> = "overflow-checks",
10931095
overflow_checks_std: Option<bool> = "overflow-checks-std",
@@ -1179,6 +1181,7 @@ impl Config {
11791181
backtrace: true,
11801182
rust_optimize: RustOptimize::Bool(true),
11811183
rust_optimize_tests: true,
1184+
rust_randomize_layout: false,
11821185
submodules: None,
11831186
docs: true,
11841187
docs_minification: true,
@@ -1629,6 +1632,7 @@ impl Config {
16291632
backtrace,
16301633
incremental,
16311634
parallel_compiler,
1635+
randomize_layout,
16321636
default_linker,
16331637
channel,
16341638
description,
@@ -1718,6 +1722,7 @@ impl Config {
17181722
set(&mut config.lld_mode, lld_mode);
17191723
set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker);
17201724

1725+
config.rust_randomize_layout = randomize_layout.unwrap_or_default();
17211726
config.llvm_tools_enabled = llvm_tools.unwrap_or(true);
17221727
config.rustc_parallel =
17231728
parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly");
@@ -2889,6 +2894,7 @@ fn check_incompatible_options_for_ci_rustc(
28892894
let Rust {
28902895
// Following options are the CI rustc incompatible ones.
28912896
optimize,
2897+
randomize_layout,
28922898
debug_logging,
28932899
debuginfo_level_rustc,
28942900
llvm_tools,
@@ -2953,6 +2959,7 @@ fn check_incompatible_options_for_ci_rustc(
29532959
// otherwise, we just print a warning with `warn` macro.
29542960

29552961
err!(current_rust_config.optimize, optimize);
2962+
err!(current_rust_config.randomize_layout, randomize_layout);
29562963
err!(current_rust_config.debug_logging, debug_logging);
29572964
err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc);
29582965
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)