authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-22 15:47:44+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-24 20:42:39+01:00
logff85396f7a85750cb703460b5b57b9860088c5ef
tree516071459a89cd58730d30e22f1e317863b52134
parent857d8ac650b3154bfd320911f8d8fd0bd7ed9ea7
signaturelock-open Commit is signed but in an unrecognized format.

test runner: initialize `std.testing.io_instance` in fuzz tests

Because this was left at `undefined`, fuzz tests were exhibiting Illegal Behavior when they used `std.testing.io`. This wasn't noticed sooner probably because an all-zeroes `std.Io.Threaded` happens to be fairly functional, so things would broadly work if the optimizer didn't catch the IB.

1 files changed, 16 insertions(+), 7 deletions(-)

lib/compiler/test_runner.zig+16-7
......@@ -185,7 +185,6 @@ fn mainServer(init: std.process.Init.Minimal) !void {
185185 .environ = init.environ,
186186 });
187187 defer io_instance.deinit();
188 const io = io_instance.io();
189188
190189 const mode: fuzz_abi.LimitKind = @fromBackingInt(@intCast(try server.receiveBody_u8()));
191190 const amount_or_instance = try server.receiveBody_u64();
......@@ -208,7 +207,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {
208207 .indexes = test_indexes,
209208 .server = &server,
210209 .gpa = gpa,
211 .io = io,
210 .threaded_io = &io_instance,
212211 .input_poller = undefined,
213212 };
214213
......@@ -422,7 +421,7 @@ var fuzz_runner: if (builtin.fuzz) struct {
422421 indexes: []u32,
423422 server: *std.zig.Server,
424423 gpa: std.mem.Allocator,
425 io: Io,
424 threaded_io: *Io.Threaded,
426425 input_poller: Io.Future(Io.Cancelable!void),
427426
428427 comptime {
......@@ -443,6 +442,12 @@ var fuzz_runner: if (builtin.fuzz) struct {
443442 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
444443 is_fuzz_test = false;
445444
445 testing.io_instance = .init(testing.allocator, .{
446 .argv0 = fuzz_runner.threaded_io.argv0,
447 .environ = fuzz_runner.threaded_io.environ.process_environ,
448 });
449 defer testing.io_instance.deinit();
450
446451 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
447452 error.SkipZigTest => return,
448453 else => {
......@@ -473,7 +478,8 @@ var fuzz_runner: if (builtin.fuzz) struct {
473478
474479 export fn runner_start_input_poller() void {
475480 @disableInstrumentation();
476 const future = fuzz_runner.io.concurrent(inputPoller, .{}) catch |e| switch (e) {
481 const io = fuzz_runner.threaded_io.io();
482 const future = io.concurrent(inputPoller, .{}) catch |e| switch (e) {
477483 error.ConcurrencyUnavailable => @panic("failed to spawn concurrent fuzz input poller"),
478484 };
479485 fuzz_runner.input_poller = future;
......@@ -481,17 +487,20 @@ var fuzz_runner: if (builtin.fuzz) struct {
481487
482488 export fn runner_stop_input_poller() void {
483489 @disableInstrumentation();
484 assert(fuzz_runner.input_poller.cancel(fuzz_runner.io) == error.Canceled);
490 const io = fuzz_runner.threaded_io.io();
491 assert(fuzz_runner.input_poller.cancel(io) == error.Canceled);
485492 }
486493
487494 export fn runner_futex_wait(ptr: *const u32, expected: u32) bool {
488495 @disableInstrumentation();
489 return fuzz_runner.io.futexWait(u32, ptr, expected) == error.Canceled;
496 const io = fuzz_runner.threaded_io.io();
497 return io.futexWait(u32, ptr, expected) == error.Canceled;
490498 }
491499
492500 export fn runner_futex_wake(ptr: *const u32, waiters: u32) void {
493501 @disableInstrumentation();
494 fuzz_runner.io.futexWake(u32, ptr, waiters);
502 const io = fuzz_runner.threaded_io.io();
503 io.futexWake(u32, ptr, waiters);
495504 }
496505
497506 fn inputPoller() Io.Cancelable!void {