Skip to content

Commit add24d2

Browse files
authored
Rollup merge of rust-lang#85377 - ijackson:abort-docs, r=m-ou-se
aborts: Clarify documentation and comments In the docs for intrinsics::abort(): * Strengthen the recommendation by to use process::abort instead. * Document the fact that it sometimes (ab)uses an LLVM debug trap and what the likely consequences are. * State that the precise behaviour is unstable. In the docs for process::abort(): * Promise that we have the same behaviour as C `abort()`. * Document the likely consequences, including, specifically, the consequences on Unix. In the internal comment for unix::abort_internal: * Refer to the public docs for the public API functions. * Correct and expand the description of libc::abort. Specifically: * Do not claim that abort() unregisters signal handlers. It doesn't; it honours the SIGABRT handler. * Discuss, extensively, the issue with abort() flushing stdio buffers. * Describe the glibc behaviour in some detail. Co-authored-by: Mark Wooding <[email protected]> Signed-off-by: Ian Jackson <[email protected]> Fixes rust-lang#40230
2 parents 1fcd9ab + 08d912f commit add24d2

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

library/core/src/intrinsics.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -717,8 +717,14 @@ extern "rust-intrinsic" {
717717
/// Therefore, implementations must not require the user to uphold
718718
/// any safety invariants.
719719
///
720-
/// A more user-friendly and stable version of this operation is
721-
/// [`std::process::abort`](../../std/process/fn.abort.html).
720+
/// [`std::process::abort`](../../std/process/fn.abort.html) is to be preferred if possible,
721+
/// as its behavior is more user-friendly and more stable.
722+
///
723+
/// The current implementation of `intrinsics::abort` is to invoke an invalid instruction,
724+
/// on most platforms.
725+
/// On Unix, the
726+
/// process will probably terminate with a signal like `SIGABRT`, `SIGILL`, `SIGTRAP`, `SIGSEGV` or
727+
/// `SIGBUS`. The precise behaviour is not guaranteed and not stable.
722728
pub fn abort() -> !;
723729

724730
/// Informs the optimizer that this point in the code is not reachable,

library/std/src/process.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,9 @@ pub fn exit(code: i32) -> ! {
18981898
/// process, no destructors on the current stack or any other thread's stack
18991899
/// will be run.
19001900
///
1901+
/// Rust IO buffers (eg, from `BufWriter`) will not be flushed.
1902+
/// Likewise, C stdio buffers will (on most platforms) not be flushed.
1903+
///
19011904
/// This is in contrast to the default behaviour of [`panic!`] which unwinds
19021905
/// the current thread's stack and calls all destructors.
19031906
/// When `panic="abort"` is set, either as an argument to `rustc` or in a
@@ -1908,6 +1911,10 @@ pub fn exit(code: i32) -> ! {
19081911
/// this function at a known point where there are no more destructors left
19091912
/// to run.
19101913
///
1914+
/// The process's termination will be similar to that from the C `abort()`
1915+
/// function. On Unix, the process will terminate with signal `SIGABRT`, which
1916+
/// typically means that the shell prints "Aborted".
1917+
///
19111918
/// # Examples
19121919
///
19131920
/// ```no_run

library/std/src/sys/unix/mod.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,41 @@ pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> {
217217
if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) }
218218
}
219219

220-
// On Unix-like platforms, libc::abort will unregister signal handlers
221-
// including the SIGABRT handler, preventing the abort from being blocked, and
222-
// fclose streams, with the side effect of flushing them so libc buffered
223-
// output will be printed. Additionally the shell will generally print a more
224-
// understandable error message like "Abort trap" rather than "Illegal
225-
// instruction" that intrinsics::abort would cause, as intrinsics::abort is
226-
// implemented as an illegal instruction.
220+
// libc::abort() will run the SIGABRT handler. That's fine because anyone who
221+
// installs a SIGABRT handler already has to expect it to run in Very Bad
222+
// situations (eg, malloc crashing).
223+
//
224+
// Current glibc's abort() function unblocks SIGABRT, raises SIGABRT, clears the
225+
// SIGABRT handler and raises it again, and then starts to get creative.
226+
//
227+
// See the public documentation for `intrinsics::abort()` and `process::abort()`
228+
// for further discussion.
229+
//
230+
// There is confusion about whether libc::abort() flushes stdio streams.
231+
// libc::abort() is required by ISO C 99 (7.14.1.1p5) to be async-signal-safe,
232+
// so flushing streams is at least extremely hard, if not entirely impossible.
233+
//
234+
// However, some versions of POSIX (eg IEEE Std 1003.1-2001) required abort to
235+
// do so. In 1003.1-2004 this was fixed.
236+
//
237+
// glibc's implementation did the flush, unsafely, before glibc commit
238+
// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]' by Florian
239+
// Weimer. According to glibc's NEWS:
240+
//
241+
// The abort function terminates the process immediately, without flushing
242+
// stdio streams. Previous glibc versions used to flush streams, resulting
243+
// in deadlocks and further data corruption. This change also affects
244+
// process aborts as the result of assertion failures.
245+
//
246+
// This is an accurate description of the problem. The only solution for
247+
// program with nontrivial use of C stdio is a fixed libc - one which does not
248+
// try to flush in abort - since even libc-internal errors, and assertion
249+
// failures generated from C, will go via abort().
250+
//
251+
// On systems with old, buggy, libcs, the impact can be severe for a
252+
// multithreaded C program. It is much less severe for Rust, because Rust
253+
// stdlib doesn't use libc stdio buffering. In a typical Rust program, which
254+
// does not use C stdio, even a buggy libc::abort() is, in fact, safe.
227255
pub fn abort_internal() -> ! {
228256
unsafe { libc::abort() }
229257
}

0 commit comments

Comments
 (0)