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 {...@@ -218,6 +218,8 @@ pub const VTable = struct {
218 childKill: *const fn (?*anyopaque, *std.process.Child) void,218 childKill: *const fn (?*anyopaque, *std.process.Child) void,
219219
220 progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,220 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
222 now: *const fn (?*anyopaque, Clock) Timestamp,224 now: *const fn (?*anyopaque, Clock) Timestamp,
223 clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration,225 clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration,
...@@ -2800,6 +2802,8 @@ pub const failing: std.Io = .{...@@ -2800,6 +2802,8 @@ pub const failing: std.Io = .{
2800 .childKill = unreachableChildKill,2802 .childKill = unreachableChildKill,
28012803
2802 .progressParentFile = failingProgressParentFile,2804 .progressParentFile = failingProgressParentFile,
2805 .inheritParentDir = unreachableInheritParentDir,
2806 .inheritParentFile = unreachableInheritParentFile,
28032807
2804 .random = noRandom,2808 .random = noRandom,
2805 .randomSecure = failingRandomSecure,2809 .randomSecure = failingRandomSecure,
...@@ -3470,6 +3474,19 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE...@@ -3470,6 +3474,19 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE
3470 return error.UnsupportedOperation;3474 return error.UnsupportedOperation;
3471}3475}
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
3473pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void {3490pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void {
3474 _ = userdata;3491 _ = userdata;
3475 @memset(buffer, 0);3492 @memset(buffer, 0);
lib/std/Io/Threaded.zig+40
...@@ -1906,6 +1906,8 @@ pub fn io(t: *Threaded) Io {...@@ -1906,6 +1906,8 @@ pub fn io(t: *Threaded) Io {
1906 .childKill = childKill,1906 .childKill = childKill,
19071907
1908 .progressParentFile = progressParentFile,1908 .progressParentFile = progressParentFile,
1909 .inheritParentDir = inheritParentDir,
1910 .inheritParentFile = inheritParentFile,
19091911
1910 .now = now,1912 .now = now,
1911 .clockResolution = clockResolution,1913 .clockResolution = clockResolution,
...@@ -17694,6 +17696,44 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {...@@ -17694,6 +17696,44 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
17694 return t.environ.zig_progress_file;17696 return t.environ.zig_progress_file;
17695}17697}
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
17697pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {17737pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
17698 t.scanEnviron();17738 t.scanEnviron();
17699 return @field(t.environ.string, name);17739 return @field(t.environ.string, name);
test/src/Runner.zig+19-5
...@@ -132,8 +132,15 @@ pub fn runServer(runner: *Runner) ProtocolError {...@@ -132,8 +132,15 @@ pub fn runServer(runner: *Runner) ProtocolError {
132 args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)],132 args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)],
133 );133 );
134 args_body_offset += @sizeOf(std.Io.Dir.Handle);134 args_body_offset += @sizeOf(std.Io.Dir.Handle);
135 const dir: std.Io.Dir = .{135 const dir = runner.io.vtable.inheritParentDir(
136 .handle = dir_handle.*,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 ),
137 };144 };
138 switch (state) {145 switch (state) {
139 .positional => {146 .positional => {
...@@ -171,9 +178,16 @@ pub fn runServer(runner: *Runner) ProtocolError {...@@ -171,9 +178,16 @@ pub fn runServer(runner: *Runner) ProtocolError {
171 args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)],178 args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)],
172 );179 );
173 args_body_offset += @sizeOf(std.Io.File.Handle);180 args_body_offset += @sizeOf(std.Io.File.Handle);
174 const file: std.Io.File = .{181 const file = runner.io.vtable.inheritParentFile(
175 .handle = file_handle.*,182 runner.io.userdata,
176 .flags = .{ .nonblocking = false },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 ),
177 };191 };
178 switch (state) {192 switch (state) {
179 .positional => {193 .positional => {