Skip to content

Commit d4686c6

Browse files
committed
Use verbatim paths for process::Command if necessary
1 parent 582b696 commit d4686c6

File tree

2 files changed

+26
-13
lines changed

2 files changed

+26
-13
lines changed

library/std/src/sys/windows/process.rs

+21-11
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ use crate::path::{Path, PathBuf};
1919
use crate::ptr;
2020
use crate::sys::c;
2121
use crate::sys::c::NonZeroDWORD;
22+
use crate::sys::cvt;
2223
use crate::sys::fs::{File, OpenOptions};
2324
use crate::sys::handle::Handle;
2425
use crate::sys::path;
2526
use crate::sys::pipe::{self, AnonPipe};
2627
use crate::sys::stdio;
27-
use crate::sys::{cvt, to_u16s};
2828
use crate::sys_common::mutex::StaticMutex;
2929
use crate::sys_common::process::{CommandEnv, CommandEnvs};
3030
use crate::sys_common::{AsInner, IntoInner};
@@ -269,8 +269,13 @@ impl Command {
269269
None
270270
};
271271
let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?;
272+
let is_batch_file = program
273+
.extension()
274+
.map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat"))
275+
.unwrap_or(false);
276+
let program = path::maybe_verbatim(&program)?;
272277
let mut cmd_str =
273-
make_command_line(program.as_os_str(), &self.args, self.force_quotes_enabled)?;
278+
make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?;
274279
cmd_str.push(0); // add null terminator
275280

276281
// stolen from the libuv code.
@@ -309,7 +314,6 @@ impl Command {
309314
si.hStdOutput = stdout.as_raw_handle();
310315
si.hStdError = stderr.as_raw_handle();
311316

312-
let program = to_u16s(&program)?;
313317
unsafe {
314318
cvt(c::CreateProcessW(
315319
program.as_ptr(),
@@ -730,7 +734,12 @@ enum Quote {
730734

731735
// Produces a wide string *without terminating null*; returns an error if
732736
// `prog` or any of the `args` contain a nul.
733-
fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> {
737+
fn make_command_line(
738+
prog: &[u16],
739+
args: &[Arg],
740+
force_quotes: bool,
741+
is_batch_file: bool,
742+
) -> io::Result<Vec<u16>> {
734743
// Encode the command and arguments in a command line string such
735744
// that the spawned process may recover them using CommandLineToArgvW.
736745
let mut cmd: Vec<u16> = Vec::new();
@@ -739,17 +748,18 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu
739748
// need to add an extra pair of quotes surrounding the whole command line
740749
// so they are properly passed on to the script.
741750
// See issue #91991.
742-
let is_batch_file = Path::new(prog)
743-
.extension()
744-
.map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat"))
745-
.unwrap_or(false);
746751
if is_batch_file {
747752
cmd.push(b'"' as u16);
748753
}
749754

750-
// Always quote the program name so CreateProcess doesn't interpret args as
751-
// part of the name if the binary wasn't found first time.
752-
append_arg(&mut cmd, prog, Quote::Always)?;
755+
// Always quote the program name so CreateProcess to avoid ambiguity when
756+
// the child process parses its arguments.
757+
// Note that quotes aren't escaped here because they can't be used in arg0.
758+
// But that's ok because file paths can't contain quotes.
759+
cmd.push(b'"' as u16);
760+
cmd.extend_from_slice(prog.strip_suffix(&[0]).unwrap_or(prog));
761+
cmd.push(b'"' as u16);
762+
753763
for arg in args {
754764
cmd.push(' ' as u16);
755765
let (arg, quote) = match arg {

library/std/src/sys/windows/process/tests.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use super::Arg;
33
use crate::env;
44
use crate::ffi::{OsStr, OsString};
55
use crate::process::Command;
6+
use crate::sys::to_u16s;
67

78
#[test]
89
fn test_raw_args() {
910
let command_line = &make_command_line(
10-
OsStr::new("quoted exe"),
11+
&to_u16s("quoted exe").unwrap(),
1112
&[
1213
Arg::Regular(OsString::from("quote me")),
1314
Arg::Raw(OsString::from("quote me *not*")),
@@ -16,6 +17,7 @@ fn test_raw_args() {
1617
Arg::Regular(OsString::from("optional-quotes")),
1718
],
1819
false,
20+
false,
1921
)
2022
.unwrap();
2123
assert_eq!(
@@ -28,9 +30,10 @@ fn test_raw_args() {
2830
fn test_make_command_line() {
2931
fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
3032
let command_line = &make_command_line(
31-
OsStr::new(prog),
33+
&to_u16s(prog).unwrap(),
3234
&args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
3335
force_quotes,
36+
false,
3437
)
3538
.unwrap();
3639
String::from_utf16(command_line).unwrap()

0 commit comments

Comments
 (0)