Handling Process Signals Safely in Rust/C Interoperability
Understanding the Problem
Signals are asynchronous interrupts that can arrive mid-instruction. Your Rust code might be halfway through updating a Vec’s length when SIGTERM hits. If your C signal handler calls back into Rust code that touches that Vec, you’ve got heap corruption that only manifests under production load when signal timing aligns perfectly with allocation operations.
The Core Problem: Async-Signal-Safety Doesn’t Exist in Rust
POSIX defines 120 async-signal-safe functions safe to call from signal handlers.
write(),_exit(), and signal management syscalls make the list. Everything else—including malloc, printf, and pthread mutexes—can deadlock if interrupted mid-operation. Rust provides zero async-signal-safety guarantees. The borrow checker assumes synchronous execution. When signals violate this assumption, memory safety guarantees evaporate.I debugged a production issue where SIGTERM handlers called Rust cleanup functions via FFI. The signal interrupted another Vec operation. Both tried manipulating the global allocator simultaneously. Result: heap metadata corruption visible only in customer environments after days of runtime. The backtrace showed malloc arena locks held in both the interrupted context and the signal handler.


