Skip to content

Commit 33576b3

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

37 files changed

+156
-29
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_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub type GatedCfg = (Symbol, Symbol, GateFn);
2525
const GATED_CFGS: &[GatedCfg] = &[
2626
// (name in cfg, feature, function to check if the feature is enabled)
2727
(sym::overflow_checks, sym::cfg_overflow_checks, cfg_fn!(cfg_overflow_checks)),
28+
(sym::ub_checks, sym::cfg_ub_checks, cfg_fn!(cfg_ub_checks)),
2829
(sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
2930
(
3031
sym::target_has_atomic_equal_alignment,

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ declare_features! (
381381
(unstable, cfg_target_has_atomic_equal_alignment, "1.60.0", Some(93822)),
382382
/// Allows `cfg(target_thread_local)`.
383383
(unstable, cfg_target_thread_local, "1.7.0", Some(29594)),
384+
/// Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled.
385+
(unstable, cfg_ub_checks, "CURRENT_RUSTC_VERSION", Some(123499)),
384386
/// Allow conditional compilation depending on rust version
385387
(unstable, cfg_version, "1.45.0", Some(64796)),
386388
/// Allows to use the `#[cfi_encoding = ""]` attribute.

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

+6
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,10 @@ fn default_configuration(sess: &Session) -> Cfg {
13291329
ins_none!(sym::test);
13301330
}
13311331

1332+
if sess.ub_checks() {
1333+
ins_none!(sym::ub_checks);
1334+
}
1335+
13321336
ret
13331337
}
13341338

@@ -1532,6 +1536,8 @@ impl CheckCfg {
15321536

15331537
ins!(sym::test, no_values);
15341538

1539+
ins!(sym::ub_checks, no_values);
1540+
15351541
ins!(sym::unix, no_values);
15361542
ins!(sym::windows, no_values);
15371543
}

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
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ symbols! {
514514
cfg_target_has_atomic_equal_alignment,
515515
cfg_target_thread_local,
516516
cfg_target_vendor,
517+
cfg_ub_checks,
517518
cfg_version,
518519
cfi,
519520
cfi_encoding,

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

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
//@ revisions: DEBUG NOCHECKS
9+
//@ [DEBUG] compile-flags:
10+
//@ [NOCHECKS] compile-flags: -Zub-checks=no
11+
//@ compile-flags: -O -Cdebug-assertions=yes
12+
13+
#![crate_type = "lib"]
14+
15+
use std::ops::Range;
16+
17+
// CHECK-LABEL: @slice_get_unchecked(
18+
#[no_mangle]
19+
pub unsafe fn slice_get_unchecked(x: &[i32], i: usize) -> &i32 {
20+
// CHECK: icmp ult
21+
// NOCHECKS: tail call void @llvm.assume
22+
// DEBUG: br i1
23+
// DEBUG: call core::panicking::panic_nounwind
24+
// DEBUG: unreachable
25+
// CHECK: getelementptr inbounds
26+
// CHECK: ret ptr
27+
x.get_unchecked(i)
28+
}

tests/ui/check-cfg/allow-same-level.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `FALSE`
44
LL | #[cfg(FALSE)]
55
| ^^^^^
66
|
7-
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
7+
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
88
= help: to expect this configuration use `--check-cfg=cfg(FALSE)`
99
= note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
1010
= note: `#[warn(unexpected_cfgs)]` on by default

tests/ui/check-cfg/cargo-feature.none.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable`
2525
LL | #[cfg(tokio_unstable)]
2626
| ^^^^^^^^^^^^^^
2727
|
28-
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
28+
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
2929
= help: consider using a Cargo feature instead or adding `println!("cargo:rustc-check-cfg=cfg(tokio_unstable)");` to the top of a `build.rs`
3030
= note: see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration
3131

tests/ui/check-cfg/cargo-feature.some.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable`
2525
LL | #[cfg(tokio_unstable)]
2626
| ^^^^^^^^^^^^^^
2727
|
28-
= help: expected names are: `CONFIG_NVME`, `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
28+
= help: expected names are: `CONFIG_NVME`, `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
2929
= help: consider using a Cargo feature instead or adding `println!("cargo:rustc-check-cfg=cfg(tokio_unstable)");` to the top of a `build.rs`
3030
= note: see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration
3131

tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value`
44
LL | #[cfg(value)]
55
| ^^^^^
66
|
7-
= help: expected names are: `bar`, `bee`, `clippy`, `cow`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
7+
= help: expected names are: `bar`, `bee`, `clippy`, `cow`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
88
= help: to expect this configuration use `--check-cfg=cfg(value)`
99
= note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
1010
= note: `#[warn(unexpected_cfgs)]` on by default

tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_value`
44
LL | #[cfg(my_value)]
55
| ^^^^^^^^
66
|
7-
= help: expected names are: `bar`, `clippy`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
7+
= help: expected names are: `bar`, `clippy`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
88
= help: to expect this configuration use `--check-cfg=cfg(my_value)`
99
= note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
1010
= note: `#[warn(unexpected_cfgs)]` on by default

tests/ui/check-cfg/cfg-value-for-cfg-name.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `linux`
44
LL | #[cfg(linux)]
55
| ^^^^^ help: found config with similar value: `target_os = "linux"`
66
|
7-
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
7+
= help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows`
88
= help: to expect this configuration use `--check-cfg=cfg(linux)`
99
= note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration
1010
= note: `#[warn(unexpected_cfgs)]` on by default

0 commit comments

Comments
 (0)