Skip to content

Commit 36e7d82

Browse files
adpaco-awstedinski
authored andcommitted
Correctly handle assert_* intrinsics (rust-lang#742)
1 parent 6c316fb commit 36e7d82

File tree

8 files changed

+95
-55
lines changed

8 files changed

+95
-55
lines changed

src/kani-compiler/rustc_codegen_kani/src/codegen/intrinsic.rs

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -301,28 +301,9 @@ impl<'tcx> GotocCtx<'tcx> {
301301
match intrinsic {
302302
"add_with_overflow" => codegen_op_with_overflow!(add_overflow),
303303
"arith_offset" => codegen_wrapping_op!(plus),
304-
"assert_inhabited" => {
305-
let ty = instance.substs.type_at(0);
306-
let layout = self.layout_of(ty);
307-
if layout.abi.is_uninhabited() {
308-
let loc = self.codegen_span_option(span);
309-
Stmt::assert_false(&format!("type is uninhabited: {:?}", ty), loc)
310-
} else {
311-
Stmt::skip(loc)
312-
}
313-
}
314-
// https://doc.rust-lang.org/std/intrinsics/fn.assert_uninit_valid.html
315-
// assert_uninit_valid is guard for unsafe functions that cannot ever be executed if T
316-
// has invalid bit patterns: This will statically either panic, or do nothing. For now
317-
// we map this into a no-op.
318-
// TODO: https://github.com/model-checking/rmc/issues/6
319-
"assert_uninit_valid" => Stmt::skip(loc),
320-
// https://doc.rust-lang.org/std/intrinsics/fn.assert_zero_valid.html
321-
// assert_zero_valid is a guard for unsafe functions that cannot ever be executed if T
322-
// does not permit zero-initialization: This will statically either panic, or do
323-
// nothing. For now we map this into a no-op.
324-
// TODO: https://github.com/model-checking/rmc/issues/7
325-
"assert_zero_valid" => Stmt::skip(loc),
304+
"assert_inhabited" => self.codegen_assert_intrinsic(instance, intrinsic, span),
305+
"assert_uninit_valid" => self.codegen_assert_intrinsic(instance, intrinsic, span),
306+
"assert_zero_valid" => self.codegen_assert_intrinsic(instance, intrinsic, span),
326307
// https://doc.rust-lang.org/core/intrinsics/fn.assume.html
327308
// Informs the optimizer that a condition is always true.
328309
// If the condition is false, the behavior is undefined.
@@ -567,6 +548,54 @@ impl<'tcx> GotocCtx<'tcx> {
567548
)
568549
}
569550

551+
/// Generates either a panic or no-op for `assert_*` intrinsics.
552+
/// These are intrinsics that statically compile to panics if the type
553+
/// layout is invalid so we get a message that mentions the offending type.
554+
///
555+
/// https://doc.rust-lang.org/std/intrinsics/fn.assert_inhabited.html
556+
/// https://doc.rust-lang.org/std/intrinsics/fn.assert_uninit_valid.html
557+
/// https://doc.rust-lang.org/std/intrinsics/fn.assert_zero_valid.html
558+
fn codegen_assert_intrinsic(
559+
&mut self,
560+
instance: Instance<'tcx>,
561+
intrinsic: &str,
562+
span: Option<Span>,
563+
) -> Stmt {
564+
let ty = instance.substs.type_at(0);
565+
let layout = self.layout_of(ty);
566+
// Note: We follow the pattern seen in `codegen_panic_intrinsic` from `rustc_codegen_ssa`
567+
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_ssa/src/mir/block.rs
568+
569+
// For all intrinsics we first check `is_uninhabited` to give a more
570+
// precise error message
571+
if layout.abi.is_uninhabited() {
572+
return self.codegen_fatal_error(
573+
&format!("attempted to instantiate uninhabited type `{}`", ty),
574+
span,
575+
);
576+
}
577+
578+
// Then we check if the type allows "raw" initialization for the cases
579+
// where memory is zero-initialized or entirely uninitialized
580+
if intrinsic == "assert_zero_valid" && !layout.might_permit_raw_init(self, true) {
581+
return self.codegen_fatal_error(
582+
&format!("attempted to zero-initialize type `{}`, which is invalid", ty),
583+
span,
584+
);
585+
}
586+
587+
if intrinsic == "assert_uninit_valid" && !layout.might_permit_raw_init(self, false) {
588+
return self.codegen_fatal_error(
589+
&format!("attempted to leave type `{}` uninitialized, which is invalid", ty),
590+
span,
591+
);
592+
}
593+
594+
// Otherwise we generate a no-op statement
595+
let loc = self.codegen_span_option(span);
596+
return Stmt::skip(loc);
597+
}
598+
570599
/// An atomic load simply returns the value referenced
571600
/// in its argument (as in other atomic operations)
572601
/// -------------------------

src/test/kani/Assert/UninitValid/fixme_main.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/test/kani/Assert/ZeroValid/fixme_main.rs

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-verify-fail
4+
5+
#![feature(never_type)]
6+
use std::mem::MaybeUninit;
7+
8+
// The code below attempts to instantiate uninhabited type `!`.
9+
// This should cause the intrinsic `assert_inhabited` to generate a panic during
10+
// compilation, but at present it triggers the `Nevers` hook instead.
11+
// See https://github.com/model-checking/rmc/issues/751
12+
fn main() {
13+
let _uninit_never: () = unsafe {
14+
MaybeUninit::<!>::uninit().assume_init();
15+
};
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-verify-fail
4+
5+
#![feature(core_intrinsics)]
6+
use std::intrinsics;
7+
8+
// The code below attempts to leave type `bool` uninitialized, causing the
9+
// intrinsic `assert_uninit_valid` to generate a panic during compilation.
10+
fn main() {
11+
let _var: () = unsafe {
12+
intrinsics::assert_uninit_valid::<bool>();
13+
};
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-verify-fail
4+
5+
#![feature(core_intrinsics)]
6+
use std::intrinsics;
7+
8+
// The code below attempts to zero-initialize type `&i32`, causing the intrinsic
9+
// `assert_zero_valid` to generate a panic during compilation.
10+
fn main() {
11+
let _var: () = unsafe {
12+
intrinsics::assert_zero_valid::<&i32>();
13+
};
14+
}

0 commit comments

Comments
 (0)