Skip to content

Commit 02c0c76

Browse files
authored
Rollup merge of rust-lang#97323 - 5225225:strict_init_checks, r=oli-obk
Introduce stricter checks for might_permit_raw_init under a debug flag This is intended to be a version of the strict checks tried out in rust-lang#79296, but also checking number validity (under the assumption that `let _ = std::mem::uninitialized::<u32>()` is UB, which seems to be what rust-lang/unsafe-code-guidelines#71 is leaning towards.)
2 parents 89bdbd0 + dd9f31d commit 02c0c76

File tree

6 files changed

+116
-26
lines changed

6 files changed

+116
-26
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub(crate) use llvm::codegen_llvm_intrinsic_call;
5858
use rustc_middle::ty::print::with_no_trimmed_paths;
5959
use rustc_middle::ty::subst::SubstsRef;
6060
use rustc_span::symbol::{kw, sym, Symbol};
61+
use rustc_target::abi::InitKind;
6162

6263
use crate::prelude::*;
6364
use cranelift_codegen::ir::AtomicRmwOp;
@@ -671,7 +672,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
671672
return;
672673
}
673674

674-
if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true) {
675+
if intrinsic == sym::assert_zero_valid
676+
&& !layout.might_permit_raw_init(
677+
fx,
678+
InitKind::Zero,
679+
fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
680+
675681
with_no_trimmed_paths!({
676682
crate::base::codegen_panic(
677683
fx,
@@ -682,7 +688,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
682688
return;
683689
}
684690

685-
if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false) {
691+
if intrinsic == sym::assert_uninit_valid
692+
&& !layout.might_permit_raw_init(
693+
fx,
694+
InitKind::Uninit,
695+
fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
696+
686697
with_no_trimmed_paths!({
687698
crate::base::codegen_panic(
688699
fx,

compiler/rustc_codegen_ssa/src/mir/block.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_span::source_map::Span;
2222
use rustc_span::{sym, Symbol};
2323
use rustc_symbol_mangling::typeid_for_fnabi;
2424
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
25-
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
25+
use rustc_target::abi::{self, HasDataLayout, InitKind, WrappingRange};
2626
use rustc_target::spec::abi::Abi;
2727

2828
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@@ -521,6 +521,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
521521
source_info: mir::SourceInfo,
522522
target: Option<mir::BasicBlock>,
523523
cleanup: Option<mir::BasicBlock>,
524+
strict_validity: bool,
524525
) -> bool {
525526
// Emit a panic or a no-op for `assert_*` intrinsics.
526527
// These are intrinsics that compile to panics so that we can get a message
@@ -543,8 +544,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
543544
let layout = bx.layout_of(ty);
544545
let do_panic = match intrinsic {
545546
Inhabited => layout.abi.is_uninhabited(),
546-
ZeroValid => !layout.might_permit_raw_init(bx, /*zero:*/ true),
547-
UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false),
547+
ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity),
548+
UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity),
548549
};
549550
if do_panic {
550551
let msg_str = with_no_visible_paths!({
@@ -678,6 +679,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
678679
source_info,
679680
target,
680681
cleanup,
682+
self.cx.tcx().sess.opts.debugging_opts.strict_init_checks,
681683
) {
682684
return;
683685
}

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _;
1515
use rustc_middle::ty::subst::SubstsRef;
1616
use rustc_middle::ty::{Ty, TyCtxt};
1717
use rustc_span::symbol::{sym, Symbol};
18-
use rustc_target::abi::{Abi, Align, Primitive, Size};
18+
use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size};
1919

2020
use super::{
2121
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@@ -408,7 +408,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
408408
)?;
409409
}
410410
if intrinsic_name == sym::assert_zero_valid
411-
&& !layout.might_permit_raw_init(self, /*zero:*/ true)
411+
&& !layout.might_permit_raw_init(
412+
self,
413+
InitKind::Zero,
414+
self.tcx.sess.opts.debugging_opts.strict_init_checks,
415+
)
412416
{
413417
M::abort(
414418
self,
@@ -419,7 +423,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
419423
)?;
420424
}
421425
if intrinsic_name == sym::assert_uninit_valid
422-
&& !layout.might_permit_raw_init(self, /*zero:*/ false)
426+
&& !layout.might_permit_raw_init(
427+
self,
428+
InitKind::Uninit,
429+
self.tcx.sess.opts.debugging_opts.strict_init_checks,
430+
)
423431
{
424432
M::abort(
425433
self,

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,8 @@ options! {
14951495
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
14961496
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
14971497
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
1498+
strict_init_checks: bool = (false, parse_bool, [TRACKED],
1499+
"control if mem::uninitialized and mem::zeroed panic on more UB"),
14981500
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
14991501
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
15001502
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED],

compiler/rustc_target/src/abi/mod.rs

+47-12
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,15 @@ impl Scalar {
894894
Scalar::Union { .. } => true,
895895
}
896896
}
897+
898+
/// Returns `true` if this type can be left uninit.
899+
#[inline]
900+
pub fn is_uninit_valid(&self) -> bool {
901+
match *self {
902+
Scalar::Initialized { .. } => false,
903+
Scalar::Union { .. } => true,
904+
}
905+
}
897906
}
898907

899908
/// Describes how the fields of a type are located in memory.
@@ -1355,6 +1364,14 @@ pub struct PointeeInfo {
13551364
pub address_space: AddressSpace,
13561365
}
13571366

1367+
/// Used in `might_permit_raw_init` to indicate the kind of initialisation
1368+
/// that is checked to be valid
1369+
#[derive(Copy, Clone, Debug)]
1370+
pub enum InitKind {
1371+
Zero,
1372+
Uninit,
1373+
}
1374+
13581375
/// Trait that needs to be implemented by the higher-level type representation
13591376
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
13601377
pub trait TyAbiInterface<'a, C>: Sized {
@@ -1461,26 +1478,37 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
14611478

14621479
/// Determines if this type permits "raw" initialization by just transmuting some
14631480
/// memory into an instance of `T`.
1464-
/// `zero` indicates if the memory is zero-initialized, or alternatively
1465-
/// left entirely uninitialized.
1481+
///
1482+
/// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
1483+
///
1484+
/// `strict` is an opt-in debugging flag added in #97323 that enables more checks.
1485+
///
14661486
/// This is conservative: in doubt, it will answer `true`.
14671487
///
14681488
/// FIXME: Once we removed all the conservatism, we could alternatively
14691489
/// create an all-0/all-undef constant and run the const value validator to see if
14701490
/// this is a valid value for the given type.
1471-
pub fn might_permit_raw_init<C>(self, cx: &C, zero: bool) -> bool
1491+
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
14721492
where
14731493
Self: Copy,
14741494
Ty: TyAbiInterface<'a, C>,
14751495
C: HasDataLayout,
14761496
{
14771497
let scalar_allows_raw_init = move |s: Scalar| -> bool {
1478-
if zero {
1479-
// The range must contain 0.
1480-
s.valid_range(cx).contains(0)
1481-
} else {
1482-
// The range must include all values.
1483-
s.is_always_valid(cx)
1498+
match init_kind {
1499+
InitKind::Zero => {
1500+
// The range must contain 0.
1501+
s.valid_range(cx).contains(0)
1502+
}
1503+
InitKind::Uninit => {
1504+
if strict {
1505+
// The type must be allowed to be uninit (which means "is a union").
1506+
s.is_uninit_valid()
1507+
} else {
1508+
// The range must include all values.
1509+
s.is_always_valid(cx)
1510+
}
1511+
}
14841512
}
14851513
};
14861514

@@ -1500,12 +1528,19 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
15001528
// If we have not found an error yet, we need to recursively descend into fields.
15011529
match &self.fields {
15021530
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
1503-
FieldsShape::Array { .. } => {
1504-
// FIXME(#66151): For now, we are conservative and do not check arrays.
1531+
FieldsShape::Array { count, .. } => {
1532+
// FIXME(#66151): For now, we are conservative and do not check arrays by default.
1533+
if strict
1534+
&& *count > 0
1535+
&& !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict)
1536+
{
1537+
// Found non empty array with a type that is unhappy about this kind of initialization
1538+
return false;
1539+
}
15051540
}
15061541
FieldsShape::Arbitrary { offsets, .. } => {
15071542
for idx in 0..offsets.len() {
1508-
if !self.field(cx, idx).might_permit_raw_init(cx, zero) {
1543+
if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) {
15091544
// We found a field that is unhappy with this kind of initialization.
15101545
return false;
15111546
}

src/test/ui/intrinsics/panic-uninitialized-zeroed.rs

+38-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// run-pass
22
// needs-unwind
33
// ignore-wasm32-bare compiled with panic=abort by default
4-
// revisions: mir thir
4+
// revisions: mir thir strict
55
// [thir]compile-flags: -Zthir-unsafeck
6+
// [strict]compile-flags: -Zstrict-init-checks
67
// ignore-tidy-linelength
78

89
// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
@@ -54,6 +55,8 @@ enum LR_NonZero {
5455
Right(num::NonZeroI64),
5556
}
5657

58+
struct ZeroSized;
59+
5760
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
5861
let err = panic::catch_unwind(op).err();
5962
assert_eq!(
@@ -228,11 +231,40 @@ fn main() {
228231
let _val = mem::zeroed::<[!; 0]>();
229232
let _val = mem::uninitialized::<MaybeUninit<bool>>();
230233
let _val = mem::uninitialized::<[!; 0]>();
234+
let _val = mem::uninitialized::<()>();
235+
let _val = mem::uninitialized::<ZeroSized>();
236+
237+
if cfg!(strict) {
238+
test_panic_msg(
239+
|| mem::uninitialized::<i32>(),
240+
"attempted to leave type `i32` uninitialized, which is invalid"
241+
);
242+
243+
test_panic_msg(
244+
|| mem::uninitialized::<*const ()>(),
245+
"attempted to leave type `*const ()` uninitialized, which is invalid"
246+
);
247+
248+
test_panic_msg(
249+
|| mem::uninitialized::<[i32; 1]>(),
250+
"attempted to leave type `[i32; 1]` uninitialized, which is invalid"
251+
);
252+
253+
test_panic_msg(
254+
|| mem::zeroed::<NonNull<()>>(),
255+
"attempted to zero-initialize type `core::ptr::non_null::NonNull<()>`, which is invalid"
256+
);
231257

232-
// These are UB because they have not been officially blessed, but we await the resolution
233-
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
234-
// anything about that.
235-
let _val = mem::uninitialized::<i32>();
236-
let _val = mem::uninitialized::<*const ()>();
258+
test_panic_msg(
259+
|| mem::zeroed::<[NonNull<()>; 1]>(),
260+
"attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid"
261+
);
262+
} else {
263+
// These are UB because they have not been officially blessed, but we await the resolution
264+
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
265+
// anything about that.
266+
let _val = mem::uninitialized::<i32>();
267+
let _val = mem::uninitialized::<*const ()>();
268+
}
237269
}
238270
}

0 commit comments

Comments
 (0)