From 41bf816fa6dd6fde0264db885c2f00757aba74b0 Mon Sep 17 00:00:00 2001 From: xEgoist Date: Tue, 2 May 2023 13:36:48 -0500 Subject: [PATCH 1/2] child_process: Add write access to the null handle This commit allows write access to the `\\Device\\Null` Handle. Without a write access, it's not possible for the child process to write SdOut to Null. As a requirement `SetHandleInformation` was also changed to mark the handle as iheritable (by adding it to Flags) by the spawned process. This allows the child to access the NUL device that was opened. This also makes the Windows part to behave similarly to `spawnPosix`. --- lib/std/child_process.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 444adebbdc..4be8b1cd83 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -661,8 +661,8 @@ pub const ChildProcess = struct { const nul_handle = if (any_ignore) // "\Device\Null" or "\??\NUL" windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{ - .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, - .share_access = windows.FILE_SHARE_READ, + .access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE | windows.SYNCHRONIZE, + .share_access = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE, .creation = windows.OPEN_EXISTING, .io_mode = .blocking, }) catch |err| switch (err) { @@ -681,7 +681,7 @@ pub const ChildProcess = struct { if (any_ignore) os.close(nul_handle); } if (any_ignore) { - try windows.SetHandleInformation(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); + try windows.SetHandleInformation(nul_handle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT); } var g_hChildStd_IN_Rd: ?windows.HANDLE = null; From 194ed308259e49af3f4725659e22ccd7457404e0 Mon Sep 17 00:00:00 2001 From: xEgoist Date: Thu, 4 May 2023 19:42:27 -0500 Subject: [PATCH 2/2] child_process: Use security attributes while creating handle. As suggested by @matu3ba, it can be better to use Security Attributes directly while creating the handle instead of creating the handle then setting the handle to inherit. Doing so can prevent potentially leaking to other parallel spawned processes which would inherit the opened `\Device\Null` handle. This change also allows windows.OpenFile to handle when bInheritHandle is set. Note that we are using the same `saAttr`, but since it's taken as a pointer to a const in all calls, it's never mutated, and OpenFile never alters it. This also saves 1 kernel call for setting the handle to inherit. --- lib/std/child_process.zig | 6 ++---- lib/std/os/windows.zig | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 4be8b1cd83..0a0c06ff89 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -650,7 +650,7 @@ pub const ChildProcess = struct { } fn spawnWindows(self: *ChildProcess) SpawnError!void { - const saAttr = windows.SECURITY_ATTRIBUTES{ + var saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, .lpSecurityDescriptor = null, @@ -663,6 +663,7 @@ pub const ChildProcess = struct { windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{ .access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE | windows.SYNCHRONIZE, .share_access = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE, + .sa = &saAttr, .creation = windows.OPEN_EXISTING, .io_mode = .blocking, }) catch |err| switch (err) { @@ -680,9 +681,6 @@ pub const ChildProcess = struct { defer { if (any_ignore) os.close(nul_handle); } - if (any_ignore) { - try windows.SetHandleInformation(nul_handle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT); - } var g_hChildStd_IN_Rd: ?windows.HANDLE = null; var g_hChildStd_IN_Wr: ?windows.HANDLE = null; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 451310c44d..974699a736 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -86,7 +86,10 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN var attr = OBJECT_ATTRIBUTES{ .Length = @sizeOf(OBJECT_ATTRIBUTES), .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir, - .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .Attributes = if (options.sa) |ptr| blk: { // Note we do not use OBJ_CASE_INSENSITIVE here. + const inherit: ULONG = if (ptr.bInheritHandle == TRUE) OBJ_INHERIT else 0; + break :blk inherit; + } else 0, .ObjectName = &nt_name, .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, .SecurityQualityOfService = null,