Skip to content

Commit 9c9a0da

Browse files
committed
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 implementation approaches to consider. Some simpler, some more complicated. This particular solution is nice in that it also guards against accidental implementation issues in various pieces of runtime code, something we cannot prevent statically right now. Fixes #86030
1 parent 66ba810 commit 9c9a0da

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)]
@@ -297,7 +298,6 @@
297298
#![feature(nll)]
298299
#![feature(nonnull_slice_from_raw_parts)]
299300
#![feature(once_cell)]
300-
#![feature(auto_traits)]
301301
#![feature(panic_info_message)]
302302
#![feature(panic_internals)]
303303
#![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)