-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
panic! in Command
child forked from non-main thread results in exit status 0
#79740
Comments
Error: Label libs-impl can only be set by Rust team members Please let |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@rustbot modify labels +T-libs |
Unwinding from a |
Jonas Schievink writes ("Re: [rust-lang/rust] panic! in `Command` child forked from non-main thread results in exit status 0 (#79740)"):
Unwinding from a pre_exec callback is probably UB
This isn't documented AFAICT. There is an extensive discussion of the
strange state in the pre_exec callback but nothing saying one
shouldn't panic. Furthermore, presumably any panicking bugs in
Command itself (or anything it calls) will have the same effect.
Perhaps the same thing could occur in a Rust library being used in a
mostly-C program which made a thread and then forked.
Ian.
…--
Ian Jackson <ijackson@chiark.greenend.org.uk> These opinions are my own.
Pronouns: they/he. If I emailed you from @fyvzl.net or @evade.org.uk,
that is a private address which bypasses my fierce spamfilter.
|
Trying to catch the unwind seems plausible even if attempting to unwind causes undefined behaviour already. Alternatively the callbacks could be invoked through a function with |
Here is another program with weird behaviour:
Output
Demonstrating that I think the solution has to be that in the child process, unwinding must stop at |
Unwinding past fork() in the child causes whatever traps the unwind to return twice. This is very strange and clearly not desirable. With the default behaviour of the thread library, this can even result in a panic in the child being transformed into zero exit status (ie, success) as seen in the parent! If unwinding reaches the fork point, the child should abort. Annotating the closure with #[unwind(aborts)] is not sufficiently stable right now, so we use catch_unwind. This requires marking the closure UnwindSafe - which is fine regardless of the contents of the closure, since we abort on unwind and won't therefore touch anything the closure might have captured. Fixes rust-lang#79740. Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Do not allocate or unwind after fork ### Objective scenarios * Make (simple) panics safe in `Command::pre_exec_hook`, including most `panic!` calls, `Option::unwrap`, and array bounds check failures. * Make it possible to `libc::fork` and then safely panic in the child (needed for the above, but this requirement means exposing the new raw hook API which the `Command` implementation needs). * In singlethreaded programs, where panic in `pre_exec_hook` is already memory-safe, prevent the double-unwinding malfunction rust-lang#79740. I think we want to make panic after fork safe even though the post-fork child environment is only experienced by users of `unsafe`, beause the subset of Rust in which any panic is UB is really far too hazardous and unnatural. #### Approach * Provide a way for a program to, at runtime, switch to having panics abort. This makes it possible to panic without making *any* heap allocations, which is needed because on some platforms malloc is UB in a child forked from a multithreaded program (see rust-lang#80263 (comment), and maybe also the SuS [spec](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)). * Make that change in the child spawned by `Command`. * Document the rules comprehensively enough that a programmer has a fighting chance of writing correct code. * Test that this all works as expected (and in particular, that there aren't any heap allocations we missed) Fixes rust-lang#79740 #### Rejected (or previously attempted) approaches * Change the panic machinery to be able to unwind without allocating, at least when the payload and message are both `'static`. This seems like it would be even more subtle. Also that is a potentially-hot path which I don't want to mess with. * Change the existing panic hook mechanism to not convert the message to a `String` before calling the hook. This would be a surprising change for existing code and would not be detected by the type system. * Provide a `raw_panic_hook` function to intercept panics in a way that doesn't allocate. (That was an earlier version of this MR.) ### History This MR could be considered a v2 of rust-lang#80263. Thanks to everyone who commented there. In particular, thanks to `@m-ou-se,` `@Mark-Simulacrum` and `@hyd-dev.` (Tagging you since I think you might be interested in this new MR.) Compared to rust-lang#80263, this MR has very substantial changes and additions. Additionally, I have recently (2021-04-20) completely revised this series following very helpful comments from `@m-ou-se.` r? `@m-ou-se`
Do not allocate or unwind after fork ### Objective scenarios * Make (simple) panics safe in `Command::pre_exec_hook`, including most `panic!` calls, `Option::unwrap`, and array bounds check failures. * Make it possible to `libc::fork` and then safely panic in the child (needed for the above, but this requirement means exposing the new raw hook API which the `Command` implementation needs). * In singlethreaded programs, where panic in `pre_exec_hook` is already memory-safe, prevent the double-unwinding malfunction rust-lang#79740. I think we want to make panic after fork safe even though the post-fork child environment is only experienced by users of `unsafe`, beause the subset of Rust in which any panic is UB is really far too hazardous and unnatural. #### Approach * Provide a way for a program to, at runtime, switch to having panics abort. This makes it possible to panic without making *any* heap allocations, which is needed because on some platforms malloc is UB in a child forked from a multithreaded program (see rust-lang#80263 (comment), and maybe also the SuS [spec](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)). * Make that change in the child spawned by `Command`. * Document the rules comprehensively enough that a programmer has a fighting chance of writing correct code. * Test that this all works as expected (and in particular, that there aren't any heap allocations we missed) Fixes rust-lang#79740 #### Rejected (or previously attempted) approaches * Change the panic machinery to be able to unwind without allocating, at least when the payload and message are both `'static`. This seems like it would be even more subtle. Also that is a potentially-hot path which I don't want to mess with. * Change the existing panic hook mechanism to not convert the message to a `String` before calling the hook. This would be a surprising change for existing code and would not be detected by the type system. * Provide a `raw_panic_hook` function to intercept panics in a way that doesn't allocate. (That was an earlier version of this MR.) ### History This MR could be considered a v2 of rust-lang#80263. Thanks to everyone who commented there. In particular, thanks to `@m-ou-se,` `@Mark-Simulacrum` and `@hyd-dev.` (Tagging you since I think you might be interested in this new MR.) Compared to rust-lang#80263, this MR has very substantial changes and additions. Additionally, I have recently (2021-04-20) completely revised this series following very helpful comments from `@m-ou-se.` r? `@m-ou-se`
If a panic occurs in a child process forked off by
Command
on a thread other than the main thread, the whole child process exits 0, so it looks like things have worked when in fact they've gone catastrophically wrong. This seems like a serious error handling bug.@rustbot modify labels +T-libs +libs-impl
Steps to reproduce
Actual behaviour
Expected behaviour
Command.status
tries to run the command, soCommand.status
forks, and then runs thepre_exec
closurepre_exec
closure panicsSIGABRT
or in any case nonzerost
is not a successSpecifically, the last line should be something like
ExitStatus(ExitStatus(25856))
(which is what you get without thethread::spawn
)Meta
rustc --version --verbose
:Backtrace (not particularly enlightening)
``` thread '' panicked at 'crash now!', t.rs:11:27 stack backtrace: 0: std::panicking::begin_panic 1: t::main::{{closure}}::{{closure}} 2: as core::ops::function::FnMut>::call_mut at /rustc/1773f60ea5d42e86b8fdf78d2fc5221ead222bc1/library/alloc/src/boxed.rs:1314:9 3: std::sys::unix::process::process_inner::::do_exec at /rustc/1773f60ea5d42e86b8fdf78d2fc5221ead222bc1/library/std/src/sys/unix/process/process_unix.rs:228:13 4: std::sys::unix::process::process_inner::::spawn at /rustc/1773f60ea5d42e86b8fdf78d2fc5221ead222bc1/library/std/src/sys/unix/process/process_unix.rs:58:36 5: std::process::Command::status at /rustc/1773f60ea5d42e86b8fdf78d2fc5221ead222bc1/library/std/src/process.rs:904:9 6: t::main::{{closure}} note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ExitStatus(ExitStatus(0))
The text was updated successfully, but these errors were encountered: