Zig Version
0.17.0-dev.305+bdfbf432d
Steps to Reproduce and Observed Behavior
This error can be reproduced by using std.testing.io.async in the function passed to std.testing.fuzz. You can follow these steps:
- Create the following
build.zig file:
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttest_exe=b.addTest(.{.root_module=b.createModule(.{.root_source_file=b.path("main.zig"),.target=b.graph.host,}),});construn_test_exe=b.addRunArtifact(test_exe);b.getInstallStep().dependOn(&run_test_exe.step);}
- Create the following
main.zig file:
conststd=@import("std");consttesting=std.testing;constSmith=testing.Smith;test{constContext=struct{fnjob()void{std.log.debug("running job",.{});}fntestOne(_:@This(),smith:*Smith)!void{_=smith;constio=testing.io;varfuture=io.async(job,.{});future.await(io);}};trytesting.fuzz(Context{},Context.testOne,.{});}
- Run
zig build --fuzz=1
- See the crash:
install
+- run test failure
Segmentation fault at address 0x0
/Users/user/.local/lib/zig/std/mem/Allocator.zig:142:26: 0x102838ad4 in rawAlloc (test)
return a.vtable.alloc(a.ptr, len, alignment, ret_addr);
^
/Users/user/.local/lib/zig/std/mem/Allocator.zig:286:40: 0x102845f9f in allocWithSizeAndAlignment__anon_9595 (test)
return self.allocBytesWithAlignment(alignment, byte_count, return_address);
^
/Users/user/.local/lib/zig/std/mem/Allocator.zig:274:89: 0x102845cf3 in allocAdvancedWithRetAddr (test)
const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));
^
/Users/user/.local/lib/zig/std/Io/Threaded.zig:676:73: 0x10292681b in create (test)
const future: *Future = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Future), alloc_len)));
^
/Users/user/.local/lib/zig/std/Io/Threaded.zig:2085:33: 0x10292626f in async (test)
const future = Future.create(gpa, result.len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
^
/Users/user/.local/lib/zig/std/Io.zig:2414:40: 0x10296ffef in async__anon_37378 (test)
future.any_future = io.vtable.async(
^
/Users/user/scratch/zig-io-fuzz-segfault/main.zig:18:34: 0x10296fc33 in testOne (test)
var future = io.async(job, .{});
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:560:20: 0x10296f923 in test_one (test)
testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
^
/Users/user/.local/lib/zig/fuzzer.zig:1356:32: 0x1029b235b in run (fuzzer)
const skip = f.test_one();
^
/Users/user/.local/lib/zig/fuzzer.zig:1526:24: 0x1029af927 in batch (fuzzer)
f.cycle();
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:592:35: 0x10296f7f3 in fuzz__anon_37325 (test)
fuzz_abi.fuzzer_start_test();
^
/Users/user/.local/lib/zig/std/testing.zig:1232:32: 0x10296f68f in fuzz (test)
return @import("root").fuzz(context, testOne, options);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:437:60: 0x10296bb87 in runner_test_run (test)
builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
^
/Users/user/.local/lib/zig/fuzzer.zig:2240:24: 0x1029b7f03 in runTest (fuzzer)
abi.runner_test_run(i);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:243:37: 0x1029491fb in mainServer (test)
fuzz_abi.fuzzer_main(n_tests, testing.random_seed, mode, amount_or_instance);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:71:26: 0x102949ebb in main (test)
return mainServer(init) catch |err| panic("internal test runner failure: {t}", .{err});
^
/Users/user/.local/lib/zig/std/start.zig:712:88: 0x102945e2f in callMain (test)
if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{
^
???:?:?: 0x18344ab97 in start (/usr/lib/dyld)
error: test 'main.test_0' terminated with signal ABRT; input saved to '.zig-cache/f/crash'
failed command: ./.zig-cache/o/b2a7265a57c540133711574618ef17a2/test --cache-dir=./.zig-cache --seed=0x6477dd32 --listen=-
======= FUZZING REPORT =======
Step: run test
Fuzz test: "main.test_0" (c107868dbee9c5e)
Runs: 0 -> 0
Unique runs: 0 -> 0
Coverage: 0/8755 -> 0/8755 (0.00%)
------------------------------
Values are accumulated across multiple runs when preserving the cache.
==============================
Expected Behavior
I would expect the task to run asynchronously without issue, or at the very least for the task to be run eagerly.
The above works when constructing an Io.Threaded inside the testOne function:
diff --git a/main.zig b/threaded_io.zig
index 5da8047..43d244d 100644
--- a/main.zig
+++ b/threaded_io.zig
@@ -1,6 +1,8 @@
const std = @import("std");
const testing = std.testing;
const Smith = testing.Smith;
+const Io = std.Io;
+const Threaded = Io.Threaded;
test {
const Context = struct {
@@ -11,7 +13,10 @@ test {
fn testOne(_: @This(), smith: *Smith) !void {
_ = smith;
- const io = testing.io;
+ var threaded = Threaded.init(testing.allocator, .{});
+ defer threaded.deinit();
+
+ const io = threaded.io();
var future = io.async(job, .{});
future.await(io);
If it's expected that using std.testing.io (or, more specifically, functions that are supposed to use its allocator) will cause fuzz tests to crash, I think this should be documented explicitly (if it is - I wasn't able to find it...)
### Zig Version
0.17.0-dev.305+bdfbf432d
### Steps to Reproduce and Observed Behavior
This error can be reproduced by using `std.testing.io.async` in the function passed to `std.testing.fuzz`. You can follow these steps:
1. Create the following `build.zig` file:
```zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const test_exe = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = b.graph.host,
}),
});
const run_test_exe = b.addRunArtifact(test_exe);
b.getInstallStep().dependOn(&run_test_exe.step);
}
```
2. Create the following `main.zig` file:
```zig
const std = @import("std");
const testing = std.testing;
const Smith = testing.Smith;
test {
const Context = struct {
fn job() void {
std.log.debug("running job", .{});
}
fn testOne(_: @This(), smith: *Smith) !void {
_ = smith;
const io = testing.io;
var future = io.async(job, .{});
future.await(io);
}
};
try testing.fuzz(Context{}, Context.testOne, .{});
}
```
3. Run `zig build --fuzz=1`
4. See the crash:
```
install
+- run test failure
Segmentation fault at address 0x0
/Users/user/.local/lib/zig/std/mem/Allocator.zig:142:26: 0x102838ad4 in rawAlloc (test)
return a.vtable.alloc(a.ptr, len, alignment, ret_addr);
^
/Users/user/.local/lib/zig/std/mem/Allocator.zig:286:40: 0x102845f9f in allocWithSizeAndAlignment__anon_9595 (test)
return self.allocBytesWithAlignment(alignment, byte_count, return_address);
^
/Users/user/.local/lib/zig/std/mem/Allocator.zig:274:89: 0x102845cf3 in allocAdvancedWithRetAddr (test)
const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));
^
/Users/user/.local/lib/zig/std/Io/Threaded.zig:676:73: 0x10292681b in create (test)
const future: *Future = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Future), alloc_len)));
^
/Users/user/.local/lib/zig/std/Io/Threaded.zig:2085:33: 0x10292626f in async (test)
const future = Future.create(gpa, result.len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
^
/Users/user/.local/lib/zig/std/Io.zig:2414:40: 0x10296ffef in async__anon_37378 (test)
future.any_future = io.vtable.async(
^
/Users/user/scratch/zig-io-fuzz-segfault/main.zig:18:34: 0x10296fc33 in testOne (test)
var future = io.async(job, .{});
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:560:20: 0x10296f923 in test_one (test)
testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
^
/Users/user/.local/lib/zig/fuzzer.zig:1356:32: 0x1029b235b in run (fuzzer)
const skip = f.test_one();
^
/Users/user/.local/lib/zig/fuzzer.zig:1526:24: 0x1029af927 in batch (fuzzer)
f.cycle();
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:592:35: 0x10296f7f3 in fuzz__anon_37325 (test)
fuzz_abi.fuzzer_start_test();
^
/Users/user/.local/lib/zig/std/testing.zig:1232:32: 0x10296f68f in fuzz (test)
return @import("root").fuzz(context, testOne, options);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:437:60: 0x10296bb87 in runner_test_run (test)
builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
^
/Users/user/.local/lib/zig/fuzzer.zig:2240:24: 0x1029b7f03 in runTest (fuzzer)
abi.runner_test_run(i);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:243:37: 0x1029491fb in mainServer (test)
fuzz_abi.fuzzer_main(n_tests, testing.random_seed, mode, amount_or_instance);
^
/Users/user/.local/lib/zig/compiler/test_runner.zig:71:26: 0x102949ebb in main (test)
return mainServer(init) catch |err| panic("internal test runner failure: {t}", .{err});
^
/Users/user/.local/lib/zig/std/start.zig:712:88: 0x102945e2f in callMain (test)
if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{
^
???:?:?: 0x18344ab97 in start (/usr/lib/dyld)
error: test 'main.test_0' terminated with signal ABRT; input saved to '.zig-cache/f/crash'
failed command: ./.zig-cache/o/b2a7265a57c540133711574618ef17a2/test --cache-dir=./.zig-cache --seed=0x6477dd32 --listen=-
======= FUZZING REPORT =======
Step: run test
Fuzz test: "main.test_0" (c107868dbee9c5e)
Runs: 0 -> 0
Unique runs: 0 -> 0
Coverage: 0/8755 -> 0/8755 (0.00%)
------------------------------
Values are accumulated across multiple runs when preserving the cache.
==============================
```
### Expected Behavior
I would expect the task to run asynchronously without issue, or at the very least for the task to be run eagerly.
The above works when constructing an `Io.Threaded` inside the `testOne` function:
```diff
diff --git a/main.zig b/threaded_io.zig
index 5da8047..43d244d 100644
--- a/main.zig
+++ b/threaded_io.zig
@@ -1,6 +1,8 @@
const std = @import("std");
const testing = std.testing;
const Smith = testing.Smith;
+const Io = std.Io;
+const Threaded = Io.Threaded;
test {
const Context = struct {
@@ -11,7 +13,10 @@ test {
fn testOne(_: @This(), smith: *Smith) !void {
_ = smith;
- const io = testing.io;
+ var threaded = Threaded.init(testing.allocator, .{});
+ defer threaded.deinit();
+
+ const io = threaded.io();
var future = io.async(job, .{});
future.await(io);
```
If it's expected that using `std.testing.io` (or, more specifically, functions that are supposed to use its allocator) will cause fuzz tests to crash, I think this should be documented explicitly (if it is - I wasn't able to find it...)