Skip to content

Commit 0ce678c

Browse files
committed
Put checks that detect UB under their own flag below debug_assertions
1 parent c7491b9 commit 0ce678c

File tree

15 files changed

+65
-15
lines changed

15 files changed

+65
-15
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ fn codegen_stmt<'tcx>(
789789
layout.offset_of_subfield(fx, fields.iter()).bytes()
790790
}
791791
NullOp::UbChecks => {
792-
let val = fx.tcx.sess.opts.debug_assertions;
792+
let val = fx.tcx.sess.ub_checks();
793793
let val = CValue::by_val(
794794
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
795795
fx.layout_of(fx.tcx.types.bool),

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
682682
bx.cx().const_usize(val)
683683
}
684684
mir::NullOp::UbChecks => {
685-
let val = bx.tcx().sess.opts.debug_assertions;
685+
let val = bx.tcx().sess.ub_checks();
686686
bx.cx().const_bool(val)
687687
}
688688
};

compiler/rustc_const_eval/src/interpret/step.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
258258
let val = layout.offset_of_subfield(self, fields.iter()).bytes();
259259
Scalar::from_target_usize(val, self)
260260
}
261-
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.opts.debug_assertions),
261+
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()),
262262
};
263263
self.write_scalar(val, &dest)?;
264264
}

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ fn test_unstable_options_tracking_hash() {
847847
tracked!(trap_unreachable, Some(false));
848848
tracked!(treat_err_as_bug, NonZero::new(1));
849849
tracked!(tune_cpu, Some(String::from("abc")));
850+
tracked!(ub_checks, Some(false));
850851
tracked!(uninit_const_chunk_threshold, 123);
851852
tracked!(unleash_the_miri_inside_of_you, true);
852853
tracked!(use_ctors_section, Some(true));

compiler/rustc_mir_transform/src/check_alignment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl<'tcx> MirPass<'tcx> for CheckAlignment {
1616
if sess.target.llvm_target == "i686-pc-windows-msvc" {
1717
return false;
1818
}
19-
sess.opts.debug_assertions
19+
sess.ub_checks()
2020
}
2121

2222
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

compiler/rustc_mir_transform/src/instsimplify.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
149149

150150
fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
151151
if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
152-
let const_ = Const::from_bool(self.tcx, self.tcx.sess.opts.debug_assertions);
152+
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
153153
let constant = ConstOperand { span: source_info.span, const_, user_ty: None };
154154
*rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
155155
}

compiler/rustc_session/src/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,10 @@ fn default_configuration(sess: &Session) -> Cfg {
12361236
ins_none!(sym::overflow_checks);
12371237
}
12381238

1239+
if sess.ub_checks() {
1240+
ins_none!(sym::ub_checks);
1241+
}
1242+
12391243
ins_sym!(sym::panic, sess.panic_strategy().desc_symbol());
12401244

12411245
// JUSTIFICATION: before wrapper fn is available

compiler/rustc_session/src/options.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,9 @@ written to standard error output)"),
19941994
"in diagnostics, use heuristics to shorten paths referring to items"),
19951995
tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
19961996
"select processor to schedule for (`rustc --print target-cpus` for details)"),
1997+
#[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")]
1998+
ub_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
1999+
"emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"),
19972000
ui_testing: bool = (false, parse_bool, [UNTRACKED],
19982001
"emit compiler diagnostics in a form suitable for UI testing (default: no)"),
19992002
uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED],

compiler/rustc_session/src/session.rs

+4
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,10 @@ impl Session {
735735
self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
736736
}
737737

738+
pub fn ub_checks(&self) -> bool {
739+
self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions)
740+
}
741+
738742
pub fn relocation_model(&self) -> RelocModel {
739743
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
740744
}

library/core/src/intrinsics.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -2704,17 +2704,17 @@ pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
27042704
}
27052705

27062706
/// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to
2707-
/// `cfg!(debug_assertions)`, but behaves different from `cfg!` when mixing crates built with different
2708-
/// flags: if the crate has debug assertions enabled or carries the `#[rustc_preserve_ub_checks]`
2707+
/// `cfg!(ub_checks)`, but behaves different from `cfg!` when mixing crates built with different
2708+
/// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_ub_checks]`
27092709
/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
27102710
/// a crate that does not delay evaluation further); otherwise it can happen any time.
27112711
///
2712-
/// The common case here is a user program built with debug_assertions linked against the distributed
2713-
/// sysroot which is built without debug_assertions but with `#[rustc_preserve_ub_checks]`.
2712+
/// The common case here is a user program built with ub_checks linked against the distributed
2713+
/// sysroot which is built without ub_checks but with `#[rustc_preserve_ub_checks]`.
27142714
/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
2715-
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that
2716-
/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the
2717-
/// user has debug assertions disabled, the checks will still get optimized out. This intrinsic is
2715+
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that
2716+
/// assertions are enabled whenever the *user crate* has UB checks enabled. However if the
2717+
/// user has UB checks disabled, the checks will still get optimized out. This intrinsic is
27182718
/// primarily used by [`ub_checks::assert_unsafe_precondition`].
27192719
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
27202720
#[unstable(feature = "core_intrinsics", issue = "none")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# `ub-checks`
2+
3+
--------------------
4+
5+
The `-Zub-checks` compiler flag enables additional runtime checks that detect some causes of Undefined Behavior at runtime.
6+
By default, `-Zub-checks` flag inherits the value of `-Cdebug-assertions`.
7+
8+
All checks are generated on a best-effort basis; even if we have a check implemented for some cause of Undefined Behavior, it may be possible for the check to not fire.
9+
If a dependency is compiled with `-Zub-checks=no` but the final binary or library is compiled with `-Zub-checks=yes`, UB checks reached by the dependency are likely to be optimized out.
10+
11+
When `-Zub-checks` detects UB, a non-unwinding panic is produced.
12+
That means that we will not unwind the stack and will not call any `Drop` impls, but we will execute the configured panic hook.
13+
We expect that unsafe code has been written which relies on code not unwinding which may have UB checks inserted.
14+
Ergo, an unwinding panic could easily turn works-as-intended UB into a much bigger problem.
15+
Calling the panic hook theoretically has the same implications, but we expect that the standard library panic hook will be stateless enough to be always called, and that if a user has configured a panic hook that the hook may be very helpful to debugging the detected UB.

tests/codegen/ub-checks.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// With -Zub-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a runtime
2+
// check that the index to slice::get_unchecked is in-bounds of the slice. That is tested for by
3+
// tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs
4+
//
5+
// This test ensures that such a runtime check is *not* emitted when debug-assertions are enabled,
6+
// but ub-checks are explicitly disabled.
7+
8+
//@ compile-flags: -O -Cdebug-assertions=yes -Zub-checks=no
9+
10+
#![crate_type = "lib"]
11+
12+
use std::ops::Range;
13+
14+
// CHECK-LABEL: @slice_get_unchecked(
15+
#[no_mangle]
16+
pub unsafe fn slice_get_unchecked(x: &[i32], i: usize) -> &i32 {
17+
// CHECK-NEXT: start:
18+
// CHECK-NEXT: icmp ult
19+
// CHECK-NEXT: tail call void @llvm.assume
20+
// CHECK-NEXT: getelementptr inbounds
21+
// CHECK-NEXT: ret ptr
22+
x.get_unchecked(i)
23+
}

tests/ui/precondition-checks/misaligned-slice.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ run-fail
2-
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=yes
2+
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
33
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
44
//@ ignore-debug
55

tests/ui/precondition-checks/null-slice.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ run-fail
2-
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=yes
2+
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
33
//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
44
//@ ignore-debug
55

tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ run-fail
2-
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=yes
2+
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
33
//@ error-pattern: slice::get_unchecked requires
44
//@ ignore-debug
55

0 commit comments

Comments
 (0)