authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-25 10:00:39-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
logee0bd6c69ee01caf076e5c9f7461fe09fa8cf9ba
tree1594473f397d6ccee7c2628bdfd4fcc6c7bc8788
parent9fe0ebe1087ba824dac7a32042b476a9ce8d770e

Io: avoid leaking inherited descriptors


3 files changed, 76 insertions(+), 5 deletions(-)

lib/std/Io.zig+17
......@@ -218,6 +218,8 @@ pub const VTable = struct {
218218 childKill: *const fn (?*anyopaque, *std.process.Child) void,
219219
220220 progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,
221 inheritParentDir: *const fn (?*anyopaque, handle: Dir.Handle) (Cancelable || UnexpectedError)!Dir,
222 inheritParentFile: *const fn (?*anyopaque, handle: File.Handle, flags: File.Flags) (Cancelable || UnexpectedError)!File,
221223
222224 now: *const fn (?*anyopaque, Clock) Timestamp,
223225 clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration,
......@@ -2800,6 +2802,8 @@ pub const failing: std.Io = .{
28002802 .childKill = unreachableChildKill,
28012803
28022804 .progressParentFile = failingProgressParentFile,
2805 .inheritParentDir = unreachableInheritParentDir,
2806 .inheritParentFile = unreachableInheritParentFile,
28032807
28042808 .random = noRandom,
28052809 .randomSecure = failingRandomSecure,
......@@ -3470,6 +3474,19 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE
34703474 return error.UnsupportedOperation;
34713475}
34723476
3477pub fn unreachableInheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) (Cancelable || UnexpectedError)!Dir {
3478 _ = userdata;
3479 _ = handle;
3480 unreachable;
3481}
3482
3483pub fn unreachableInheritParentFile(userdata: ?*anyopaque, handle: File.Handle, flags: File.Flags) (Cancelable || UnexpectedError)!File {
3484 _ = userdata;
3485 _ = handle;
3486 _ = flags;
3487 unreachable;
3488}
3489
34733490pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void {
34743491 _ = userdata;
34753492 @memset(buffer, 0);
lib/std/Io/Threaded.zig+40
......@@ -1906,6 +1906,8 @@ pub fn io(t: *Threaded) Io {
19061906 .childKill = childKill,
19071907
19081908 .progressParentFile = progressParentFile,
1909 .inheritParentDir = inheritParentDir,
1910 .inheritParentFile = inheritParentFile,
19091911
19101912 .now = now,
19111913 .clockResolution = clockResolution,
......@@ -17694,6 +17696,44 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
1769417696 return t.environ.zig_progress_file;
1769517697}
1769617698
17699fn inheritParentHandle(handle: posix.fd_t) Io.InheritParentHandleError!void {
17700 switch (native_os) {
17701 .linux,
17702 .dragonfly,
17703 .freebsd,
17704 .netbsd,
17705 .openbsd,
17706 .illumos,
17707 .driverkit,
17708 .ios,
17709 .maccatalyst,
17710 .macos,
17711 .tvos,
17712 .visionos,
17713 .watchos,
17714 => return setCloexec(handle),
17715 else => return error.UnsupportedOperation,
17716 }
17717}
17718
17719fn inheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) Io.InheritParentHandleError!Dir {
17720 const t: *Threaded = @ptrCast(@alignCast(userdata));
17721 _ = t;
17722 try inheritParentHandle(handle);
17723 return .{ .handle = handle };
17724}
17725
17726fn inheritParentFile(
17727 userdata: ?*anyopaque,
17728 handle: File.Handle,
17729 flags: File.Flags,
17730) Io.InheritParentHandleError!File {
17731 const t: *Threaded = @ptrCast(@alignCast(userdata));
17732 _ = t;
17733 try inheritParentHandle(handle);
17734 return .{ .handle = handle, .flags = flags };
17735}
17736
1769717737pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
1769817738 t.scanEnviron();
1769917739 return @field(t.environ.string, name);
test/src/Runner.zig+19-5
......@@ -132,8 +132,15 @@ pub fn runServer(runner: *Runner) ProtocolError {
132132 args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)],
133133 );
134134 args_body_offset += @sizeOf(std.Io.Dir.Handle);
135 const dir: std.Io.Dir = .{
136 .handle = dir_handle.*,
135 const dir = runner.io.vtable.inheritParentDir(
136 runner.io.userdata,
137 dir_handle.*,
138 ) catch |err| switch (err) {
139 error.Canceled => |e| return e,
140 error.Unexpected => |e| return runner.fail(
141 "unable to inherit parent dir: {t}",
142 .{e},
143 ),
137144 };
138145 switch (state) {
139146 .positional => {
......@@ -171,9 +178,16 @@ pub fn runServer(runner: *Runner) ProtocolError {
171178 args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)],
172179 );
173180 args_body_offset += @sizeOf(std.Io.File.Handle);
174 const file: std.Io.File = .{
175 .handle = file_handle.*,
176 .flags = .{ .nonblocking = false },
181 const file = runner.io.vtable.inheritParentFile(
182 runner.io.userdata,
183 file_handle.*,
184 .{ .nonblocking = false },
185 ) catch |err| switch (err) {
186 error.Canceled => |e| return e,
187 error.Unexpected => |e| return runner.fail(
188 "unable to inherit parent file: {t}",
189 .{e},
190 ),
177191 };
178192 switch (state) {
179193 .positional => {