Skip to content

Commit 6b354a1

Browse files
committed
Auto merge of rust-lang#86034 - nagisa:nagisa/rt-soundness, r=m-ou-se
Change entry point to 🛡️ against 💥 💥-payloads Guard against panic payloads panicking within entrypoints, where it is UB to do so. Note that there are a number of tradeoffs to consider. For instance, I considered guarding against accidental panics inside the `rt::init` and `rt::cleanup` code as well, as it is not all that obvious these may not panic, but doing so would mean that we initialize certain thread-local slots unconditionally, which has its own problems. Fixes rust-lang#86030 r? `@m-ou-se`
2 parents 29cd70d + 9c9a0da commit 6b354a1

File tree

3 files changed

+61
-15
lines changed

3 files changed

+61
-15
lines changed

library/std/src/lib.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,14 @@
225225
#![feature(allocator_internals)]
226226
#![feature(allow_internal_unsafe)]
227227
#![feature(allow_internal_unstable)]
228-
#![feature(async_stream)]
229228
#![feature(arbitrary_self_types)]
230229
#![feature(array_error_internals)]
231230
#![feature(asm)]
232231
#![feature(assert_matches)]
233232
#![feature(associated_type_bounds)]
233+
#![feature(async_stream)]
234234
#![feature(atomic_mut_ptr)]
235+
#![feature(auto_traits)]
235236
#![feature(bench_black_box)]
236237
#![feature(box_syntax)]
237238
#![feature(c_variadic)]
@@ -244,14 +245,14 @@
244245
#![feature(concat_idents)]
245246
#![feature(const_cstr_unchecked)]
246247
#![feature(const_fn_floating_point_arithmetic)]
247-
#![feature(const_fn_transmute)]
248248
#![feature(const_fn_fn_ptr_basics)]
249+
#![feature(const_fn_transmute)]
249250
#![feature(const_io_structs)]
250251
#![feature(const_ip)]
252+
#![feature(const_ipv4)]
251253
#![feature(const_ipv6)]
252254
#![feature(const_raw_ptr_deref)]
253255
#![feature(const_socketaddr)]
254-
#![feature(const_ipv4)]
255256
#![feature(container_error_extra)]
256257
#![feature(core_intrinsics)]
257258
#![feature(custom_test_frameworks)]
@@ -298,7 +299,6 @@
298299
#![feature(nll)]
299300
#![feature(nonnull_slice_from_raw_parts)]
300301
#![feature(once_cell)]
301-
#![feature(auto_traits)]
302302
#![feature(panic_info_message)]
303303
#![feature(panic_internals)]
304304
#![feature(panic_unwind)]
@@ -330,6 +330,7 @@
330330
#![feature(unboxed_closures)]
331331
#![feature(unsafe_cell_raw_get)]
332332
#![feature(unwind_attributes)]
333+
#![feature(unwrap_infallible)]
333334
#![feature(vec_into_raw_parts)]
334335
#![feature(vec_spare_capacity)]
335336
// NB: the above list is sorted to minimize merge conflicts.

library/std/src/rt.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,32 @@ fn lang_start_internal(
2424
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
2525
argc: isize,
2626
argv: *const *const u8,
27-
) -> isize {
28-
use crate::panic;
29-
use crate::sys_common;
30-
27+
) -> Result<isize, !> {
28+
use crate::{mem, panic, sys, sys_common};
29+
let rt_abort = move |e| {
30+
mem::forget(e);
31+
rtabort!("initialization or cleanup bug");
32+
};
33+
// Guard against the code called by this function from unwinding outside of the Rust-controlled
34+
// code, which is UB. This is a requirement imposed by a combination of how the
35+
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
36+
// mechanism itself.
37+
//
38+
// There are a couple of instances where unwinding can begin. First is inside of the
39+
// `rt::init`, `rt::cleanup` and similar functions controlled by libstd. In those instances a
40+
// panic is a libstd implementation bug. A quite likely one too, as there isn't any way to
41+
// prevent libstd from accidentally introducing a panic to these functions. Another is from
42+
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
3143
// SAFETY: Only called once during runtime initialization.
32-
unsafe { sys_common::rt::init(argc, argv) };
33-
34-
let exit_code = panic::catch_unwind(main);
35-
36-
sys_common::rt::cleanup();
37-
38-
exit_code.unwrap_or(101) as isize
44+
panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
45+
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
46+
.map_err(move |e| {
47+
mem::forget(e);
48+
rtprintpanic!("drop of the panic payload panicked");
49+
sys::abort_internal()
50+
});
51+
panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
52+
ret_code
3953
}
4054

4155
#[cfg(not(test))]
@@ -50,4 +64,5 @@ fn lang_start<T: crate::process::Termination + 'static>(
5064
argc,
5165
argv,
5266
)
67+
.into_ok()
5368
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-pass
2+
// ignore-emscripten no processes
3+
// ignore-sgx no processes
4+
// ignore-wasm32-bare no unwinding panic
5+
// ignore-avr no unwinding panic
6+
// ignore-nvptx64 no unwinding panic
7+
8+
use std::env;
9+
use std::process::Command;
10+
11+
struct Bomb;
12+
13+
impl Drop for Bomb {
14+
fn drop(&mut self) {
15+
std::panic::panic_any(Bomb);
16+
}
17+
}
18+
19+
fn main() {
20+
let args = env::args().collect::<Vec<_>>();
21+
let output = match &args[..] {
22+
[me] => Command::new(&me).arg("plant the").output(),
23+
[..] => std::panic::panic_any(Bomb),
24+
}.expect("running the command should have succeeded");
25+
println!("{:#?}", output);
26+
let stderr = std::str::from_utf8(&output.stderr);
27+
assert!(stderr.map(|v| {
28+
v.ends_with("drop of the panic payload panicked")
29+
}).unwrap_or(false));
30+
}

0 commit comments

Comments
 (0)