-
Notifications
You must be signed in to change notification settings - Fork 84
Description
This is probably a very unlikely issue to hit in practice, but since the library is otherwise very careful to do things by the book and document caveats, I wanted to raise it for consideration.
Signal handlers should save the current value of errno on entry and restore this value on exit if they call any functions that set errno. Otherwise, they will clobber errno for the thread being interrupted, which can cause problems if that thread was interrupted after calling a function that set errno but before checking the errno value. glibc documentation says:
Many functions that are AS-Safe may set errno, or modify the floating-point environment, because their doing so does not make them unsuitable for use in signal handlers. However, programs could misbehave should asynchronous signal handlers modify this thread-local state, and the signal handling machinery cannot be counted on to preserve it. Therefore, signal handlers that call functions that may set errno or modify the floating-point environment must save their original values, and restore them before returning.
POSIX-2024 (haven't checked earlier versions) also explicitly ties this to async-signal-safety:
Operations which obtain the value of errno and operations which assign a value to errno shall be async-signal-safe, provided that the signal-catching function saves the value of errno upon entry and restores it before it returns.
I believe this is not an issue for the bare signal handler installed by signal-hook-registry. It seems to only call errno-setting functions in the "NULL siginfo" error case, which aborts the process. However, the registered actions that are invoked in the signal handler need to worry about this. For example, the self-pipe trick as implemented in signal-hook calls write. This write usually shouldn't encounter errors, but making the write end of the pipe non-blocking means that errno will be clobbered if more signals are delivered while the pipe is already full (not common but certainly possible). It's also potentially relevant for anyone using signal-hook-registry directly -- I've become aware of this issue while once again scrutinizing my own signal handler using sem_post and sem_wait to implement thread suspension for a sampling profiler.
I think it would be great if signal-hook-registry could take care of the errno save-restore dance so that individual actions don't need to do it themselves. It's a fairly subtle issue that affects even code that carefully limits itself to async-signal-safe libc functions and it's easy to miss this requirement even when researching async-signal-safety. Unfortunately, implementing errno safe-restore in signal-hook-registry is not trivial:
std::io::Error::last_os_erroronly allows reading errno, not restoring it.- The
libccrate only exposes the raw platform-specific guts underlying theerrnomacro, so it's hard to use portably. - The
errnocrate deals with the platform specific differences to allow reading and writing, but its MSRV policy is different. - Vendoring code from std or the errno crate that papers over differences in libc internals means one more copy of this code will have to be updated for future platforms.
If this is too much trouble, it would be great to at least fix the affected handlers defined in this repository and extend the documentation of signal_hook_registry::register to draw attention to this subtlety.