Skip to content

Commit 3f66c4e

Browse files
authored
Rollup merge of rust-lang#99033 - 5225225:interpreter-validity-checks, r=oli-obk
Use constant eval to do strict mem::uninit/zeroed validity checks I'm not sure about the code organisation here, I just dumped the check in rustc_const_eval at the root. Not hard to move it elsewhere, in any case. Also, this means cranelift codegen intrinsics lose the strict checks, since they don't seem to depend on rustc_const_eval, and I didn't see a point in keeping around two copies. I also left comments in the is_zero_valid methods about "uhhh help how do i do this", those apply to both methods equally. Also rustc_codegen_ssa now depends on rustc_const_eval... is this okay? Pinging `@RalfJung` since you were the one who mentioned this to me, so I'm assuming you're interested. Haven't had a chance to run full tests on this since it's really warm, and it's 1AM, I'll check out any failures/comments in the morning :)
2 parents a93152d + 236c7c0 commit 3f66c4e

File tree

11 files changed

+143
-86
lines changed

11 files changed

+143
-86
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,6 +3664,7 @@ dependencies = [
36643664
"rustc_arena",
36653665
"rustc_ast",
36663666
"rustc_attr",
3667+
"rustc_const_eval",
36673668
"rustc_data_structures",
36683669
"rustc_errors",
36693670
"rustc_fs_util",

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod simd;
5555
pub(crate) use cpuid::codegen_cpuid_call;
5656
pub(crate) use llvm::codegen_llvm_intrinsic_call;
5757

58+
use rustc_const_eval::might_permit_raw_init::might_permit_raw_init;
5859
use rustc_middle::ty::print::with_no_trimmed_paths;
5960
use rustc_middle::ty::subst::SubstsRef;
6061
use rustc_span::symbol::{kw, sym, Symbol};
@@ -673,10 +674,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
673674
}
674675

675676
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) {
677+
&& !might_permit_raw_init(fx.tcx, layout, InitKind::Zero) {
680678

681679
with_no_trimmed_paths!({
682680
crate::base::codegen_panic(
@@ -689,10 +687,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
689687
}
690688

691689
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) {
690+
&& !might_permit_raw_init(fx.tcx, layout, InitKind::Uninit) {
696691

697692
with_no_trimmed_paths!({
698693
crate::base::codegen_panic(

compiler/rustc_codegen_cranelift/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
extern crate rustc_middle;
99
extern crate rustc_ast;
1010
extern crate rustc_codegen_ssa;
11+
extern crate rustc_const_eval;
1112
extern crate rustc_data_structures;
1213
extern crate rustc_errors;
1314
extern crate rustc_fs_util;

compiler/rustc_codegen_ssa/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ rustc_metadata = { path = "../rustc_metadata" }
4040
rustc_query_system = { path = "../rustc_query_system" }
4141
rustc_target = { path = "../rustc_target" }
4242
rustc_session = { path = "../rustc_session" }
43+
rustc_const_eval = { path = "../rustc_const_eval" }
4344

4445
[dependencies.object]
4546
version = "0.29.0"

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
528528
source_info: mir::SourceInfo,
529529
target: Option<mir::BasicBlock>,
530530
cleanup: Option<mir::BasicBlock>,
531-
strict_validity: bool,
532531
) -> bool {
533532
// Emit a panic or a no-op for `assert_*` intrinsics.
534533
// These are intrinsics that compile to panics so that we can get a message
@@ -546,13 +545,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
546545
_ => None,
547546
});
548547
if let Some(intrinsic) = panic_intrinsic {
548+
use rustc_const_eval::might_permit_raw_init::might_permit_raw_init;
549549
use AssertIntrinsic::*;
550+
550551
let ty = instance.unwrap().substs.type_at(0);
551552
let layout = bx.layout_of(ty);
552553
let do_panic = match intrinsic {
553554
Inhabited => layout.abi.is_uninhabited(),
554-
ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity),
555-
UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity),
555+
ZeroValid => !might_permit_raw_init(bx.tcx(), layout, InitKind::Zero),
556+
UninitValid => !might_permit_raw_init(bx.tcx(), layout, InitKind::Uninit),
556557
};
557558
if do_panic {
558559
let msg_str = with_no_visible_paths!({
@@ -687,7 +688,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
687688
source_info,
688689
target,
689690
cleanup,
690-
self.cx.tcx().sess.opts.debugging_opts.strict_init_checks,
691691
) {
692692
return;
693693
}

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
104104
}
105105

106106
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
107-
pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
107+
pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
108108
CompileTimeInterpreter {
109109
steps_remaining: const_eval_limit.0,
110110
stack: Vec::new(),

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use super::{
2222
Pointer,
2323
};
2424

25+
use crate::might_permit_raw_init::might_permit_raw_init;
26+
2527
mod caller_location;
2628
mod type_name;
2729

@@ -413,35 +415,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
413415
),
414416
)?;
415417
}
416-
if intrinsic_name == sym::assert_zero_valid
417-
&& !layout.might_permit_raw_init(
418-
self,
419-
InitKind::Zero,
420-
self.tcx.sess.opts.debugging_opts.strict_init_checks,
421-
)
422-
{
423-
M::abort(
424-
self,
425-
format!(
426-
"aborted execution: attempted to zero-initialize type `{}`, which is invalid",
427-
ty
428-
),
429-
)?;
418+
419+
if intrinsic_name == sym::assert_zero_valid {
420+
let should_panic = !might_permit_raw_init(*self.tcx, layout, InitKind::Zero);
421+
422+
if should_panic {
423+
M::abort(
424+
self,
425+
format!(
426+
"aborted execution: attempted to zero-initialize type `{}`, which is invalid",
427+
ty
428+
),
429+
)?;
430+
}
430431
}
431-
if intrinsic_name == sym::assert_uninit_valid
432-
&& !layout.might_permit_raw_init(
433-
self,
434-
InitKind::Uninit,
435-
self.tcx.sess.opts.debugging_opts.strict_init_checks,
436-
)
437-
{
438-
M::abort(
439-
self,
440-
format!(
441-
"aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
442-
ty
443-
),
444-
)?;
432+
433+
if intrinsic_name == sym::assert_uninit_valid {
434+
let should_panic = !might_permit_raw_init(*self.tcx, layout, InitKind::Uninit);
435+
436+
if should_panic {
437+
M::abort(
438+
self,
439+
format!(
440+
"aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
441+
ty
442+
),
443+
)?;
444+
}
445445
}
446446
}
447447
sym::simd_insert => {

compiler/rustc_const_eval/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern crate rustc_middle;
3333
pub mod const_eval;
3434
mod errors;
3535
pub mod interpret;
36+
pub mod might_permit_raw_init;
3637
pub mod transform;
3738
pub mod util;
3839

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::const_eval::CompileTimeInterpreter;
2+
use crate::interpret::{InterpCx, MemoryKind, OpTy};
3+
use rustc_middle::ty::layout::LayoutCx;
4+
use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt};
5+
use rustc_session::Limit;
6+
use rustc_target::abi::InitKind;
7+
8+
pub fn might_permit_raw_init<'tcx>(
9+
tcx: TyCtxt<'tcx>,
10+
ty: TyAndLayout<'tcx>,
11+
kind: InitKind,
12+
) -> bool {
13+
let strict = tcx.sess.opts.debugging_opts.strict_init_checks;
14+
15+
if strict {
16+
let machine = CompileTimeInterpreter::new(Limit::new(0), false);
17+
18+
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
19+
20+
// We could panic here... Or we could just return "yeah it's valid whatever". Or let
21+
// codegen_panic_intrinsic return an error that halts compilation.
22+
// I'm not exactly sure *when* this can fail. OOM?
23+
let allocated = cx
24+
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
25+
.expect("failed to allocate for uninit check");
26+
27+
if kind == InitKind::Zero {
28+
// Again, unclear what to do here if it fails.
29+
cx.write_bytes_ptr(
30+
allocated.ptr,
31+
std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
32+
)
33+
.expect("failed to write bytes for zero valid check");
34+
}
35+
36+
let ot: OpTy<'_, _> = allocated.into();
37+
38+
// Assume that if it failed, it's a validation failure.
39+
cx.validate_operand(&ot).is_ok()
40+
} else {
41+
let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
42+
ty.might_permit_raw_init(&layout_cx, kind)
43+
}
44+
}

compiler/rustc_target/src/abi/mod.rs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,7 +1372,7 @@ pub struct PointeeInfo {
13721372

13731373
/// Used in `might_permit_raw_init` to indicate the kind of initialisation
13741374
/// that is checked to be valid
1375-
#[derive(Copy, Clone, Debug)]
1375+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13761376
pub enum InitKind {
13771377
Zero,
13781378
Uninit,
@@ -1487,14 +1487,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
14871487
///
14881488
/// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
14891489
///
1490-
/// `strict` is an opt-in debugging flag added in #97323 that enables more checks.
1490+
/// This code is intentionally conservative, and will not detect
1491+
/// * zero init of an enum whose 0 variant does not allow zero initialization
1492+
/// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
1493+
/// * Any form of invalid value being made inside an array (unless the value is uninhabited)
14911494
///
1492-
/// This is conservative: in doubt, it will answer `true`.
1495+
/// A strict form of these checks that uses const evaluation exists in
1496+
/// [`rustc_const_eval::might_permit_raw_init`], and a tracking issue for making these checks
1497+
/// stricter is <https://github.com/rust-lang/rust/issues/66151>.
14931498
///
1494-
/// FIXME: Once we removed all the conservatism, we could alternatively
1495-
/// create an all-0/all-undef constant and run the const value validator to see if
1496-
/// this is a valid value for the given type.
1497-
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
1499+
/// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
1500+
/// we can use the const evaluation checks always instead.
1501+
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
14981502
where
14991503
Self: Copy,
15001504
Ty: TyAbiInterface<'a, C>,
@@ -1507,13 +1511,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
15071511
s.valid_range(cx).contains(0)
15081512
}
15091513
InitKind::Uninit => {
1510-
if strict {
1511-
// The type must be allowed to be uninit (which means "is a union").
1512-
s.is_uninit_valid()
1513-
} else {
1514-
// The range must include all values.
1515-
s.is_always_valid(cx)
1516-
}
1514+
// The range must include all values.
1515+
s.is_always_valid(cx)
15171516
}
15181517
}
15191518
};
@@ -1534,19 +1533,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
15341533
// If we have not found an error yet, we need to recursively descend into fields.
15351534
match &self.fields {
15361535
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
1537-
FieldsShape::Array { count, .. } => {
1536+
FieldsShape::Array { .. } => {
15381537
// FIXME(#66151): For now, we are conservative and do not check arrays by default.
1539-
if strict
1540-
&& *count > 0
1541-
&& !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict)
1542-
{
1543-
// Found non empty array with a type that is unhappy about this kind of initialization
1544-
return false;
1545-
}
15461538
}
15471539
FieldsShape::Arbitrary { offsets, .. } => {
15481540
for idx in 0..offsets.len() {
1549-
if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) {
1541+
if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
15501542
// We found a field that is unhappy with this kind of initialization.
15511543
return false;
15521544
}

0 commit comments

Comments
 (0)