Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

env argv0 overwrite possibility (unix only) - fixes new gnu test version #6154

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ UNIX_PROGS := \
nohup \
pathchk \
pinky \
sleep \
stat \
stdbuf \
timeout \
Expand Down Expand Up @@ -221,6 +222,7 @@ TEST_PROGS := \
rmdir \
runcon \
seq \
sleep \
sort \
split \
stat \
Expand Down
82 changes: 66 additions & 16 deletions src/uu/env/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
use std::ops::Deref;

#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
use std::os::unix::process::{CommandExt, ExitStatusExt};
use std::process::{self};
use uucore::display::Quotable;
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError};
Expand All @@ -48,6 +48,7 @@
unsets: Vec<&'a OsStr>,
sets: Vec<(Cow<'a, OsStr>, Cow<'a, OsStr>)>,
program: Vec<&'a OsStr>,
argv0: Option<&'a OsStr>,
}

// print name=value env pairs on screen
Expand Down Expand Up @@ -173,7 +174,7 @@
Arg::new("debug")
.short('v')
.long("debug")
.action(ArgAction::SetTrue)
.action(ArgAction::Count)
.help("print verbose information for each processing step"),
)
.arg(
Expand All @@ -184,6 +185,16 @@
.action(ArgAction::Set)
.value_parser(ValueParser::os_string())
.help("process and split S into separate arguments; used to pass multiple arguments on shebang lines")
).arg(
Arg::new("argv0")
.overrides_with("argv0")
cre4ture marked this conversation as resolved.
Show resolved Hide resolved
.short('a')
.long("argv0")
.value_name("a")
.action(ArgAction::Set)
.value_parser(ValueParser::os_string())
.help("Override the zeroth argument passed to the command being executed.\
Without this option a default value of `command` is used.")
)
.arg(
Arg::new("vars")
Expand Down Expand Up @@ -248,6 +259,7 @@
#[derive(Default)]
struct EnvAppData {
do_debug_printing: bool,
do_input_debug_printing: Option<bool>,
had_string_argument: bool,
}

Expand All @@ -273,14 +285,19 @@
b if check_and_handle_string_args(b, "-S", &mut all_args, None)? => {
self.had_string_argument = true;
}
b if check_and_handle_string_args(b, "-vS", &mut all_args, None)? => {
self.do_debug_printing = true;
self.had_string_argument = true;
}
b if check_and_handle_string_args(
b,
"-vS",
"-vvS",
&mut all_args,
Some(original_args),
)? =>
{
self.do_debug_printing = true;
self.do_input_debug_printing = Some(false); // already done
self.had_string_argument = true;
}
_ => {
Expand Down Expand Up @@ -323,10 +340,15 @@
fn run_env(&mut self, original_args: impl uucore::Args) -> UResult<()> {
let (original_args, matches) = self.parse_arguments(original_args)?;

let did_debug_printing_before = self.do_debug_printing; // could have been done already as part of the "-vS" string parsing
let do_debug_printing = self.do_debug_printing || matches.get_flag("debug");
if do_debug_printing && !did_debug_printing_before {
debug_print_args(&original_args);
self.do_debug_printing = self.do_debug_printing || (0 != matches.get_count("debug"));
self.do_input_debug_printing = self
.do_input_debug_printing
.or(Some(matches.get_count("debug") >= 2));
if let Some(value) = self.do_input_debug_printing {
if value {
debug_print_args(&original_args);
self.do_input_debug_printing = Some(false);
BenWiederhake marked this conversation as resolved.
Show resolved Hide resolved
}
}

let mut opts = make_options(&matches)?;
Expand All @@ -349,7 +371,7 @@
// no program provided, so just dump all env vars to stdout
print_env(opts.line_ending);
} else {
return self.run_program(opts, do_debug_printing);
return self.run_program(opts, self.do_debug_printing);
}

Ok(())
Expand All @@ -361,22 +383,48 @@
do_debug_printing: bool,
) -> Result<(), Box<dyn UError>> {
let prog = Cow::from(opts.program[0]);
#[cfg(unix)]
let mut arg0 = prog.clone();
#[cfg(not(unix))]
let arg0 = prog.clone();
let args = &opts.program[1..];
if do_debug_printing {
eprintln!("executable: {}", prog.quote());
for (i, arg) in args.iter().enumerate() {
eprintln!("arg[{}]: {}", i, arg.quote());
}
}
// we need to execute a command

/*
* On Unix-like systems Command::status either ends up calling either fork or posix_spawnp
* (which ends up calling clone). Keep using the current process would be ideal, but the
* standard library contains many checks and fail-safes to ensure the process ends up being
* created. This is much simpler than dealing with the hassles of calling execvp directly.
*/
match process::Command::new(&*prog).args(args).status() {
let mut cmd = process::Command::new(&*prog);
cmd.args(args);

if let Some(_argv0) = opts.argv0 {
#[cfg(unix)]
{
cmd.arg0(_argv0);
arg0 = Cow::Borrowed(_argv0);
if do_debug_printing {
eprintln!("argv0: {}", arg0.quote());
}
}

#[cfg(not(unix))]
return Err(USimpleError::new(

Check warning on line 412 in src/uu/env/src/env.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/env/src/env.rs#L412

Added line #L412 was not covered by tests
2,
"--argv0 is currently not supported on this platform",
));
}

if do_debug_printing {
eprintln!("executing: {}", prog.maybe_quote());
let arg_prefix = " arg";
eprintln!("{}[{}]= {}", arg_prefix, 0, arg0.quote());
for (i, arg) in args.iter().enumerate() {
eprintln!("{}[{}]= {}", arg_prefix, i + 1, arg.quote());
}
}

match cmd.status() {
Ok(exit) if !exit.success() => {
#[cfg(unix)]
if let Some(exit_code) = exit.code() {
Expand Down Expand Up @@ -443,6 +491,7 @@
Some(v) => v.map(|s| s.as_os_str()).collect(),
None => Vec::with_capacity(0),
};
let argv0 = matches.get_one::<OsString>("argv0").map(|s| s.as_os_str());

let mut opts = Options {
ignore_env,
Expand All @@ -452,6 +501,7 @@
unsets,
sets: vec![],
program: vec![],
argv0,
};

let mut begin_prog_opts = false;
Expand Down
Loading
Loading