From 7afcf51fe4f635ff4e864ee13085397b41880fa6 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Sun, 4 Jun 2017 21:47:24 +0100 Subject: [PATCH 1/2] Always quote program name in Command::spawn on Windows `CreateProcess` 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. --- src/libstd/sys/windows/process.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index dfbc1b581ee..0bd0ce73138 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -427,20 +427,22 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result> { // Encode the command and arguments in a command line string such // that the spawned process may recover them using CommandLineToArgvW. let mut cmd: Vec = Vec::new(); - append_arg(&mut cmd, prog)?; + // Always quote the program name so CreateProcess doesn't interpret args as + // part of the name if the binary wasn't found first time. + append_arg(&mut cmd, prog, true)?; for arg in args { cmd.push(' ' as u16); - append_arg(&mut cmd, arg)?; + append_arg(&mut cmd, arg, false)?; } return Ok(cmd); - fn append_arg(cmd: &mut Vec, arg: &OsStr) -> io::Result<()> { + fn append_arg(cmd: &mut Vec, arg: &OsStr, force_quotes: bool) -> io::Result<()> { // If an argument has 0 characters then we need to quote it to ensure // that it actually gets passed through on the command line or otherwise // it will be dropped entirely when parsed on the other end. ensure_no_nuls(arg)?; let arg_bytes = &arg.as_inner().inner.as_inner(); - let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') + let quote = force_quotes || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(); if quote { cmd.push('"' as u16); @@ -526,7 +528,7 @@ mod tests { assert_eq!( test_wrapper("prog", &["aaa", "bbb", "ccc"]), - "prog aaa bbb ccc" + "\"prog\" aaa bbb ccc" ); assert_eq!( @@ -539,15 +541,15 @@ mod tests { ); assert_eq!( test_wrapper("echo", &["a b c"]), - "echo \"a b c\"" + "\"echo\" \"a b c\"" ); assert_eq!( test_wrapper("echo", &["\" \\\" \\", "\\"]), - "echo \"\\\" \\\\\\\" \\\\\" \\" + "\"echo\" \"\\\" \\\\\\\" \\\\\" \\" ); assert_eq!( test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), - "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}" + "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\"" ); } } From 02955f508e577d104bcd0f6b8b92e7a7b4657708 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Mon, 5 Jun 2017 17:57:32 +0100 Subject: [PATCH 2/2] Add run-make test for Command::spawn on Windows Make sure args aren't interpreted as part of the program name. --- src/test/run-make/windows-spawn/Makefile | 14 ++++++++++++++ src/test/run-make/windows-spawn/hello.rs | 13 +++++++++++++ src/test/run-make/windows-spawn/spawn.rs | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/test/run-make/windows-spawn/Makefile create mode 100644 src/test/run-make/windows-spawn/hello.rs create mode 100644 src/test/run-make/windows-spawn/spawn.rs diff --git a/src/test/run-make/windows-spawn/Makefile b/src/test/run-make/windows-spawn/Makefile new file mode 100644 index 00000000000..f0d4242260f --- /dev/null +++ b/src/test/run-make/windows-spawn/Makefile @@ -0,0 +1,14 @@ +-include ../tools.mk + +ifdef IS_WINDOWS + +all: + $(RUSTC) -o "$(TMPDIR)/hopefullydoesntexist bar.exe" hello.rs + $(RUSTC) spawn.rs + $(TMPDIR)/spawn.exe + +else + +all: + +endif diff --git a/src/test/run-make/windows-spawn/hello.rs b/src/test/run-make/windows-spawn/hello.rs new file mode 100644 index 00000000000..b177f41941d --- /dev/null +++ b/src/test/run-make/windows-spawn/hello.rs @@ -0,0 +1,13 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("Hello World!"); +} diff --git a/src/test/run-make/windows-spawn/spawn.rs b/src/test/run-make/windows-spawn/spawn.rs new file mode 100644 index 00000000000..2913cbe2260 --- /dev/null +++ b/src/test/run-make/windows-spawn/spawn.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::io::ErrorKind; +use std::process::Command; + +fn main() { + // Make sure it doesn't try to run "hopefullydoesntexist bar.exe". + assert_eq!(Command::new("hopefullydoesntexist") + .arg("bar") + .spawn() + .unwrap_err() + .kind(), + ErrorKind::NotFound); +}