Skip to content

abort immediately on bad mem::zeroed/uninit #105997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.set_debug_loc(bx, terminator.source_info);

// Obtain the panic entry point.
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicNoUnwind);
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicCannotUnwind);

// Codegen the actual panic invoke/call.
let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &[], None, None, &[], false);
Expand Down Expand Up @@ -698,19 +698,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
});
let msg = bx.const_str(&msg_str);
let location = self.get_caller_location(bx, source_info).immediate();

// Obtain the panic entry point.
let (fn_abi, llfn) =
common::build_langcall(bx, Some(source_info.span), LangItem::Panic);
common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind);

// Codegen the actual panic invoke/call.
helper.do_call(
self,
bx,
fn_abi,
llfn,
&[msg.0, msg.1, location],
&[msg.0, msg.1],
target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)),
cleanup,
&[],
Expand Down Expand Up @@ -1665,7 +1664,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let llpersonality = self.cx.eh_personality();
bx.cleanup_landing_pad(llpersonality);

let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicNoUnwind);
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);

let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], None);
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
}

language_item_table! {
// Variant name, Name, Method name, Target Generic requirements;
// Variant name, Name, Getter method name, Target Generic requirements;
Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
Expand Down Expand Up @@ -232,14 +232,15 @@ language_item_table! {
// is required to define it somewhere. Additionally, there are restrictions on crates that use
// a weak lang item, but do not have it defined.
Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
PanicNoUnwind, sym::panic_no_unwind, panic_no_unwind, Target::Fn, GenericRequirement::Exact(0);

My brain wants to parse PanicNounwind as Panic-Noun-Wind, so I'd prefer to keep the original spelling here.

In the snake case variant, I'm fine with either panic_no_unwind or panic_nounwind.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that the old PanicNoUnwind is actually subtly different from the new PanicNounwind, which is why I did the rename -- the old lang item is equal to what is now called PannicCannotUnwind.

If you are okay with giving a lang item a different meaning without renaming it, I can change this to PanicNoUnwind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Changing the name makes it so everyone has to actively do something now that it's changed, which seems like a good thing. If it annoys people in the future we can always change it then, but this reasoning makes sense to leave it as PanicNounwind.

PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
PanicNoUnwind, sym::panic_no_unwind, panic_no_unwind, Target::Fn, GenericRequirement::Exact(0);
PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
/// libstd panic entry point. Necessary for const eval to be able to catch it
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
mir::TerminatorKind::Abort { .. } => {
let instance = Instance::mono(
tcx,
tcx.require_lang_item(LangItem::PanicNoUnwind, Some(source)),
tcx.require_lang_item(LangItem::PanicCannotUnwind, Some(source)),
);
if should_codegen_locally(tcx, &instance) {
self.output.push(create_fn_mono_item(tcx, instance, source));
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,14 +1041,15 @@ symbols! {
panic_2021,
panic_abort,
panic_bounds_check,
panic_cannot_unwind,
panic_display,
panic_fmt,
panic_handler,
panic_impl,
panic_implementation,
panic_info,
panic_location,
panic_no_unwind,
panic_nounwind,
panic_runtime,
panic_str,
panic_unwind,
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2281,7 +2281,7 @@ macro_rules! assert_unsafe_precondition {
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
if !$e {
// don't unwind to reduce impact on code size
::core::panicking::panic_str_nounwind(
::core::panicking::panic_nounwind(
concat!("unsafe precondition(s) violated: ", $name)
);
}
Expand Down
14 changes: 8 additions & 6 deletions library/core/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,13 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
unsafe { panic_impl(&pi) }
}

/// Like panic_fmt, but without unwinding and track_caller to reduce the impact on codesize.
/// Also just works on `str`, as a `fmt::Arguments` needs more space to be passed.
/// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize.
/// (No `fmt` variant as a `fmt::Arguments` needs more space to be passed.)
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[cfg_attr(not(bootstrap), lang = "panic_nounwind")] // needed by codegen for non-unwinding panics
#[rustc_nounwind]
pub fn panic_str_nounwind(msg: &'static str) -> ! {
pub fn panic_nounwind(msg: &'static str) -> ! {
if cfg!(feature = "panic_immediate_abort") {
super::intrinsics::abort()
}
Expand Down Expand Up @@ -153,10 +154,11 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
/// any extra arguments (including those synthesized by track_caller).
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
#[cfg_attr(bootstrap, lang = "panic_no_unwind")] // needed by codegen for panic in nounwind function
#[cfg_attr(not(bootstrap), lang = "panic_cannot_unwind")] // needed by codegen for panic in nounwind function
#[rustc_nounwind]
fn panic_no_unwind() -> ! {
panic_str_nounwind("panic in a function that cannot unwind")
fn panic_cannot_unwind() -> ! {
panic_nounwind("panic in a function that cannot unwind")
}

/// This function is used instead of panic_fmt in const eval.
Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#[no_mangle]
pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
// Handle both legacy and v0 symbol mangling.
// CHECK: call void @{{.*core9panicking15panic_no_unwind}}
// CHECK: call void @{{.*core9panicking19panic_cannot_unwind}}
may_unwind();
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/unwind-and-panic-abort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extern "C-unwind" {
// CHECK: Function Attrs:{{.*}}nounwind
// CHECK-NEXT: define{{.*}}void @foo
// Handle both legacy and v0 symbol mangling.
// CHECK: call void @{{.*core9panicking15panic_no_unwind}}
// CHECK: call void @{{.*core9panicking19panic_cannot_unwind}}
#[no_mangle]
pub unsafe extern "C" fn foo() {
bar();
Expand Down
16 changes: 8 additions & 8 deletions src/test/codegen/vec-shrink-panik.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ pub fn shrink_to_fit(vec: &mut Vec<u32>) {
pub fn issue71861(vec: Vec<u32>) -> Box<[u32]> {
// CHECK-NOT: panic

// Call to panic_no_unwind in case of double-panic is expected,
// Call to panic_cannot_unwind in case of double-panic is expected,
// but other panics are not.
// CHECK: cleanup
// CHECK-NEXT: ; call core::panicking::panic_no_unwind
// CHECK-NEXT: panic_no_unwind
// CHECK-NEXT: ; call core::panicking::panic_cannot_unwind
// CHECK-NEXT: panic_cannot_unwind

// CHECK-NOT: panic
vec.into_boxed_slice()
Expand All @@ -33,15 +33,15 @@ pub fn issue71861(vec: Vec<u32>) -> Box<[u32]> {
pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> {
// CHECK-NOT: panic

// Call to panic_no_unwind in case of double-panic is expected,
// Call to panic_cannot_unwind in case of double-panic is expected,
// but other panics are not.
// CHECK: cleanup
// CHECK-NEXT: ; call core::panicking::panic_no_unwind
// CHECK-NEXT: panic_no_unwind
// CHECK-NEXT: ; call core::panicking::panic_cannot_unwind
// CHECK-NEXT: panic_cannot_unwind

// CHECK-NOT: panic
iter.iter().copied().collect()
}

// CHECK: ; core::panicking::panic_no_unwind
// CHECK: declare void @{{.*}}panic_no_unwind
// CHECK: ; core::panicking::panic_cannot_unwind
// CHECK: declare void @{{.*}}panic_cannot_unwind
52 changes: 36 additions & 16 deletions src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// run-pass
// needs-unwind
// revisions: mir thir strict
// [thir]compile-flags: -Zthir-unsafeck
// revisions: default strict
// [strict]compile-flags: -Zstrict-init-checks
// ignore-tidy-linelength
// ignore-emscripten spawning processes is not supported
// ignore-sgx no processes

// This test checks panic emitted from `mem::{uninitialized,zeroed}`.

Expand All @@ -12,7 +12,6 @@

use std::{
mem::{self, MaybeUninit, ManuallyDrop},
panic,
ptr::NonNull,
num,
};
Expand Down Expand Up @@ -70,21 +69,42 @@ enum ZeroIsValid {
}

#[track_caller]
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
let err = panic::catch_unwind(op).err();
assert_eq!(
err.as_ref().and_then(|a| a.downcast_ref::<&str>()),
Some(&msg)
);
fn test_panic_msg<T, F: (FnOnce() -> T) + 'static>(op: F, msg: &str) {
use std::{panic, env, process};

// The tricky part is that we can't just run `op`, as that would *abort* the process.
// So instead, we reinvoke this process with the caller location as argument.
// For the purpose of this test, the line number is unique enough.
// If we are running in such a re-invocation, we skip all the tests *except* for the one with that type name.
let our_loc = panic::Location::caller().line().to_string();
let mut args = env::args();
let this = args.next().unwrap();
if let Some(loc) = args.next() {
if loc == our_loc {
op();
panic!("we did not abort");
} else {
// Nothing, we are running another test.
}
} else {
// Invoke new process for actual test, and check result.
let mut cmd = process::Command::new(this);
cmd.arg(our_loc);
let res = cmd.output().unwrap();
assert!(!res.status.success(), "test did not fail");
let stderr = String::from_utf8_lossy(&res.stderr);
assert!(stderr.contains(msg), "test did not contain expected output: looking for {:?}, output:\n{}", msg, stderr);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does make the test very slow. But that seems fine? I can't think of a better way...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could spawn all the processes and only once that's done collect all the results. But that requires a bit more plumbing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or these tests could be rewritten as #[test] functions. This is also used in some other UI tests.
And lib-test spawns new processes for tests when the it's compiled with panic=abort or some unstable flag is passed. Not sure if compiletest can pass arguments to run-pass tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to use -Cpanic=abort and -Zpanic-abort-tests together when compiling the crate to be tested to have libtest spawn new processes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yeah I guess that could work, though I would have to restructure the test quite a bit.

Let's see what the reviewer says, for now I'd prefer this approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this approach looks okay. It launches 75 processes or so, which I don't think will be too much time in the grand scheme of things. It might be worse on Windows since process creation is more expensive there though. Given that we run the whole test suite in parallel, I doubt one slower test will affect the overall time too much.

}

#[track_caller]
fn test_panic_msg_only_if_strict<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
let err = panic::catch_unwind(op).err();
assert_eq!(
err.as_ref().and_then(|a| a.downcast_ref::<&str>()),
if cfg!(strict) { Some(&msg) } else { None },
);
fn test_panic_msg_only_if_strict<T>(op: impl (FnOnce() -> T) + 'static, msg: &str) {
if !cfg!(strict) {
// Just run it.
op();
} else {
test_panic_msg(op, msg);
}
}

fn main() {
Expand Down