Skip to content

Commit f545448

Browse files
unify files setup
1 parent 16778eb commit f545448

File tree

3 files changed

+100
-47
lines changed

3 files changed

+100
-47
lines changed

lib/fuzzer/InputPoolPosix.zig

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const std = @import("std");
1515
const assert = std.debug.assert;
1616
const fatal = std.process.fatal;
1717
const MemoryMappedList = @import("memory_mapped_list.zig").MemoryMappedList;
18+
const File = std.fs.File;
1819

1920
/// maximum 2GiB of input data should be enough. 32th bit is delete flag
2021
pub const Index = u31;
@@ -63,20 +64,7 @@ fn getData(m: MemoryMappedList(u32)) []volatile Flags {
6364
return @ptrCast(rest);
6465
}
6566

66-
pub fn init(dir: std.fs.Dir, pc_digest: u64) InputPoolPosix {
67-
const hex_digest = std.fmt.hex(pc_digest);
68-
69-
const buffer_file_path = "v/" ++ hex_digest ++ "buffer";
70-
const meta_file_path = "v/" ++ hex_digest ++ "meta";
71-
const buffer_file = dir.createFile(buffer_file_path, .{
72-
.read = true,
73-
.truncate = false,
74-
}) catch |e| fatal("create file at '{s}' failed: {}", .{ buffer_file_path, e });
75-
const meta_file = dir.createFile(meta_file_path, .{
76-
.read = true,
77-
.truncate = false,
78-
}) catch |e| fatal("create file at '{s}' failed: {}", .{ meta_file_path, e });
79-
67+
pub fn init(meta_file: File, buffer_file: File) InputPoolPosix {
8068
const buffer = MemoryMappedList(u8).init(buffer_file, std.math.maxInt(Index));
8169
var meta = MemoryMappedList(u32).init(meta_file, std.math.maxInt(Index));
8270

@@ -88,7 +76,7 @@ pub fn init(dir: std.fs.Dir, pc_digest: u64) InputPoolPosix {
8876
.number_of_string = 0,
8977
};
9078

91-
// []u8 to []u32
79+
// []u8 to []u32 conversion
9280
const s = std.mem.asBytes(&header);
9381
const z: [*]const u32 = @ptrCast(s.ptr);
9482
const size32 = @divExact(@sizeOf(MetaHeader), @sizeOf(u32));
@@ -119,7 +107,7 @@ pub fn insertString(ip: *InputPoolPosix, str: []const u8) void {
119107

120108
ip.buffer.appendSlice(str);
121109
ip.meta.append(@intCast(ip.buffer.items.len));
122-
getHeader(ip.meta).number_of_string += 1;
110+
header.number_of_string += 1;
123111
}
124112

125113
const deleteMask: u32 = 0x8000_0000;

lib/fuzzer/main.zig

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const MemoryMappedList = @import("memory_mapped_list.zig").MemoryMappedList;
66
const feature_capture = @import("feature_capture.zig");
77

88
const Allocator = std.mem.Allocator;
9+
const File = std.fs.File;
10+
const Dir = std.fs.Dir;
911
const ArrayList = std.ArrayList;
1012
const ArrayListUnmanaged = std.ArrayListUnmanaged;
1113
const Options = std.testing.FuzzInputOptions;
@@ -20,17 +22,6 @@ const InitialFeatureBufferCap = 64;
2022
// currently unused
2123
export threadlocal var __sancov_lowest_stack: usize = std.math.maxInt(usize);
2224

23-
fn createFileBail(dir: std.fs.Dir, sub_path: []const u8, flags: std.fs.File.CreateFlags) std.fs.File {
24-
return dir.createFile(sub_path, flags) catch |err| switch (err) {
25-
error.FileNotFound => {
26-
const dir_name = std.fs.path.dirname(sub_path).?;
27-
dir.makePath(dir_name) catch |e| fatal("makePath '{s}' failed: {}", .{ dir_name, e });
28-
return dir.createFile(sub_path, flags) catch |e| fatal("createFile '{s}' failed: {}", .{ sub_path, e });
29-
},
30-
else => |e| fatal("create file '{s}' failed: {}", .{ sub_path, e }),
31-
};
32-
}
33-
3425
/// Deduplicates array of sorted features
3526
fn uniq(a: []u32) []u32 {
3627
var write: usize = 0;
@@ -138,15 +129,8 @@ fn hashPCs(pcs: []const usize) u64 {
138129
return hasher.final();
139130
}
140131

141-
/// File contains SeenPcsHeader and trailing data:
142-
/// - list of PC addresses (usize elements)
143-
/// - list of hit flag, 1 bit per address (stored in u8 elements)
144-
fn initCoverageFile(cache_dir: std.fs.Dir, coverage_file_path: []const u8, pcs: []const usize) MemoryMappedList(u8) {
145-
const coverage_file = createFileBail(cache_dir, coverage_file_path, .{
146-
.read = true,
147-
.truncate = false,
148-
});
149-
defer coverage_file.close();
132+
/// File contains SeenPcsHeader and its trailing data
133+
fn initCoverageFile(coverage_file: File, pcs: []const usize) MemoryMappedList(u8) {
150134
const n_bitset_elems = (pcs.len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
151135

152136
comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
@@ -161,7 +145,7 @@ fn initCoverageFile(cache_dir: std.fs.Dir, coverage_file_path: []const u8, pcs:
161145
const existing_len = seen_pcs.items.len;
162146

163147
if (existing_len != 0 and existing_len != bytes_len) {
164-
fatal("coverage file '{s}' is invalid (wrong length)", .{coverage_file_path});
148+
fatal("coverage file is invalid (wrong length, wanted {}, is {})", .{ bytes_len, existing_len });
165149
}
166150

167151
if (existing_len != 0) {
@@ -170,7 +154,7 @@ fn initCoverageFile(cache_dir: std.fs.Dir, coverage_file_path: []const u8, pcs:
170154
const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
171155
for (existing_pcs, pcs) |old, new| {
172156
if (old != new) {
173-
fatal("coverage file '{s}' is invalid (pc missmatch)", .{coverage_file_path});
157+
fatal("coverage file is invalid (pc missmatch)", .{});
174158
}
175159
}
176160
} else {
@@ -359,6 +343,46 @@ fn mergeInput(
359343
updateGlobalCoverage(pc_counters, seen_pcs);
360344
}
361345

346+
const Files = struct { buffer: File, meta: File, coverage: File };
347+
348+
fn setupFilesBail(c: Dir, i: u64) Files {
349+
return setupFiles(c, i) catch |e| fatal("Failed to setup files: {}", .{e});
350+
}
351+
352+
fn cleanupFiles(f: Files) void {
353+
f.buffer.close();
354+
f.meta.close();
355+
f.coverage.close();
356+
}
357+
358+
fn setupFiles(cache_dir: Dir, coverage_id: u64) !Files {
359+
// we create 1 folder and 3 files:
360+
// cache/v/
361+
// cache/v/***buffer
362+
// cache/v/***meta
363+
// cache/v/***coverage
364+
365+
const hex_digest = std.fmt.hex(coverage_id);
366+
367+
const flags = File.CreateFlags{ .read = true, .truncate = false };
368+
369+
try cache_dir.makeDir("v");
370+
371+
const v = try cache_dir.openDir("v", .{});
372+
defer v.close();
373+
374+
const buffer = try v.createFile(hex_digest ++ "buffer", flags);
375+
errdefer buffer.close();
376+
377+
const meta = try v.createFile(hex_digest ++ "meta", flags);
378+
errdefer meta.close();
379+
380+
const coverage = try v.createFile(hex_digest ++ "coverage", flags);
381+
errdefer coverage.close();
382+
383+
return .{ .buffer = buffer, .meta = meta, .coverage = coverage };
384+
}
385+
362386
pub const Fuzzer = struct {
363387
// given to us by LLVM
364388
pcs: []const usize,
@@ -368,9 +392,9 @@ pub const Fuzzer = struct {
368392
/// information, available to other processes.
369393
coverage_id: u64,
370394

371-
cache_dir: std.fs.Dir,
395+
cache_dir: Dir,
372396

373-
pub fn init(cache_dir: std.fs.Dir, pc_counters: []u8, pcs: []usize) Fuzzer {
397+
pub fn init(cache_dir: Dir, pc_counters: []u8, pcs: []usize) Fuzzer {
374398
assert(pc_counters.len == pcs.len);
375399

376400
return .{
@@ -400,26 +424,23 @@ pub const Fuzzer = struct {
400424
var feature_buffer = try ArrayListUnmanaged(u32).initCapacity(a, InitialFeatureBufferCap);
401425
defer feature_buffer.deinit(a);
402426

403-
// Choose a file name for the coverage based on a hash of the PCs that
404-
// will be stored within.
405-
const hex_digest = std.fmt.hex(f.coverage_id);
406-
const coverage_file_path = "v/" ++ hex_digest ++ "coverage";
427+
const files = setupFilesBail(f.cache_dir, f.coverage_id);
428+
defer cleanupFiles(files);
407429

408430
var ip = InputPool.init(f.cache_dir, f.coverage_id);
409431
defer ip.deinit();
410432

411433
// Tracks which PCs have been seen across all runs that do not crash the fuzzer process.
412434
// Stored in a memory-mapped file so that it can be shared with other
413435
// processes and viewed while the fuzzer is running.
414-
const seen_pcs = initCoverageFile(f.cache_dir, coverage_file_path, f.pcs);
415-
416-
std.log.info("Coverage id is {s}", .{&hex_digest});
436+
const seen_pcs = initCoverageFile(files.coverage, f.pcs);
417437

418438
std.log.info(
419439
\\Initial corpus of size {}
420440
\\F - this input features
421441
\\T - new unique features
422442
, .{ip.len()});
443+
423444
if (ip.len() == 0) {
424445
initialCorpusRandom(&ip, rng);
425446
}

lib/fuzzer/monitor.zig

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// This is a standalone tool that can print the strings stored inside a corpus
2+
// (buffer + meta file pair in the .zig-cache/v/ directory)
3+
4+
const std = @import("std");
5+
const fatal = std.process.fatal;
6+
7+
const InputPool = @import("input_pool.zig").InputPool;
8+
9+
pub fn main() void {
10+
var args = std.process.args();
11+
const bin = args.next();
12+
const cache_dir_path = args.next();
13+
const pc_digest_str = args.next();
14+
15+
if (cache_dir_path == null or pc_digest_str == null or args.next() != null) {
16+
fatal("usage: {s} CACHE_DIR PC_DIGEST\n", .{bin.?});
17+
}
18+
19+
// std.fmt.hex actually produces the hex number in the opposite order than
20+
// parseInt reads...
21+
const pc_digest = @byteSwap(std.fmt.parseInt(u64, pc_digest_str.?, 16) catch |e|
22+
fatal("invalid pc digest: {}", .{e}));
23+
24+
const cache_dir = std.fs.cwd().makeOpenPath(cache_dir_path.?, .{}) catch |e|
25+
fatal("invalid cache dir: {}", .{e});
26+
27+
std.log.info("cache_dir: {s}", .{cache_dir_path.?});
28+
std.log.info("pc_digest: {x}", .{@byteSwap(pc_digest)});
29+
30+
var input_pool = InputPool.init(cache_dir, pc_digest);
31+
32+
const len = input_pool.len();
33+
34+
std.log.info("There are {} strings in the corpus:", .{len});
35+
36+
for (0..len) |i| {
37+
const str = input_pool.getString(@intCast(i));
38+
39+
// Only writing to this buffer has side effects.
40+
const str2: []const u8 = @volatileCast(str);
41+
42+
std.log.info("\"{}\"", .{std.zig.fmtEscapes(str2)});
43+
}
44+
}

0 commit comments

Comments
 (0)