The rustnomicon contains the following example:
#[no_mangle]
extern "C" fn assert_nonzero(input: u32) {
assert!(input != 0)
}
If
assert_nonzero
is called with the argument0
, the runtime is guaranteed to (safely) abort the process, whether or not compiled withpanic=abort
.
The Rust Reference on the other hand claims that:
Behavior considered undefined: [...] Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI.
These two statements seem contradictory to me. assert_nonzero
has the C
ABI, but unwinds from a Rust panic. So is that guaranteed to (safely) abort, or UB? It can't be both.
Chayim Friedman kindly suggested on another question that this is indeed UB.
So is the Rustnomicon simply outdated, or am I misunderstanding something?
More generally: Exactly when is a Rust panic crossing an FFI boundary considered Undefined Behavior?
From the RFC for -unwind
ABIs:
Changes to the behavior of existing ABI strings
Prior to this RFC, any unwinding operation that crossed an
extern "C"
boundary, either from apanic!
"escaping" from a Rust function defined withextern "C"
or by entering Rust from another language via an entrypoint declared withextern "C"
, caused undefined behavior.This RFC retains most of that undefined behavior, with one exception: with the
panic=unwind
runtime,panic!
will cause anabort
if it would otherwise "escape" from a function defined withextern "C"
.This change will be applied to all ABI strings other than
"Rust"
, such as"system"
.
So it appears that this used to be UB, but after this RFC was accepted, this is no longer UB and guaranteed to abort.
I don't know if we can say that the reference is outdated, since we can say that we just don't unwind (but abort) on panics from extern "C"
Rust functions.