about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-02-23 06:18:06 +0100
committerGitHub <noreply@github.com>2023-02-23 06:18:06 +0100
commitc4a4bce6950260045576e9d50014fe0dfe178a1f (patch)
tree247b65857229d1b67d403aaff6d20e86544dc3de
parenta423fa7b4683e6d8cf3abaaaf1567fbaccfb266b (diff)
parent0b7c867ec5e59bdae5e0646c99b369596a535909 (diff)
downloadrust-c4a4bce6950260045576e9d50014fe0dfe178a1f.tar.gz
rust-c4a4bce6950260045576e9d50014fe0dfe178a1f.zip
Rollup merge of #108218 - ChrisDenton:cmd-escape, r=cuviper
Windows: Quote more batch file arguments

Make sure to always quote batch file arguments that contain command prompt special characters.

Additionally add `/d` command line parameter to disable any autorun scripts that may change the way variable expansion works. This makes it more consistent across systems and may help avoid surprises.

## Background Info

[`CreateProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw) with the `lpApplicationName` set can only be used to run `.exe` files and not script files such as `.bat`. However, for historical reasons, we do have special handling so that `.bat` files will be correctly run with `cmd.exe` as the application.

In Windows, command line arguments are passed as a single string (not an array). Applications can parse this string however they like but most follow the standard MSVC C/C++ convention. But `cmd.exe` uses different argument parsing rules to other Windows programs (because it emulates old DOS).  This PR aims to help smooth over some of the differences.

r? libs
-rw-r--r--library/std/src/sys/windows/args.rs11
1 files changed, 10 insertions, 1 deletions
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 6741ae46d32..30356fa8519 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -270,7 +270,7 @@ pub(crate) fn make_bat_command_line(
     // It is necessary to surround the command in an extra pair of quotes,
     // hence the trailing quote here. It will be closed after all arguments
     // have been added.
-    let mut cmd: Vec<u16> = "cmd.exe /c \"".encode_utf16().collect();
+    let mut cmd: Vec<u16> = "cmd.exe /d /c \"".encode_utf16().collect();
 
     // Push the script name surrounded by its quote pair.
     cmd.push(b'"' as u16);
@@ -290,6 +290,15 @@ pub(crate) fn make_bat_command_line(
     // reconstructed by the batch script by default.
     for arg in args {
         cmd.push(' ' as u16);
+        // Make sure to always quote special command prompt characters, including:
+        // * Characters `cmd /?` says require quotes.
+        // * `%` for environment variables, as in `%TMP%`.
+        // * `|<>` pipe/redirect characters.
+        const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>";
+        let force_quotes = match arg {
+            Arg::Regular(arg) if !force_quotes => arg.bytes().iter().any(|c| SPECIAL.contains(c)),
+            _ => force_quotes,
+        };
         append_arg(&mut cmd, arg, force_quotes)?;
     }