| author | |
| committer | |
| log | 9bb0b43ea3ababb715a15bba8c09ba71e9c3ccc2 |
| tree | d2f43d4ac88949889e5971327426fb099e576941 |
| parent | 0feacc2b81679514c0168a6ba4c0decafeb2e43e |
5 files changed, 56 insertions(+), 78 deletions(-)
lib/compiler/build_runner.zig+10-6| ... | ... | @@ -280,21 +280,21 @@ pub fn main() !void { |
| 280 | 280 | } |
| 281 | 281 | } else if (mem.startsWith(u8, arg, "--fuzz=")) { |
| 282 | 282 | const value = arg["--fuzz=".len..]; |
| 283 | if (value.len == 0) fatal("missing argument to --fuzz\n", .{}); | |
| 283 | if (value.len == 0) fatal("missing argument to --fuzz", .{}); | |
| 284 | 284 | |
| 285 | 285 | const unit: u8 = value[value.len - 1]; |
| 286 | const digits = switch (value[value.len - 1]) { | |
| 286 | const digits = switch (unit) { | |
| 287 | 287 | '0'...'9' => value, |
| 288 | 288 | 'K', 'M', 'G' => value[0 .. value.len - 1], |
| 289 | 289 | else => fatal( |
| 290 | "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n", | |
| 290 | "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]", | |
| 291 | 291 | .{}, |
| 292 | 292 | ), |
| 293 | 293 | }; |
| 294 | 294 | |
| 295 | 295 | const amount = std.fmt.parseInt(u64, digits, 10) catch { |
| 296 | 296 | fatal( |
| 297 | "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]\n", | |
| 297 | "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]", | |
| 298 | 298 | .{}, |
| 299 | 299 | ); |
| 300 | 300 | }; |
| ... | ... | @@ -305,7 +305,7 @@ pub fn main() !void { |
| 305 | 305 | 'K' => 1000, |
| 306 | 306 | 'M' => 1_000_000, |
| 307 | 307 | 'G' => 1_000_000_000, |
| 308 | }) catch fatal("fuzzing limit amount overflows u64\n", .{}); | |
| 308 | }) catch fatal("fuzzing limit amount overflows u64", .{}); | |
| 309 | 309 | |
| 310 | 310 | fuzz = .{ |
| 311 | 311 | .limit = .{ |
| ... | ... | @@ -520,7 +520,11 @@ pub fn main() !void { |
| 520 | 520 | }; |
| 521 | 521 | |
| 522 | 522 | if (run.web_server) |*web_server| { |
| 523 | if (fuzz) |mode| assert(mode == .forever); | |
| 523 | if (fuzz) |mode| if (mode != .forever) fatal( | |
| 524 | "error: limited fuzzing is not implemented yet for --webui", | |
| 525 | .{}, | |
| 526 | ); | |
| 527 | ||
| 524 | 528 | web_server.finishBuild(.{ .fuzz = fuzz != null }); |
| 525 | 529 | } |
| 526 | 530 |
lib/compiler/test_runner.zig+17-68| ... | ... | @@ -56,20 +56,21 @@ pub fn main() void { |
| 56 | 56 | } |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | fba.reset(); | |
| 60 | 59 | if (builtin.fuzz) { |
| 61 | 60 | const cache_dir = opt_cache_dir orelse @panic("missing --cache-dir=[path] argument"); |
| 62 | 61 | fuzz_abi.fuzzer_init(.fromSlice(cache_dir)); |
| 63 | 62 | } |
| 64 | 63 | |
| 64 | fba.reset(); | |
| 65 | ||
| 65 | 66 | if (listen) { |
| 66 | return mainServer(opt_cache_dir) catch @panic("internal test runner failure"); | |
| 67 | return mainServer() catch @panic("internal test runner failure"); | |
| 67 | 68 | } else { |
| 68 | 69 | return mainTerminal(); |
| 69 | 70 | } |
| 70 | 71 | } |
| 71 | 72 | |
| 72 | fn mainServer(opt_cache_dir: ?[]const u8) !void { | |
| 73 | fn mainServer() !void { | |
| 73 | 74 | @disableInstrumentation(); |
| 74 | 75 | var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer); |
| 75 | 76 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); |
| ... | ... | @@ -79,66 +80,14 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void { |
| 79 | 80 | .zig_version = builtin.zig_version_string, |
| 80 | 81 | }); |
| 81 | 82 | |
| 82 | if (builtin.fuzz) blk: { | |
| 83 | const cache_dir = opt_cache_dir.?; | |
| 84 | const coverage_id = fuzz_abi.fuzzer_coverage_id(); | |
| 85 | const coverage_file_path: std.Build.Cache.Path = .{ | |
| 86 | .root_dir = .{ | |
| 87 | .path = cache_dir, | |
| 88 | .handle = std.fs.cwd().openDir(cache_dir, .{}) catch |err| { | |
| 89 | if (err == error.FileNotFound) { | |
| 90 | try server.serveCoverageIdMessage(coverage_id, 0, 0, 0); | |
| 91 | break :blk; | |
| 92 | } | |
| 93 | ||
| 94 | fatal("failed to access cache dir '{s}': {s}", .{ | |
| 95 | cache_dir, @errorName(err), | |
| 96 | }); | |
| 97 | }, | |
| 98 | }, | |
| 99 | .sub_path = "v/" ++ std.fmt.hex(coverage_id), | |
| 100 | }; | |
| 101 | ||
| 102 | var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| { | |
| 103 | if (err == error.FileNotFound) { | |
| 104 | try server.serveCoverageIdMessage(coverage_id, 0, 0, 0); | |
| 105 | break :blk; | |
| 106 | } | |
| 107 | ||
| 108 | fatal("failed to load coverage file '{f}': {s}", .{ | |
| 109 | coverage_file_path, @errorName(err), | |
| 110 | }); | |
| 111 | }; | |
| 112 | defer coverage_file.close(); | |
| 113 | ||
| 114 | var rbuf: [0x1000]u8 = undefined; | |
| 115 | var r = coverage_file.reader(&rbuf); | |
| 116 | ||
| 117 | var header: fuzz_abi.SeenPcsHeader = undefined; | |
| 118 | r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| { | |
| 119 | fatal("failed to read from coverage file '{f}': {s}", .{ | |
| 120 | coverage_file_path, @errorName(err), | |
| 121 | }); | |
| 122 | }; | |
| 123 | ||
| 124 | if (header.pcs_len == 0) { | |
| 125 | fatal("corrupted coverage file '{f}': pcs_len was zero", .{ | |
| 126 | coverage_file_path, | |
| 127 | }); | |
| 128 | } | |
| 129 | ||
| 130 | var seen_count: usize = 0; | |
| 131 | const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len); | |
| 132 | for (0..chunk_count) |_| { | |
| 133 | const seen = r.interface.takeInt(usize, .little) catch |err| { | |
| 134 | fatal("failed to read from coverage file '{f}': {s}", .{ | |
| 135 | coverage_file_path, @errorName(err), | |
| 136 | }); | |
| 137 | }; | |
| 138 | seen_count += @popCount(seen); | |
| 139 | } | |
| 140 | ||
| 141 | try server.serveCoverageIdMessage(coverage_id, header.n_runs, header.unique_runs, seen_count); | |
| 83 | if (builtin.fuzz) { | |
| 84 | const coverage = fuzz_abi.fuzzer_coverage(); | |
| 85 | try server.serveCoverageIdMessage( | |
| 86 | coverage.id, | |
| 87 | coverage.runs, | |
| 88 | coverage.unique, | |
| 89 | coverage.seen, | |
| 90 | ); | |
| 142 | 91 | } |
| 143 | 92 | |
| 144 | 93 | while (true) { |
| ... | ... | @@ -235,7 +184,7 @@ fn mainServer(opt_cache_dir: ?[]const u8) !void { |
| 235 | 184 | if (@errorReturnTrace()) |trace| { |
| 236 | 185 | std.debug.dumpStackTrace(trace.*); |
| 237 | 186 | } |
| 238 | std.debug.print("failed with error.{s}\n", .{@errorName(err)}); | |
| 187 | std.debug.print("failed with error.{t}\n", .{err}); | |
| 239 | 188 | std.process.exit(1); |
| 240 | 189 | }, |
| 241 | 190 | }; |
| ... | ... | @@ -305,11 +254,11 @@ fn mainTerminal() void { |
| 305 | 254 | else => { |
| 306 | 255 | fail_count += 1; |
| 307 | 256 | if (have_tty) { |
| 308 | std.debug.print("{d}/{d} {s}...FAIL ({s})\n", .{ | |
| 309 | i + 1, test_fn_list.len, test_fn.name, @errorName(err), | |
| 257 | std.debug.print("{d}/{d} {s}...FAIL ({t})\n", .{ | |
| 258 | i + 1, test_fn_list.len, test_fn.name, err, | |
| 310 | 259 | }); |
| 311 | 260 | } else { |
| 312 | std.debug.print("FAIL ({s})\n", .{@errorName(err)}); | |
| 261 | std.debug.print("FAIL ({t})\n", .{err}); | |
| 313 | 262 | } |
| 314 | 263 | if (@errorReturnTrace()) |trace| { |
| 315 | 264 | std.debug.dumpStackTrace(trace.*); |
| ... | ... | @@ -450,7 +399,7 @@ pub fn fuzz( |
| 450 | 399 | else => { |
| 451 | 400 | std.debug.lockStdErr(); |
| 452 | 401 | if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace.*); |
| 453 | std.debug.print("failed with error.{s}\n", .{@errorName(err)}); | |
| 402 | std.debug.print("failed with error.{t}\n", .{err}); | |
| 454 | 403 | std.process.exit(1); |
| 455 | 404 | }, |
| 456 | 405 | }; |
lib/fuzzer.zig+17-2| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | const fatal = std.process.fatal; | |
| 3 | 4 | const mem = std.mem; |
| 4 | 5 | const math = std.math; |
| 5 | 6 | const Allocator = mem.Allocator; |
| ... | ... | @@ -105,6 +106,7 @@ const Executable = struct { |
| 105 | 106 | const coverage_file_len = @sizeOf(abi.SeenPcsHeader) + |
| 106 | 107 | pc_bitset_usizes * @sizeOf(usize) + |
| 107 | 108 | pcs.len * @sizeOf(usize); |
| 109 | ||
| 108 | 110 | if (populate) { |
| 109 | 111 | defer coverage_file.lock(.shared) catch |e| panic( |
| 110 | 112 | "failed to demote lock for coverage file '{s}': {t}", |
| ... | ... | @@ -581,8 +583,21 @@ export fn fuzzer_init(cache_dir_path: abi.Slice) void { |
| 581 | 583 | } |
| 582 | 584 | |
| 583 | 585 | /// Invalid until `fuzzer_init` is called. |
| 584 | export fn fuzzer_coverage_id() u64 { | |
| 585 | return exec.pc_digest; | |
| 586 | export fn fuzzer_coverage() abi.Coverage { | |
| 587 | const coverage_id = exec.pc_digest; | |
| 588 | const header: *const abi.SeenPcsHeader = @ptrCast(@volatileCast(exec.shared_seen_pcs.items.ptr)); | |
| 589 | ||
| 590 | var seen_count: usize = 0; | |
| 591 | for (header.seenBits()) |chunk| { | |
| 592 | seen_count += @popCount(chunk); | |
| 593 | } | |
| 594 | ||
| 595 | return .{ | |
| 596 | .id = coverage_id, | |
| 597 | .runs = header.n_runs, | |
| 598 | .unique = header.unique_runs, | |
| 599 | .seen = seen_count, | |
| 600 | }; | |
| 586 | 601 | } |
| 587 | 602 | |
| 588 | 603 | /// fuzzer_init must be called beforehand |
lib/std/Build/abi.zig+11-1| ... | ... | @@ -140,7 +140,7 @@ pub const Rebuild = extern struct { |
| 140 | 140 | pub const fuzz = struct { |
| 141 | 141 | pub const TestOne = *const fn (Slice) callconv(.c) void; |
| 142 | 142 | pub extern fn fuzzer_init(cache_dir_path: Slice) void; |
| 143 | pub extern fn fuzzer_coverage_id() u64; | |
| 143 | pub extern fn fuzzer_coverage() Coverage; | |
| 144 | 144 | pub extern fn fuzzer_init_test(test_one: TestOne, unit_test_name: Slice) void; |
| 145 | 145 | pub extern fn fuzzer_new_input(bytes: Slice) void; |
| 146 | 146 | pub extern fn fuzzer_main(limit_kind: LimitKind, amount: u64) void; |
| ... | ... | @@ -253,6 +253,16 @@ pub const fuzz = struct { |
| 253 | 253 | return .{ .locs_len_raw = @bitCast(locs_len) }; |
| 254 | 254 | } |
| 255 | 255 | }; |
| 256 | ||
| 257 | /// Sent by lib/fuzzer to test_runner to obtain information about the | |
| 258 | /// active memory mapped input file and cumulative stats about previous | |
| 259 | /// fuzzing runs. | |
| 260 | pub const Coverage = extern struct { | |
| 261 | id: u64, | |
| 262 | runs: u64, | |
| 263 | unique: u64, | |
| 264 | seen: u64, | |
| 265 | }; | |
| 256 | 266 | }; |
| 257 | 267 | |
| 258 | 268 | /// ABI bits specifically relating to the time report interface. |
test/standalone/libfuzzer/main.zig+1-1| ... | ... | @@ -24,7 +24,7 @@ pub fn main() !void { |
| 24 | 24 | abi.fuzzer_new_input(.fromSlice("")); |
| 25 | 25 | abi.fuzzer_new_input(.fromSlice("hello")); |
| 26 | 26 | |
| 27 | const pc_digest = abi.fuzzer_coverage_id(); | |
| 27 | const pc_digest = abi.fuzzer_coverage().id; | |
| 28 | 28 | const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest); |
| 29 | 29 | const coverage_file = try cache_dir.openFile(coverage_file_path, .{}); |
| 30 | 30 | defer coverage_file.close(); |