Skip to content

Commit c938b58

Browse files
committed
env: fix handling of real-time signals
The nix::sys::signal::Signal enum type does not cover real-time signals. In the current env code, this means that trying to block e.g. SIGRTMIN+7 is silently ignored, apply_signal_action() silently drops signals that cannot be represented as that enum. However, the enum cannot simply be extended to represent the real-time signals, because SIGRTMIN and SIGRTMAX cannot be considered constants. To workaround this, do not use the Signal enum type, and use libc where needed instead of the wrappers. Fixes: #11151
1 parent 3b20d8d commit c938b58

1 file changed

Lines changed: 48 additions & 31 deletions

File tree

src/uu/env/src/env.rs

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ use native_int_str::{
2020
#[cfg(unix)]
2121
use nix::libc;
2222
#[cfg(unix)]
23-
use nix::sys::signal::{
24-
SigHandler::{SigDfl, SigIgn},
25-
SigSet, SigmaskHow, Signal, signal, sigprocmask,
26-
};
23+
use nix::sys::signal::{sigprocmask, SigSet, SigmaskHow};
2724
#[cfg(unix)]
2825
use nix::unistd::execvp;
2926
use std::borrow::Cow;
@@ -36,6 +33,7 @@ use std::ffi::{OsStr, OsString};
3633
use std::io;
3734
use std::io::Write as _;
3835
use std::io::stderr;
36+
use std::mem::zeroed;
3937
#[cfg(unix)]
4038
use std::os::unix::ffi::OsStrExt;
4139

@@ -294,19 +292,6 @@ fn build_signal_request(
294292
Ok(request)
295293
}
296294

297-
#[cfg(unix)]
298-
fn signal_from_value(sig_value: usize) -> UResult<Signal> {
299-
Signal::try_from(sig_value as i32).map_err(|_| {
300-
USimpleError::new(
301-
125,
302-
translate!(
303-
"env-error-invalid-signal",
304-
"signal" => sig_value.to_string().quote()
305-
),
306-
)
307-
})
308-
}
309-
310295
fn load_config_file(opts: &mut Options) -> UResult<()> {
311296
// NOTE: config files are parsed using an INI parser b/c it's available and compatible with ".env"-style files
312297
// ... * but support for actual INI files, although working, is not intended, nor claimed
@@ -1072,15 +1057,10 @@ fn apply_signal_action<F>(
10721057
signal_fn: F,
10731058
) -> UResult<()>
10741059
where
1075-
F: Fn(Signal) -> UResult<()>,
1060+
F: Fn(usize) -> UResult<()>,
10761061
{
10771062
request.for_each_signal(|sig_value, explicit| {
1078-
// On some platforms ALL_SIGNALS may contain values that are not valid in libc.
1079-
// Skip those invalid ones and continue (GNU env also ignores undefined signals).
1080-
let Ok(sig) = signal_from_value(sig_value) else {
1081-
return Ok(());
1082-
};
1083-
signal_fn(sig)?;
1063+
signal_fn(sig_value)?;
10841064
log.record(sig_value, action_kind, explicit);
10851065

10861066
// Set environment variable to communicate to Rust child processes
@@ -1096,9 +1076,14 @@ where
10961076
}
10971077

10981078
#[cfg(unix)]
1099-
fn ignore_signal(sig: Signal) -> UResult<()> {
1079+
fn ignore_signal(sig: usize) -> UResult<()> {
11001080
// SAFETY: This is safe because we write the handler for each signal only once, and therefore "the current handler is the default", as the documentation requires it.
1101-
let result = unsafe { signal(sig, SigIgn) };
1081+
// nix::sys::signal::Signal does not cover real-time signals, so we need to call
1082+
// libc::signal directly.
1083+
let result = unsafe {
1084+
let res = libc::signal(sig as libc::c_int, libc::SIG_IGN);
1085+
nix::errno::Errno::result(res)
1086+
};
11021087
if let Err(err) = result {
11031088
return Err(USimpleError::new(
11041089
125,
@@ -1109,8 +1094,13 @@ fn ignore_signal(sig: Signal) -> UResult<()> {
11091094
}
11101095

11111096
#[cfg(unix)]
1112-
fn reset_signal(sig: Signal) -> UResult<()> {
1113-
let result = unsafe { signal(sig, SigDfl) };
1097+
fn reset_signal(sig: usize) -> UResult<()> {
1098+
// nix::sys::signal::Signal does not cover real-time signals, so we need to call
1099+
// libc::signal directly.
1100+
let result = unsafe {
1101+
let res = libc::signal(sig as libc::c_int, libc::SIG_DFL);
1102+
nix::errno::Errno::result(res)
1103+
};
11141104
if let Err(err) = result {
11151105
return Err(USimpleError::new(
11161106
125,
@@ -1121,9 +1111,36 @@ fn reset_signal(sig: Signal) -> UResult<()> {
11211111
}
11221112

11231113
#[cfg(unix)]
1124-
fn block_signal(sig: Signal) -> UResult<()> {
1125-
let mut set = SigSet::empty();
1126-
set.add(sig);
1114+
fn block_signal(sig: usize) -> UResult<()> {
1115+
// nix::sys::signal::Signal does not cover real time signals, so we need to build
1116+
// sigset_t manually using libc.
1117+
let set = unsafe {
1118+
let mut sigset: libc::sigset_t = zeroed();
1119+
1120+
if let Err(err) = nix::errno::Errno::result(libc::sigemptyset(&mut sigset)) {
1121+
return Err(USimpleError::new(
1122+
125,
1123+
translate!(
1124+
"env-error-failed-set-signal-action",
1125+
"signal" => (sig as i32),
1126+
"error" => err.desc()
1127+
),
1128+
));
1129+
}
1130+
if let Err(err) = nix::errno::Errno::result(libc::sigaddset(&mut sigset, sig as libc::c_int))
1131+
{
1132+
return Err(USimpleError::new(
1133+
125,
1134+
translate!(
1135+
"env-error-failed-set-signal-action",
1136+
"signal" => (sig as i32),
1137+
"error" => err.desc()
1138+
),
1139+
));
1140+
}
1141+
SigSet::from_sigset_t_unchecked(sigset)
1142+
};
1143+
11271144
if let Err(err) = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&set), None) {
11281145
return Err(USimpleError::new(
11291146
125,

0 commit comments

Comments
 (0)