6262//! [signal-hook]: https://docs.rs/signal-hook
6363//! [async-signal-safe]: http://www.man7.org/linux/man-pages/man7/signal-safety.7.html
6464
65+ extern crate errno;
6566extern crate libc;
6667
6768mod half_lock;
@@ -77,6 +78,7 @@ use std::sync::atomic::{AtomicPtr, Ordering};
7778use std:: sync:: ONCE_INIT ;
7879use std:: sync:: { Arc , Once } ;
7980
81+ use errno:: Errno ;
8082#[ cfg( not( windows) ) ]
8183use libc:: { c_int, c_void, sigaction, siginfo_t} ;
8284#[ cfg( windows) ]
@@ -337,6 +339,8 @@ impl GlobalData {
337339
338340#[ cfg( windows) ]
339341extern "C" fn handler ( sig : c_int ) {
342+ let _errno = ErrnoGuard :: new ( ) ;
343+
340344 if sig != SIGFPE {
341345 // Windows CRT `signal` resets handler every time, unless for SIGFPE.
342346 // Reregister the handler to retain maximal compatibility.
@@ -384,6 +388,8 @@ extern "C" fn handler(sig: c_int) {
384388// cfg_attr is needed because the `allow(clippy::lint)` syntax was added in Rust 1.31
385389#[ cfg_attr( clippy, allow( clippy:: incompatible_msrv) ) ]
386390extern "C" fn handler ( sig : c_int , info : * mut siginfo_t , data : * mut c_void ) {
391+ let _errno = ErrnoGuard :: new ( ) ;
392+
387393 let globals = GlobalData :: get ( ) ;
388394 let fallback = globals. race_fallback . read ( ) ;
389395 let sigdata = globals. data . read ( ) ;
@@ -421,6 +427,20 @@ extern "C" fn handler(sig: c_int, info: *mut siginfo_t, data: *mut c_void) {
421427 }
422428}
423429
430+ struct ErrnoGuard ( Errno ) ;
431+
432+ impl ErrnoGuard {
433+ fn new ( ) -> Self {
434+ ErrnoGuard ( errno:: errno ( ) )
435+ }
436+ }
437+
438+ impl Drop for ErrnoGuard {
439+ fn drop ( & mut self ) {
440+ errno:: set_errno ( self . 0 ) ;
441+ }
442+ }
443+
424444/// List of forbidden signals.
425445///
426446/// Some signals are impossible to replace according to POSIX and some are so special that this
@@ -831,4 +851,20 @@ mod tests {
831851 // The next time unregistering does nothing and tells us so.
832852 assert ! ( !unregister( signal) ) ;
833853 }
854+
855+ /// Check that errno is not clobbered by the signal handler.
856+ #[ test]
857+ fn save_restore_errno ( ) {
858+ const MAGIC_ERRNO : i32 = 123456 ;
859+ let action = move || {
860+ errno:: set_errno ( Errno ( MAGIC_ERRNO ) ) ;
861+ } ;
862+ unsafe {
863+ register ( SIGUSR1 , action) . unwrap ( ) ;
864+ libc:: raise ( SIGUSR1 ) ;
865+ }
866+ // NB: raise() might clobber errno on some platforms, so this test isn't waterproof. But it
867+ // fails at least sometimes on some platforms if the errno save/restore is removed.
868+ assert ! ( errno:: errno( ) . 0 != MAGIC_ERRNO ) ;
869+ }
834870}
0 commit comments