Skip to content

Commit 6ff72f8

Browse files
committed
Don't panic across FFI
Panicking across FFI was UB in older Rust versions and thus because of MSRV it's safer to avoid it. This replaces the panic with print+abort on `std` and double panic on no-std. Closes rust-bitcoin#354
1 parent f531be3 commit 6ff72f8

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

contrib/test.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ if [ "$DO_ASAN" = true ]; then
8080
fi
8181

8282
# Test if panic in C code aborts the process (either with a real panic or with SIGILL)
83-
cargo test -- --ignored --exact 'tests::test_panic_raw_ctx_should_terminate_abnormally' 2>&1 | tee /dev/stderr | grep "SIGILL\\|panicked at '\[libsecp256k1\]"
83+
cargo test -- --ignored --exact 'tests::test_panic_raw_ctx_should_terminate_abnormally' 2>&1 | tee /dev/stderr | grep "SIGABRT|SIGILL\\|panicked at '\[libsecp256k1\]"
8484

8585
# Bench
8686
if [ "$DO_BENCH" = true ]; then

secp256k1-sys/src/lib.rs

+30-3
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,33 @@ pub unsafe fn secp256k1_context_destroy(ctx: *mut Context) {
569569
rustsecp256k1_v0_4_1_context_destroy(ctx)
570570
}
571571

572+
/// FFI-safe replacement for panic
573+
///
574+
/// Prints to stderr and aborts with `std`, double panics without `std`.
575+
#[cfg_attr(not(feature = "std"), allow(unused))]
576+
fn ffi_abort(msg: impl core::fmt::Display) -> ! {
577+
#[cfg(feature = "std")]
578+
{
579+
eprintln!("[libsecp256k1] {}", msg);
580+
std::process::abort()
581+
}
582+
#[cfg(not(feature = "std"))]
583+
{
584+
use core::fmt::Display;
585+
586+
// Abort by double panic
587+
struct PanicOnDrop<M: Display>(M);
588+
589+
impl<T: Display> Drop for PanicOnDrop<T> {
590+
fn drop(&mut self) {
591+
panic!("[libsecp256k1] {}", self.0);
592+
}
593+
}
594+
595+
let _bomb = PanicOnDrop(&msg);
596+
panic!("[libsecp256k1] {}", &msg)
597+
}
598+
}
572599

573600
/// **This function is an override for the C function, this is the an edited version of the original description:**
574601
///
@@ -594,7 +621,7 @@ pub unsafe extern "C" fn rustsecp256k1_v0_4_1_default_illegal_callback_fn(messag
594621
use core::str;
595622
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
596623
let msg = str::from_utf8_unchecked(msg_slice);
597-
panic!("[libsecp256k1] illegal argument. {}", msg);
624+
ffi_abort(format_args!("illegal argument. {}", msg));
598625
}
599626

600627
/// **This function is an override for the C function, this is the an edited version of the original description:**
@@ -617,7 +644,7 @@ pub unsafe extern "C" fn rustsecp256k1_v0_4_1_default_error_callback_fn(message:
617644
use core::str;
618645
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
619646
let msg = str::from_utf8_unchecked(msg_slice);
620-
panic!("[libsecp256k1] internal consistency check failed {}", msg);
647+
ffi_abort(format_args!("internal consistency check failed {}", msg));
621648
}
622649

623650
#[cfg(not(rust_secp_no_symbol_renaming))]
@@ -826,7 +853,7 @@ mod fuzz_dummy {
826853
*output = 4;
827854
ptr::copy((*pk).0.as_ptr(), output.offset(1), 64);
828855
} else {
829-
panic!("Bad flags");
856+
ffi_abort(format_args!("Bad flags"));
830857
}
831858
1
832859
}

0 commit comments

Comments
 (0)