env: Disallow __ALL__ in --{ignore,default,block}-signal (#11218)

Co-authored-by: aweinstock <avi@zellic.io>
This commit is contained in:
Avi Weinstock
2026-03-18 17:31:31 -04:00
committed by GitHub
parent 0474e55b10
commit 74bf86dbea
2 changed files with 70 additions and 21 deletions
+51 -21
View File
@@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) chdir progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction Sigmask sigprocmask
// spell-checker:ignore (ToDO) chdir progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction Sigmask sigprocmask elidable
pub mod native_int_str;
pub mod split_iterator;
@@ -168,10 +168,6 @@ fn parse_signal_opt(target: &mut SignalRequest, opt: &OsStr) -> UResult<()> {
if opt.is_empty() {
return Ok(());
}
if opt == "__ALL__" {
target.apply_all = true;
return Ok(());
}
for sig in opt
.as_bytes()
@@ -193,7 +189,7 @@ fn parse_signal_opt(target: &mut SignalRequest, opt: &OsStr) -> UResult<()> {
}
#[cfg(unix)]
#[derive(Default)]
#[derive(Default, Debug)]
struct SignalRequest {
apply_all: bool,
signals: BTreeSet<usize>,
@@ -268,7 +264,11 @@ impl SignalActionLog {
}
#[cfg(unix)]
fn build_signal_request(matches: &clap::ArgMatches, option: &str) -> UResult<SignalRequest> {
fn build_signal_request(
matches: &clap::ArgMatches,
option: &str,
signal_apply_all: &BTreeSet<&str>,
) -> UResult<SignalRequest> {
let mut request = SignalRequest::default();
let mut provided_values = 0usize;
@@ -276,7 +276,9 @@ fn build_signal_request(matches: &clap::ArgMatches, option: &str) -> UResult<Sig
if let Some(iter) = matches.get_many::<OsString>(option) {
for opt in iter {
if opt.is_empty() {
explicit_empty = true;
if !signal_apply_all.contains(option) {
explicit_empty = true;
}
continue;
}
provided_values += 1;
@@ -538,6 +540,13 @@ struct EnvAppData {
had_string_argument: bool,
}
struct ParsedArguments {
original_args: Vec<OsString>,
matches: clap::ArgMatches,
#[cfg(unix)]
signal_apply_all: BTreeSet<&'static str>,
}
impl EnvAppData {
fn make_error_no_such_file_or_dir(&self, prog: &OsStr) -> Box<dyn UError> {
uucore::show_error!(
@@ -634,17 +643,20 @@ impl EnvAppData {
fn parse_arguments(
&mut self,
original_args: impl uucore::Args,
) -> Result<(Vec<OsString>, clap::ArgMatches), Box<dyn UError>> {
) -> Result<ParsedArguments, Box<dyn UError>> {
let original_args: Vec<OsString> = original_args.collect();
let mut args = self.process_all_string_arguments(&original_args)?;
let args = self.process_all_string_arguments(&original_args)?;
#[cfg(unix)]
let mut signal_apply_all = BTreeSet::new();
for arg in &mut args {
#[cfg(unix)]
for arg in &args {
if arg == "--ignore-signal" {
*arg = OsString::from("--ignore-signal=__ALL__");
signal_apply_all.insert(options::IGNORE_SIGNAL);
} else if arg == "--default-signal" {
*arg = OsString::from("--default-signal=__ALL__");
signal_apply_all.insert(options::DEFAULT_SIGNAL);
} else if arg == "--block-signal" {
*arg = OsString::from("--block-signal=__ALL__");
signal_apply_all.insert(options::BLOCK_SIGNAL);
}
}
@@ -669,11 +681,21 @@ impl EnvAppData {
}
}
};
Ok((original_args, matches))
Ok(ParsedArguments {
original_args,
matches,
#[cfg(unix)]
signal_apply_all,
})
}
fn run_env(&mut self, original_args: impl uucore::Args) -> UResult<()> {
let (original_args, matches) = self.parse_arguments(original_args)?;
let ParsedArguments {
original_args,
matches,
#[cfg(unix)]
signal_apply_all,
} = self.parse_arguments(original_args)?;
self.do_debug_printing = self.do_debug_printing || (0 != matches.get_count("debug"));
self.do_input_debug_printing = self
@@ -686,7 +708,11 @@ impl EnvAppData {
}
}
let mut opts = make_options(&matches)?;
let mut opts = make_options(
&matches,
#[cfg(unix)]
&signal_apply_all,
)?;
apply_change_directory(&opts)?;
@@ -888,7 +914,11 @@ fn apply_removal_of_all_env_vars(opts: &Options<'_>) {
}
}
fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
#[cfg_attr(not(unix), allow(clippy::elidable_lifetime_names))]
fn make_options<'a>(
matches: &'a clap::ArgMatches,
#[cfg(unix)] signal_apply_all: &BTreeSet<&'static str>,
) -> UResult<Options<'a>> {
let ignore_env = matches.get_flag("ignore-environment");
let line_ending = LineEnding::from_zero_flag(matches.get_flag("null"));
let running_directory = matches
@@ -907,11 +937,11 @@ fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
.map(OsString::as_os_str);
#[cfg(unix)]
let ignore_signal = build_signal_request(matches, options::IGNORE_SIGNAL)?;
let ignore_signal = build_signal_request(matches, options::IGNORE_SIGNAL, signal_apply_all)?;
#[cfg(unix)]
let default_signal = build_signal_request(matches, options::DEFAULT_SIGNAL)?;
let default_signal = build_signal_request(matches, options::DEFAULT_SIGNAL, signal_apply_all)?;
#[cfg(unix)]
let block_signal = build_signal_request(matches, options::BLOCK_SIGNAL)?;
let block_signal = build_signal_request(matches, options::BLOCK_SIGNAL, signal_apply_all)?;
#[cfg(unix)]
let list_signal_handling = matches.get_flag(options::LIST_SIGNAL_HANDLING);
+19
View File
@@ -2009,3 +2009,22 @@ fn test_ignore_signal_pipe_broken_pipe_regression() {
"With --ignore-signal=PIPE, process should exit gracefully (0 or 1), got: {ignore_signal_exit_code}"
);
}
#[test]
#[cfg(unix)]
fn test_env_disallow_double_underscore_all() {
new_ucmd!()
.args(&["--ignore-signal=__ALL__", "true"])
.fails()
.stderr_contains("invalid signal");
new_ucmd!()
.args(&["--default-signal=__ALL__", "true"])
.fails()
.stderr_contains("invalid signal");
new_ucmd!()
.args(&["--block-signal=__ALL__", "true"])
.fails()
.stderr_contains("invalid signal");
}