Skip to content

Commit 2f3c6e4

Browse files
committed
save and restore errno
1 parent 990feef commit 2f3c6e4

File tree

5 files changed

+41
-0
lines changed

5 files changed

+41
-0
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ jobs:
119119
cd signal-hook-registry
120120
sed -i -e '/signal-hook =/d' Cargo.toml
121121
sed -i -e 's/libc = "^0.2"/libc = "=0.2.156"/' Cargo.toml
122+
sed -i -e 's/errno = ">=0.2, <0.4"/errno = "0.2"/' Cargo.toml
122123
cargo check
123124
124125
ancient:
@@ -144,6 +145,7 @@ jobs:
144145
run: |
145146
rm Cargo.lock
146147
sed -i -e 's/libc = "^0.2"/libc = "=0.2.156"/' Cargo.toml
148+
sed -i -e 's/errno = ">=0.2, <0.4"/errno = "0.2"/' signal-hook-registry/Cargo.toml
147149
cargo update
148150
cargo check --no-default-features
149151

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ci-check.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ rm -f Cargo.lock
1212
if [ "$RUST_VERSION" = 1.36.0 ] || [ "$RUST_VERSION" = 1.40.0 ] ; then
1313
sed -i -e 's/libc = "^0.2"/libc = "=0.2.156"/' Cargo.toml
1414
sed -i -e 's/cc = { version = "^1"/cc = { version = "=1.0.79"/' Cargo.toml
15+
sed -i -e 's/errno = ">=0.2, <0.4"/errno = "0.2"/' signal-hook-registry/Cargo.toml
1516
fi
1617

1718
cargo build --all --exclude signal-hook-async-std --exclude signal-hook-tokio

signal-hook-registry/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ maintenance = { status = "actively-developed" }
2020

2121
[dependencies]
2222
libc = "^0.2"
23+
errno = ">=0.2, <0.4"
2324

2425
[dev-dependencies]
2526
signal-hook = { version = "~0.3", path = ".." }

signal-hook-registry/src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
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;
6566
extern crate libc;
6667

6768
mod half_lock;
@@ -78,6 +79,7 @@ use std::sync::atomic::{AtomicPtr, Ordering};
7879
use std::sync::ONCE_INIT;
7980
use std::sync::{Arc, Once};
8081

82+
use errno::Errno;
8183
#[cfg(not(windows))]
8284
use libc::{c_int, c_void, sigaction, siginfo_t};
8385
#[cfg(windows)]
@@ -338,6 +340,8 @@ impl GlobalData {
338340

339341
#[cfg(windows)]
340342
extern "C" fn handler(sig: c_int) {
343+
let _errno = ErrnoGuard::new();
344+
341345
if sig != SIGFPE {
342346
// Windows CRT `signal` resets handler every time, unless for SIGFPE.
343347
// Reregister the handler to retain maximal compatibility.
@@ -385,6 +389,8 @@ extern "C" fn handler(sig: c_int) {
385389
// cfg_attr is needed because the `allow(clippy::lint)` syntax was added in Rust 1.31
386390
#[cfg_attr(clippy, allow(clippy::incompatible_msrv))]
387391
extern "C" fn handler(sig: c_int, info: *mut siginfo_t, data: *mut c_void) {
392+
let _errno = ErrnoGuard::new();
393+
388394
let globals = GlobalData::get();
389395
let fallback = globals.race_fallback.read();
390396
let sigdata = globals.data.read();
@@ -422,6 +428,20 @@ extern "C" fn handler(sig: c_int, info: *mut siginfo_t, data: *mut c_void) {
422428
}
423429
}
424430

431+
struct ErrnoGuard(Errno);
432+
433+
impl ErrnoGuard {
434+
fn new() -> Self {
435+
ErrnoGuard(errno::errno())
436+
}
437+
}
438+
439+
impl Drop for ErrnoGuard {
440+
fn drop(&mut self) {
441+
errno::set_errno(self.0);
442+
}
443+
}
444+
425445
/// List of forbidden signals.
426446
///
427447
/// Some signals are impossible to replace according to POSIX and some are so special that this
@@ -834,4 +854,20 @@ mod tests {
834854
// The next time unregistering does nothing and tells us so.
835855
assert!(!unregister(signal));
836856
}
857+
858+
/// Check that errno is not clobbered by the signal handler.
859+
#[test]
860+
fn save_restore_errno() {
861+
const MAGIC_ERRNO: i32 = 123456;
862+
let action = move || {
863+
errno::set_errno(Errno(MAGIC_ERRNO));
864+
};
865+
unsafe {
866+
register(SIGUSR1, action).unwrap();
867+
libc::raise(SIGUSR1);
868+
}
869+
// NB: raise() might clobber errno on some platforms, so this test isn't waterproof. But it
870+
// fails at least sometimes on some platforms if the errno save/restore is removed.
871+
assert!(errno::errno().0 != MAGIC_ERRNO);
872+
}
837873
}

0 commit comments

Comments
 (0)