Skip to content

Commit 9eb37e9

Browse files
committed
libfuzzer: Use std.fs.MemoryMap for seen_pcs.
1 parent 6dff77d commit 9eb37e9

File tree

1 file changed

+24
-61
lines changed

1 file changed

+24
-61
lines changed

lib/fuzzer.zig

+24-61
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ const Fuzzer = struct {
115115
/// Tracks which PCs have been seen across all runs that do not crash the fuzzer process.
116116
/// Stored in a memory-mapped file so that it can be shared with other
117117
/// processes and viewed while the fuzzer is running.
118-
seen_pcs: MemoryMappedList,
118+
seen_pcs: std.fs.MemoryMap,
119119
cache_dir: std.fs.Dir,
120120
/// Identifies the file name that will be used to store coverage
121121
/// information, available to other processes.
@@ -229,11 +229,17 @@ const Fuzzer = struct {
229229
} else if (existing_len != bytes_len) {
230230
fatal("incompatible existing coverage file (differing lengths)", .{});
231231
}
232-
f.seen_pcs = MemoryMappedList.init(coverage_file, existing_len, bytes_len) catch |err| {
232+
f.seen_pcs = std.fs.MemoryMap.init(coverage_file, .{
233+
.exclusivity = .shared,
234+
.protection = .{ .write = true },
235+
.length = bytes_len,
236+
}) catch |err| {
233237
fatal("unable to init coverage memory map: {s}", .{@errorName(err)});
234238
};
235239
if (existing_len != 0) {
236-
const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems ..][0 .. flagged_pcs.len * @sizeOf(usize)];
240+
const existing_pcs_start = @sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems;
241+
const existing_pcs_end = existing_pcs_start + flagged_pcs.len * @sizeOf(usize);
242+
const existing_pcs_bytes = f.seen_pcs.mapped[existing_pcs_start..existing_pcs_end];
237243
const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
238244
for (existing_pcs, flagged_pcs, 0..) |old, new, i| {
239245
if (old != new.addr) {
@@ -249,11 +255,18 @@ const Fuzzer = struct {
249255
.pcs_len = flagged_pcs.len,
250256
.lowest_stack = std.math.maxInt(usize),
251257
};
252-
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
253-
f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));
254-
for (flagged_pcs) |flagged_pc| {
255-
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));
256-
}
258+
f.seen_pcs.cast(SeenPcsHeader).* = header;
259+
const bitset_elems_start = @sizeOf(SeenPcsHeader);
260+
const bitset_elems_end = bitset_elems_start + n_bitset_elems * @sizeOf(usize);
261+
const bitset_elems_bytes = f.seen_pcs.mapped[bitset_elems_start..bitset_elems_end];
262+
const bitset_elems_dest = std.mem.bytesAsSlice(usize, bitset_elems_bytes);
263+
@memset(bitset_elems_dest, 0);
264+
const flagged_pcs_start = bitset_elems_end;
265+
const flagged_pcs_end = flagged_pcs_start + flagged_pcs.len * @sizeOf(usize);
266+
const flagged_pcs_bytes = f.seen_pcs.mapped[flagged_pcs_start..flagged_pcs_end];
267+
const flagged_pcs_dest = std.mem.bytesAsSlice(usize, flagged_pcs_bytes);
268+
for (flagged_pcs, flagged_pcs_dest) |item, *slot|
269+
slot.* = item.addr;
257270
}
258271
}
259272

@@ -306,7 +319,7 @@ const Fuzzer = struct {
306319
{
307320
// Track code coverage from all runs.
308321
comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
309-
const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]);
322+
const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.mapped[@sizeOf(SeenPcsHeader)..]);
310323
const remainder = f.flagged_pcs.len % @bitSizeOf(usize);
311324
const aligned_len = f.flagged_pcs.len - remainder;
312325
const seen_pcs = header_end_ptr[0..aligned_len];
@@ -330,7 +343,7 @@ const Fuzzer = struct {
330343
}
331344
}
332345

333-
const header: *volatile SeenPcsHeader = @ptrCast(f.seen_pcs.items[0..@sizeOf(SeenPcsHeader)]);
346+
const header = f.seen_pcs.cast(SeenPcsHeader);
334347
_ = @atomicRmw(usize, &header.unique_runs, .Add, 1, .monotonic);
335348
}
336349

@@ -360,7 +373,7 @@ const Fuzzer = struct {
360373
try f.mutate();
361374

362375
f.n_runs += 1;
363-
const header: *volatile SeenPcsHeader = @ptrCast(f.seen_pcs.items[0..@sizeOf(SeenPcsHeader)]);
376+
const header = f.seen_pcs.cast(SeenPcsHeader);
364377
_ = @atomicRmw(usize, &header.n_runs, .Add, 1, .monotonic);
365378
_ = @atomicRmw(usize, &header.lowest_stack, .Min, __sancov_lowest_stack, .monotonic);
366379
@memset(f.pc_counters, 0);
@@ -468,53 +481,3 @@ export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void {
468481

469482
fuzzer.init(cache_dir) catch |err| fatal("unable to init fuzzer: {s}", .{@errorName(err)});
470483
}
471-
472-
/// Like `std.ArrayListUnmanaged(u8)` but backed by memory mapping.
473-
pub const MemoryMappedList = struct {
474-
/// Contents of the list.
475-
///
476-
/// Pointers to elements in this slice are invalidated by various functions
477-
/// of this ArrayList in accordance with the respective documentation. In
478-
/// all cases, "invalidated" means that the memory has been passed to this
479-
/// allocator's resize or free function.
480-
items: []align(std.mem.page_size) volatile u8,
481-
/// How many bytes this list can hold without allocating additional memory.
482-
capacity: usize,
483-
484-
pub fn init(file: std.fs.File, length: usize, capacity: usize) !MemoryMappedList {
485-
const ptr = try std.posix.mmap(
486-
null,
487-
capacity,
488-
std.posix.PROT.READ | std.posix.PROT.WRITE,
489-
.{ .TYPE = .SHARED },
490-
file.handle,
491-
0,
492-
);
493-
return .{
494-
.items = ptr[0..length],
495-
.capacity = capacity,
496-
};
497-
}
498-
499-
/// Append the slice of items to the list.
500-
/// Asserts that the list can hold the additional items.
501-
pub fn appendSliceAssumeCapacity(l: *MemoryMappedList, items: []const u8) void {
502-
const old_len = l.items.len;
503-
const new_len = old_len + items.len;
504-
assert(new_len <= l.capacity);
505-
l.items.len = new_len;
506-
@memcpy(l.items[old_len..][0..items.len], items);
507-
}
508-
509-
/// Append a value to the list `n` times.
510-
/// Never invalidates element pointers.
511-
/// The function is inline so that a comptime-known `value` parameter will
512-
/// have better memset codegen in case it has a repeated byte pattern.
513-
/// Asserts that the list can hold the additional items.
514-
pub inline fn appendNTimesAssumeCapacity(l: *MemoryMappedList, value: u8, n: usize) void {
515-
const new_len = l.items.len + n;
516-
assert(new_len <= l.capacity);
517-
@memset(l.items.ptr[l.items.len..new_len], value);
518-
l.items.len = new_len;
519-
}
520-
};

0 commit comments

Comments
 (0)