Commit afa66f6111
lib/std/process/Child.zig
@@ -901,11 +901,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep);
try dir_buf.appendSlice(self.allocator, app_dir);
}
- if (dir_buf.items.len > 0) {
- // Need to normalize the path, openDirW can't handle things like double backslashes
- const normalized_len = windows.normalizePath(u16, dir_buf.items) catch return error.BadPathName;
- dir_buf.shrinkRetainingCapacity(normalized_len);
- }
windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| {
const original_err = switch (no_path_err) {
@@ -930,10 +925,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
while (it.next()) |search_path| {
dir_buf.clearRetainingCapacity();
try dir_buf.appendSlice(self.allocator, search_path);
- // Need to normalize the path, some PATH values can contain things like double
- // backslashes which openDirW can't handle
- const normalized_len = windows.normalizePath(u16, dir_buf.items) catch continue;
- dir_buf.shrinkRetainingCapacity(normalized_len);
if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) {
break :run;
test/standalone/windows_spawn/main.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+
const windows = std.os.windows;
const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
@@ -39,6 +40,9 @@ pub fn main() anyerror!void {
// No PATH, so it should fail to find anything not in the cwd
try testExecError(error.FileNotFound, allocator, "something_missing");
+ // make sure we don't get error.BadPath traversing out of cwd with a relative path
+ try testExecError(error.FileNotFound, allocator, "..\\.\\.\\.\\\\..\\more_missing");
+
std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
utf16Literal("PATH"),
tmp_absolute_path_w,
@@ -149,6 +153,48 @@ pub fn main() anyerror!void {
// If we try to exec but provide a cwd that is an absolute path, the PATH
// should still be searched and the goodbye.exe in something should be found.
try testExecWithCwd(allocator, "goodbye", tmp_absolute_path, "hello from exe\n");
+
+ // introduce some extra path separators into the path which is dealt with inside the spawn call.
+ const denormed_something_subdir_size = std.mem.replacementSize(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"));
+
+ const denormed_something_subdir_abs_path = try allocator.allocSentinel(u16, denormed_something_subdir_size, 0);
+ defer allocator.free(denormed_something_subdir_abs_path);
+
+ _ = std.mem.replace(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"), denormed_something_subdir_abs_path);
+
+ const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, denormed_something_subdir_abs_path);
+ defer allocator.free(denormed_something_subdir_wtf8);
+
+ // clear the path to ensure that the match comes from the cwd
+ std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
+ utf16Literal("PATH"),
+ null,
+ ) == windows.TRUE);
+
+ try testExecWithCwd(allocator, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n");
+
+ // normalization should also work if the non-normalized path is found in the PATH var.
+ std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
+ utf16Literal("PATH"),
+ denormed_something_subdir_abs_path,
+ ) == windows.TRUE);
+ try testExec(allocator, "goodbye", "hello from exe\n");
+
+ // now make sure we can launch executables "outside" of the cwd
+ var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{});
+ defer subdir_cwd.close();
+
+ try tmp.dir.rename("something/goodbye.exe", "hello.exe");
+ try subdir_cwd.setAsCwd();
+
+ // clear the PATH again
+ std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
+ utf16Literal("PATH"),
+ null,
+ ) == windows.TRUE);
+
+ // while we're at it make sure non-windows separators work fine
+ try testExec(allocator, "../hello", "hello from exe\n");
}
fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void {