Make FFI Functions Panic-Safe to Avoid Undefined Behavior #8

Closed
opened 2026-03-11 18:05:24 +03:00 by NiXTheDev · 0 comments
NiXTheDev commented 2026-03-11 18:05:24 +03:00 (Migrated from github.com)

Goal: Prevent Rust panics from unwinding across the C ABI boundary, which is undefined behavior.

All functions in ffi.rs that are called from C must not panic. Currently, they may indirectly panic via unwrap(), expect(), or out-of-bounds access.

Solution:
Wrap the body of every FFI-exported function with std::panic::catch_unwind. Convert any panic into an error return (e.g., null pointer or error code) and optionally set an error message.

Example:

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ogex_is_match(handle: *const RegexHandle, input: *const c_char) -> c_int {
    let result = std::panic::catch_unwind(|| {
        // existing logic
    });
    match result {
        Ok(val) => val,
        Err(_) => {
            // log or set error
            0
        }
    }
}

Considerations:

  • Not all functions need to catch panics if they are simple and never panic (e.g., ogex_version). Focus on functions that do nontrivial work.
  • Provide a way to retrieve the last panic message via a thread-local or error output parameter.

Implementation Steps:

  1. Identify all FFI functions that could panic (those calling Regex::new, find, etc.).
  2. Wrap their bodies with catch_unwind.
  3. For functions that return a pointer, return null on panic.
  4. For functions that return an integer, return a sentinel error value (e.g., -1) and set an error string if provided.
**Goal**: Prevent Rust panics from unwinding across the C ABI boundary, which is undefined behavior. All functions in `ffi.rs` that are called from C must not panic. Currently, they may indirectly panic via `unwrap()`, `expect()`, or out-of-bounds access. **Solution**: Wrap the body of every FFI-exported function with `std::panic::catch_unwind`. Convert any panic into an error return (e.g., null pointer or error code) and optionally set an error message. **Example**: ```rust #[unsafe(no_mangle)] pub unsafe extern "C" fn ogex_is_match(handle: *const RegexHandle, input: *const c_char) -> c_int { let result = std::panic::catch_unwind(|| { // existing logic }); match result { Ok(val) => val, Err(_) => { // log or set error 0 } } } ``` **Considerations**: - Not all functions need to catch panics if they are simple and never panic (e.g., ogex_version). Focus on functions that do nontrivial work. - Provide a way to retrieve the last panic message via a thread-local or error output parameter. **Implementation Steps**: 1. Identify all FFI functions that could panic (those calling Regex::new, find, etc.). 2. Wrap their bodies with catch_unwind. 3. For functions that return a pointer, return null on panic. 4. For functions that return an integer, return a sentinel error value (e.g., -1) and set an error string if provided.
Sign in to join this conversation.
No description provided.