Skip to content

Commit 9006db1

Browse files
committed
Auto merge of #42436 - ollie27:win_spawn_name, r=alexcrichton
Always quote program name in Command::spawn on Windows [`CreateProcess`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx) will interpret args as part of the binary name if it doesn't find the binary using just the unquoted name. For example if `foo.exe` doesn't exist, `Command::new("foo").arg("bar").spawn()` will try to launch `foo bar.exe` which is clearly not desired.
2 parents 17f493f + 02955f5 commit 9006db1

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

src/libstd/sys/windows/process.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -427,20 +427,22 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
427427
// Encode the command and arguments in a command line string such
428428
// that the spawned process may recover them using CommandLineToArgvW.
429429
let mut cmd: Vec<u16> = Vec::new();
430-
append_arg(&mut cmd, prog)?;
430+
// Always quote the program name so CreateProcess doesn't interpret args as
431+
// part of the name if the binary wasn't found first time.
432+
append_arg(&mut cmd, prog, true)?;
431433
for arg in args {
432434
cmd.push(' ' as u16);
433-
append_arg(&mut cmd, arg)?;
435+
append_arg(&mut cmd, arg, false)?;
434436
}
435437
return Ok(cmd);
436438

437-
fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr) -> io::Result<()> {
439+
fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> io::Result<()> {
438440
// If an argument has 0 characters then we need to quote it to ensure
439441
// that it actually gets passed through on the command line or otherwise
440442
// it will be dropped entirely when parsed on the other end.
441443
ensure_no_nuls(arg)?;
442444
let arg_bytes = &arg.as_inner().inner.as_inner();
443-
let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t')
445+
let quote = force_quotes || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t')
444446
|| arg_bytes.is_empty();
445447
if quote {
446448
cmd.push('"' as u16);
@@ -526,7 +528,7 @@ mod tests {
526528

527529
assert_eq!(
528530
test_wrapper("prog", &["aaa", "bbb", "ccc"]),
529-
"prog aaa bbb ccc"
531+
"\"prog\" aaa bbb ccc"
530532
);
531533

532534
assert_eq!(
@@ -539,15 +541,15 @@ mod tests {
539541
);
540542
assert_eq!(
541543
test_wrapper("echo", &["a b c"]),
542-
"echo \"a b c\""
544+
"\"echo\" \"a b c\""
543545
);
544546
assert_eq!(
545547
test_wrapper("echo", &["\" \\\" \\", "\\"]),
546-
"echo \"\\\" \\\\\\\" \\\\\" \\"
548+
"\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
547549
);
548550
assert_eq!(
549551
test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
550-
"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
552+
"\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
551553
);
552554
}
553555
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-include ../tools.mk
2+
3+
ifdef IS_WINDOWS
4+
5+
all:
6+
$(RUSTC) -o "$(TMPDIR)/hopefullydoesntexist bar.exe" hello.rs
7+
$(RUSTC) spawn.rs
8+
$(TMPDIR)/spawn.exe
9+
10+
else
11+
12+
all:
13+
14+
endif
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
println!("Hello World!");
13+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::io::ErrorKind;
12+
use std::process::Command;
13+
14+
fn main() {
15+
// Make sure it doesn't try to run "hopefullydoesntexist bar.exe".
16+
assert_eq!(Command::new("hopefullydoesntexist")
17+
.arg("bar")
18+
.spawn()
19+
.unwrap_err()
20+
.kind(),
21+
ErrorKind::NotFound);
22+
}

0 commit comments

Comments
 (0)