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:
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);
},