zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit d6f43caadf30dce2ad6f8fa938482855e08e6700 (tree)
parent 8111d3d63cf3662b5aecbd0ddd10567e025694a7
Author: Alex Rønne Petersen <alex@alexrp.com>
Date:   Sat, 11 Apr 2026 00:59:11 +0200

Merge pull request 'audit: handle process.Child.Term exhaustively and give useful exit information on process exit' (#31018) from murtaza/zig:child.term-audit into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31018
Reviewed-by: Alex Rønne Petersen <alex@alexrp.com>

Diffstat:
Mlib/compiler/reduce.zig | 6+++++-
Mlib/compiler/std-docs.zig | 9++++++++-
Mlib/std/Build.zig | 4++--
Mlib/std/Build/Step.zig | 19++++++-------------
Mlib/std/Build/Step/Run.zig | 6+++---
Mlib/std/Build/WebServer.zig | 9++++++++-
Mlib/std/Io/Threaded.zig | 2+-
Mlib/std/Io/Uring.zig | 2+-
Mlib/std/c.zig | 34+++++++++++++++++-----------------
Mlib/std/os/emscripten.zig | 2+-
Mlib/std/os/linux.zig | 4++--
Mlib/std/process/Child.zig | 2+-
Mlib/std/zig/system.zig | 22++++++++++++++++++----
Msrc/Compilation.zig | 12++++++++++--
Msrc/link/Lld.zig | 2+-
Msrc/main.zig | 18+++++++++++++++---
Mtools/doctest.zig | 7++++++-
Mtools/incr-check.zig | 30++++++++++++++++--------------
Mtools/update_clang_options.zig | 12++++++++++--
Mtools/update_cpu_features.zig | 10+++++++++-
20 files changed, 140 insertions(+), 72 deletions(-)

diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig @@ -291,7 +291,11 @@ fn termToInteresting(term: std.process.Child.Term) Interestingness { std.debug.print("interestingness check terminated with signal {t}\n", .{sig}); return .boring; }, - else => { + .stopped => |sig| { + std.debug.print("interestingness check stopped with signal {t}\n", .{sig}); + return .boring; + }, + .unknown => { std.debug.print("interestingness check aborted unexpectedly\n", .{}); return .boring; }, diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig @@ -423,7 +423,14 @@ fn buildWasmBinary( ); return error.WasmCompilationFailed; }, - .stopped, .unknown => { + .stopped => |sig| { + std.log.err( + "the following command stopped unexpectedly with signal {t}:\n{s}", + .{ sig, try std.Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) }, + ); + return error.WasmCompilationFailed; + }, + .unknown => { std.log.err( "the following command terminated unexpectedly:\n{s}", .{try std.Build.Step.allocPrintCmd(arena, .inherit, null, argv.items)}, diff --git a/lib/std/Build.zig b/lib/std/Build.zig @@ -1899,11 +1899,11 @@ pub fn runAllowFail( } return stdout; }, - .signal => |sig| { + .signal, .stopped => |sig| { out_code.* = @as(u8, @truncate(@intFromEnum(sig))); return error.ProcessTerminated; }, - .stopped, .unknown => |code| { + .unknown => |code| { out_code.* = @as(u8, @truncate(code)); return error.ProcessTerminated; }, diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig @@ -725,19 +725,12 @@ pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFaile /// Asserts that the caller has already populated `s.result_failed_command`. pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void { assert(s.result_failed_command != null); - switch (term) { - .exited => |code| { - if (code != 0) { - return s.fail("process exited with error code {d}", .{code}); - } - }, - .signal => |sig| { - return s.fail("process terminated with signal {t}", .{sig}); - }, - .stopped, .unknown => { - return s.fail("process terminated unexpectedly", .{}); - }, - } + return switch (term) { + .exited => |code| if (code != 0) s.fail("process exited with error code {d}", .{code}), + .signal => |sig| s.fail("process terminated with signal {t}", .{sig}), + .stopped => |sig| s.fail("process stopped with signal {t}", .{sig}), + .unknown => s.fail("process terminated unexpectedly", .{}), + }; } pub fn allocPrintCmd( diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig @@ -1173,7 +1173,7 @@ fn formatTerm(term: ?process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error! if (term) |t| switch (t) { .exited => |code| try w.print("exited with code {d}", .{code}), .signal => |sig| try w.print("terminated with signal {t}", .{sig}), - .stopped => |sig| try w.print("stopped with signal {d}", .{sig}), + .stopped => |sig| try w.print("stopped with signal {t}", .{sig}), .unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}), } else { try w.writeAll("exited with any code"); @@ -2804,8 +2804,8 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { .expect_term => |term| { hh.add(@as(std.meta.Tag(process.Child.Term), term)); switch (term) { - inline .exited, .signal => |x| hh.add(x), - .stopped, .unknown => |x| hh.add(x), + inline .exited, .signal, .stopped => |x| hh.add(x), + .unknown => |x| hh.add(x), } }, } diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig @@ -681,7 +681,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim ); return error.WasmCompilationFailed; }, - .stopped, .unknown => { + .stopped => |sig| { + log.err( + "the following command stopped unexpectedly with signal {t}:\n{s}", + .{ sig, try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) }, + ); + return error.WasmCompilationFailed; + }, + .unknown => { log.err( "the following command terminated unexpectedly:\n{s}", .{try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items)}, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig @@ -15281,7 +15281,7 @@ fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.T return switch (code) { .EXITED => .{ .exited = @truncate(status) }, .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) }, - .TRAPPED, .STOPPED => .{ .stopped = status }, + .TRAPPED, .STOPPED => .{ .stopped = @enumFromInt(status) }, _, .CONTINUED => .{ .unknown = status }, }; }, diff --git a/lib/std/Io/Uring.zig b/lib/std/Io/Uring.zig @@ -4729,7 +4729,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr return switch (code) { .EXITED => .{ .exited = @truncate(status) }, .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) }, - .TRAPPED, .STOPPED => .{ .stopped = status }, + .TRAPPED, .STOPPED => .{ .stopped = @enumFromInt(status) }, _, .CONTINUED => .{ .unknown = status }, }; }, diff --git a/lib/std/c.zig b/lib/std/c.zig @@ -3721,14 +3721,14 @@ pub const W = switch (native_os) { pub fn TERMSIG(x: u32) SIG { return @enumFromInt(status(x)); } - pub fn STOPSIG(x: u32) u32 { - return x >> 8; + pub fn STOPSIG(x: u32) SIG { + return @enumFromInt(x >> 8); } pub fn IFEXITED(x: u32) bool { return status(x) == 0; } pub fn IFSTOPPED(x: u32) bool { - return status(x) == stopped and STOPSIG(x) != 0x13; + return status(x) == stopped and @as(u32, @intFromEnum(STOPSIG(x))) != 0x13; } pub fn IFSIGNALED(x: u32) bool { return status(x) != stopped and status(x) != 0; @@ -3754,8 +3754,8 @@ pub const W = switch (native_os) { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; @@ -3782,8 +3782,8 @@ pub const W = switch (native_os) { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; @@ -3816,8 +3816,8 @@ pub const W = switch (native_os) { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; @@ -3850,8 +3850,8 @@ pub const W = switch (native_os) { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; @@ -3879,8 +3879,8 @@ pub const W = switch (native_os) { return @enumFromInt((s >> 8) & 0xff); } - pub fn STOPSIG(s: u32) u32 { - return (s >> 16) & 0xff; + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt((s >> 16) & 0xff); } pub fn IFEXITED(s: u32) bool { @@ -3906,8 +3906,8 @@ pub const W = switch (native_os) { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; @@ -3938,8 +3938,8 @@ pub const W = switch (native_os) { return @intCast((s & 0xff00) >> 8); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn TERMSIG(s: u32) SIG { diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig @@ -212,7 +212,7 @@ pub const W = struct { return @enumFromInt(s & 0x7f); } pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig @@ -3883,8 +3883,8 @@ pub const W = struct { pub fn TERMSIG(s: u32) SIG { return @enumFromInt(s & 0x7f); } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); + pub fn STOPSIG(s: u32) SIG { + return @enumFromInt(EXITSTATUS(s)); } pub fn IFEXITED(s: u32) bool { return (s & 0x7f) == 0; diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig @@ -94,7 +94,7 @@ pub const ResourceUsageStatistics = struct { pub const Term = union(enum) { exited: u8, signal: std.posix.SIG, - stopped: u32, + stopped: std.posix.SIG, unknown: u32, }; diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig @@ -1185,11 +1185,25 @@ fn detectAndroidApiLevel(io: Io) !u32 { return error.ApiLevelQueryFailed; }; - const term = try child.wait(io); - if (term != .exited or term.exited != 0) { - std.log.err("getprop terminated abnormally: {}", .{term}); - return error.ApiLevelQueryFailed; + switch (try child.wait(io)) { + .exited => |code| if (code != 0) { + std.log.err("getprop terminated abnormally with exit code: {d}", .{code}); + return error.ApiLevelQueryFailed; + }, + .signal => |sig| { + std.log.err("getprop terminated abnormally with signal: {t}", .{sig}); + return error.ApiLevelQueryFailed; + }, + .stopped => |sig| { + std.log.err("getprop stopped abnormally with signal: {t}", .{sig}); + return error.ApiLevelQueryFailed; + }, + .unknown => { + std.log.err("getprop terminated abnormally", .{}); + return error.ApiLevelQueryFailed; + }, } + return api_level; } diff --git a/src/Compilation.zig b/src/Compilation.zig @@ -5870,7 +5870,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr log.err("clang failed with stderr: {s}", .{stderr}); return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); }, - else => { + .stopped => |sig| { + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig}); + }, + .unknown => { log.err("clang terminated with stderr: {s}", .{stderr}); return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); }, @@ -6297,7 +6301,11 @@ fn spawnZigRc( log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr }); return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); }, - else => { + .stopped => |sig| { + log.err("zig rc stopped {t} with stderr:\n{s}", .{ sig, stderr }); + return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); + }, + .unknown => { log.err("zig rc terminated with stderr:\n{s}", .{stderr}); return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); }, diff --git a/src/link/Lld.zig b/src/link/Lld.zig @@ -1733,7 +1733,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi }, .stopped => |sig| { if (comp.clang_passthrough_mode) std.process.abort(); - return diags.fail("{s} stopped with signal {d} and stderr:\n{s}", .{ argv[0], sig, stderr }); + return diags.fail("{s} stopped with signal {t} and stderr:\n{s}", .{ argv[0], sig, stderr }); }, .unknown => |code| { if (comp.clang_passthrough_mode) std.process.abort(); diff --git a/src/main.zig b/src/main.zig @@ -4580,7 +4580,11 @@ fn runOrTest( const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command terminated with signal {t}:\n{s}", .{ sig, cmd }); }, - else => { + .stopped => |sig| { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following command stopped with signal {t}:\n{s}", .{ sig, cmd }); + }, + .unknown => { process.exit(1); }, } @@ -5630,7 +5634,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); }, - else => { + .stopped => |sig| { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd }); + }, + .unknown => { const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command crashed:\n{s}", .{cmd}); }, @@ -5932,7 +5940,11 @@ fn jitCmdInner( const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); }, - else => { + .stopped => |sig| { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command stopped with signal {t}:\n{s}", .{ sig, cmd }); + }, + .unknown => { const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command crashed:\n{s}", .{cmd}); }, diff --git a/tools/doctest.zig b/tools/doctest.zig @@ -1141,7 +1141,12 @@ fn run( dumpArgs(args); return error.ChildCrashed; }, - else => { + .stopped => |sig| { + std.debug.print("{s}\nThe following command stopped with signal {t}:\n", .{ result.stderr, sig }); + dumpArgs(args); + return error.ChildCrashed; + }, + .unknown => { std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr}); dumpArgs(args); return error.ChildCrashed; diff --git a/tools/incr-check.zig b/tools/incr-check.zig @@ -568,7 +568,10 @@ const Eval = struct { .signal => |sig| { eval.fatal("generated executable '{s}' terminated with signal {t}", .{ binary_path, sig }); }, - .stopped, .unknown => { + .stopped => |sig| { + eval.fatal("generated executable '{s}' stopped with signal {t}", .{ binary_path, sig }); + }, + .unknown => { eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path}); }, } @@ -627,19 +630,17 @@ const Eval = struct { }) catch |err| { eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err }); }; + + if (result.term == .exited and result.term.exited == 0) return; + + if (result.stderr.len != 0) { + std.log.err("zig cc stderr:\n{s}", .{result.stderr}); + } switch (result.term) { - .exited => |code| if (code != 0) { - if (result.stderr.len != 0) { - std.log.err("zig cc stderr:\n{s}", .{result.stderr}); - } - eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }); - }, - .signal, .stopped, .unknown => { - if (result.stderr.len != 0) { - std.log.err("zig cc stderr:\n{s}", .{result.stderr}); - } - eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path}); - }, + .exited => |code| eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }), + .signal => |sig| eval.fatal("zig cc for '{s}' terminated unexpectedly with signal {t}", .{ c_path, sig }), + .stopped => |sig| eval.fatal("zig cc for '{s}' stopped unexpectedly with signal {t}", .{ c_path, sig }), + .unknown => eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path}), } } @@ -918,7 +919,8 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void { switch (term) { .exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}), .signal => |sig| eval.fatal("compiler terminated with signal {t}", .{sig}), - .stopped, .unknown => eval.fatal("compiler terminated unexpectedly", .{}), + .stopped => |sig| eval.fatal("compiler stopped unexpectedly with signal {t}", .{sig}), + .unknown => eval.fatal("compiler terminated unexpectedly", .{}), } } diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig @@ -688,9 +688,17 @@ pub fn main(init: std.process.Init) !void { const json_text = switch (child_result.term) { .exited => |code| if (code == 0) child_result.stdout else { - fatal("llvm-tblgen exited with code {d}", .{code}); + fatal("llvm-tblgen exited with code {d}\n", .{code}); + }, + .signal => |sig| { + fatal("llvm-tblgen terminated with signal {t}\n", .{sig}); + }, + .stopped => |sig| { + fatal("llvm-tblgen stopped with signal {d}\n", .{sig}); + }, + .unknown => { + fatal("llvm-tblgen crashed\n", .{}); }, - else => fatal("llvm-tblgen crashed", .{}), }; const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{}); diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig @@ -2012,7 +2012,15 @@ fn processOneTarget(io: Io, job: Job) void { std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); std.process.exit(1); }, - else => { + .signal => |sig| { + std.debug.print("llvm-tblgen terminated with signal {t}\n", .{sig}); + std.process.exit(1); + }, + .stopped => |sig| { + std.debug.print("llvm-tblgen stopped with signal {t}\n", .{sig}); + std.process.exit(1); + }, + .unknown => { std.debug.print("llvm-tblgen crashed\n", .{}); std.process.exit(1); },