Skip to content

Panics through FFI in error callbacks #228

Closed
@elichai

Description

@elichai

Right now we override the error callbacks at compile time with:

pub unsafe extern "C" fn rustsecp256k1_v0_1_1_default_illegal_callback_fn(message: *const c_char, _data: *mut c_void) {
use core::str;
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
let msg = str::from_utf8_unchecked(msg_slice);
panic!("[libsecp256k1] illegal argument. {}", msg);
}

pub unsafe extern "C" fn rustsecp256k1_v0_1_1_default_error_callback_fn(message: *const c_char, _data: *mut c_void) {
use core::str;
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
let msg = str::from_utf8_unchecked(msg_slice);
panic!("[libsecp256k1] internal consistency check failed {}", msg);
}

But this is sadly UB if the panic strategy is unwind(the default):

Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI.
https://doc.rust-lang.org/reference/behavior-considered-undefined.html

You must absolutely catch any panics at the FFI boundary
https://doc.rust-lang.org/nomicon/unwinding.html

rust-lang/rust#52652

Now the easiest solution is to replace the panic with abort, but then we have a few problems:

  1. We lose the printing, the user just gets a Aborted (core dumped) or Illegal instruction (core dumped) which makes it very hard to pinpoint (We could conditionally print if std is on, but that sucks).
  2. Which abort do we call?
    2.a. The libc one?(adds the assumption of libc which might break for wasm).
    2.b. The intrinsic one? (requires nightly).
    2.c. std::process::abort? requires libstd.
    2.d. Stabilize intrinsics::abort in core rust-lang/rfcs#2512

Another potential solutions:

  1. Advise users to use panic=abort (imo this isn't a good solution).
  2. Conditionally use std::panic::catch_unwind which will catch the panic if std is on but will still print the output and/or stacktrace.
  3. Wait for "C unwind" which allows unwinding the stack through C. (https://github.com/rust-lang/project-ffi-unwind/blob/master/rfcs/0000-c-unwind-abi.md)
  4. Wait for Add #[cfg(panic = '...')] rust-lang/rust#74754 which allows to condition on if panic=abort or not.
  5. Dive deep into the machinery of panic and see if we can still trigger printing but abort instead, probably won't be possible in stable.
  6. Propose new language features to fix this(i.e. abort!(...)), but even if they'll get accepted we won't use that release for a long time.

(Thanks to @joshtriplett for some of the suggestions)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions