Skip to content

Commit 7dc6671

Browse files
remove util.zig and politely call msync
1 parent 5bb9055 commit 7dc6671

File tree

5 files changed

+199
-184
lines changed

5 files changed

+199
-184
lines changed

lib/fuzzer.zig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ const std = @import("std");
33
const Allocator = std.mem.Allocator;
44
const assert = std.debug.assert;
55
const fatal = std.process.fatal;
6-
const util = @import("fuzzer/util.zig");
7-
const check = util.check;
6+
const check = @import("fuzzer/main.zig").check;
87
const Fuzzer = @import("fuzzer/main.zig").Fuzzer;
8+
const Slice = @import("fuzzer/main.zig").Slice;
99
const fc = @import("fuzzer/feature_capture.zig");
10-
const Slice = util.Slice;
1110

1211
// ==== global state ====
1312

@@ -114,6 +113,10 @@ export fn fuzzer_init(cache_dir_struct: Slice) void {
114113
fuzzer = Fuzzer.init(general_purpose_allocator.allocator(), cache_dir, pc_counters, pcs);
115114
}
116115

116+
export fn fuzzer_deinit() void {
117+
fuzzer.deinit();
118+
}
119+
117120
// ==== log ====
118121

119122
pub const std_options = .{

lib/fuzzer/InputPoolPosix.zig

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414
const std = @import("std");
1515
const assert = std.debug.assert;
16-
const util = @import("util.zig");
17-
const check = util.check;
16+
const check = @import("main.zig").check;
1817
const MemoryMappedList = @import("memory_mapped_list.zig").MemoryMappedList;
1918

2019
/// maximum 2GiB of input data should be enough. 32th bit is delete flag
@@ -107,7 +106,7 @@ pub fn init(dir: std.fs.Dir, pc_digest: u64) InputPoolPosix {
107106
};
108107
}
109108

110-
pub fn deinit(ip: InputPoolPosix) void {
109+
pub fn deinit(ip: *InputPoolPosix) void {
111110
ip.buffer.deinit();
112111
ip.meta.deinit();
113112
}

lib/fuzzer/main.zig

Lines changed: 174 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,173 @@ const MemoryMappedList = @import("memory_mapped_list.zig").MemoryMappedList;
99
const mutate = @import("mutate.zig");
1010
const InputPool = @import("input_pool.zig").InputPool;
1111
const feature_capture = @import("feature_capture.zig");
12-
const util = @import("util.zig");
13-
const check = util.check;
1412

1513
// current unused
1614
export threadlocal var __sancov_lowest_stack: usize = std.math.maxInt(usize);
1715

16+
/// Returns error union payload or void if error set
17+
fn StripError(comptime T: type) type {
18+
return switch (@typeInfo(T)) {
19+
.error_union => |eu| eu.payload,
20+
.error_set => void,
21+
else => @compileError("no error to strip"),
22+
};
23+
}
24+
25+
/// Checks that the value is not error. If it is error, it logs the args and
26+
/// terminates
27+
pub fn check(src: std.builtin.SourceLocation, v: anytype, args: anytype) StripError(@TypeOf(v)) {
28+
return v catch |e| {
29+
var buffer: [4096]u8 = undefined;
30+
var fbs = std.io.fixedBufferStream(&buffer);
31+
var cw = std.io.countingWriter(fbs.writer());
32+
const w = cw.writer();
33+
if (@typeInfo(@TypeOf(args)).@"struct".fields.len != 0) {
34+
w.writeAll(" (") catch {};
35+
inline for (@typeInfo(@TypeOf(args)).@"struct".fields, 0..) |field, i| {
36+
const Field = @TypeOf(@field(args, field.name));
37+
if (i != 0) {
38+
w.writeAll(", ") catch {};
39+
}
40+
if (Field == []const u8 or Field == []u8) {
41+
w.print("{s}='{s}'", .{ field.name, @field(args, field.name) }) catch {};
42+
} else {
43+
w.print("{s}={any}", .{ field.name, @field(args, field.name) }) catch {};
44+
}
45+
}
46+
w.writeAll(")") catch {};
47+
}
48+
std.process.fatal("{s}:{}: {s}{s}", .{ src.file, src.line, @errorName(e), buffer[0..cw.bytes_written] });
49+
};
50+
}
51+
52+
/// Type for passing slices across extern functions where we can't use zig
53+
/// types
54+
pub const Slice = extern struct {
55+
ptr: [*]const u8,
56+
len: usize,
57+
58+
pub fn toZig(s: Slice) []const u8 {
59+
return s.ptr[0..s.len];
60+
}
61+
62+
pub fn fromZig(s: []const u8) Slice {
63+
return .{
64+
.ptr = s.ptr,
65+
.len = s.len,
66+
};
67+
}
68+
};
69+
70+
fn createFileBail(dir: std.fs.Dir, sub_path: []const u8, flags: std.fs.File.CreateFlags) std.fs.File {
71+
return dir.createFile(sub_path, flags) catch |err| switch (err) {
72+
error.FileNotFound => {
73+
const dir_name = std.fs.path.dirname(sub_path).?;
74+
check(@src(), dir.makePath(dir_name), .{ .dir_name = dir_name });
75+
return check(@src(), dir.createFile(sub_path, flags), .{ .sub_path = sub_path, .flags = flags });
76+
},
77+
else => |e| std.process.fatal("create file '{s}' failed: {}", .{ sub_path, e }),
78+
};
79+
}
80+
81+
/// Sorts array of features
82+
fn sort(a: []u32) void {
83+
std.mem.sort(u32, a, void{}, std.sort.asc(u32));
84+
}
85+
86+
/// Deduplicates array of sorted features
87+
fn uniq(a: []u32) []u32 {
88+
var write: usize = 0;
89+
90+
if (a.len == 0) return a;
91+
92+
var last: u32 = a[0];
93+
a[write] = last;
94+
write += 1;
95+
96+
for (a[1..]) |v| {
97+
if (v != last) {
98+
a[write] = v;
99+
write += 1;
100+
last = v;
101+
}
102+
}
103+
104+
return a[0..write];
105+
}
106+
107+
test uniq {
108+
var data: [9]u32 = (&[_]u32{ 0, 0, 1, 2, 2, 2, 3, 4, 4 }).*;
109+
const cropped = uniq(&data);
110+
try std.testing.expectEqualSlices(u32, &[_]u32{ 0, 1, 2, 3, 4 }, cropped);
111+
}
112+
113+
pub const CmpResult = struct { only_a: u32, only_b: u32, both: u32 };
114+
115+
/// Compares two sorted lists of features
116+
fn cmp(a: []const u32, b: []const u32) CmpResult {
117+
var ai: u32 = 0;
118+
var bi: u32 = 0;
119+
120+
var only_a: u32 = 0;
121+
var only_b: u32 = 0;
122+
var both: u32 = 0;
123+
124+
while (true) {
125+
if (ai == a.len) {
126+
only_b += @intCast(b[bi..].len);
127+
break;
128+
} else if (bi == b.len) {
129+
only_a += @intCast(a[ai..].len);
130+
break;
131+
}
132+
133+
const i = a[ai];
134+
const j = b[bi];
135+
136+
if (i < j) {
137+
only_a += 1;
138+
ai += 1;
139+
} else if (i > j) {
140+
only_b += 1;
141+
bi += 1;
142+
} else {
143+
both += 1;
144+
ai += 1;
145+
bi += 1;
146+
}
147+
}
148+
149+
return .{
150+
.only_a = only_a,
151+
.only_b = only_b,
152+
.both = both,
153+
};
154+
}
155+
156+
test cmp {
157+
const e = std.testing.expectEqual;
158+
const R = CmpResult;
159+
try e(R{ .only_a = 0, .only_b = 0, .both = 0 }, cmp(&.{}, &.{}));
160+
try e(R{ .only_a = 1, .only_b = 0, .both = 0 }, cmp(&.{1}, &.{}));
161+
try e(R{ .only_a = 0, .only_b = 1, .both = 0 }, cmp(&.{}, &.{1}));
162+
try e(R{ .only_a = 0, .only_b = 0, .both = 1 }, cmp(&.{1}, &.{1}));
163+
try e(R{ .only_a = 1, .only_b = 1, .both = 0 }, cmp(&.{1}, &.{2}));
164+
try e(R{ .only_a = 1, .only_b = 0, .both = 1 }, cmp(&.{ 1, 2 }, &.{1}));
165+
try e(R{ .only_a = 0, .only_b = 1, .both = 1 }, cmp(&.{1}, &.{ 1, 2 }));
166+
try e(R{ .only_a = 0, .only_b = 0, .both = 2 }, cmp(&.{ 1, 2 }, &.{ 1, 2 }));
167+
try e(R{ .only_a = 3, .only_b = 3, .both = 0 }, cmp(&.{ 1, 2, 3 }, &.{ 4, 5, 6 }));
168+
}
169+
170+
/// Merges the second sorted list of features into the first list of sorted
171+
/// features
172+
fn merge(dest: *std.ArrayList(u32), src: []const u32) !void {
173+
// TODO: can be in O(n) time and O(1) space
174+
try dest.appendSlice(src);
175+
sort(dest.items);
176+
dest.items = uniq(dest.items);
177+
}
178+
18179
fn hashPCs(pcs: []const usize) u64 {
19180
var hasher = std.hash.Wyhash.init(0);
20181
hasher.update(std.mem.asBytes(pcs));
@@ -26,7 +187,7 @@ fn hashPCs(pcs: []const usize) u64 {
26187
/// - list of PC addresses (usize elements)
27188
/// - list of hit flag, 1 bit per address (stored in u8 elements)
28189
fn initCoverageFile(cache_dir: std.fs.Dir, coverage_file_path: []const u8, pcs: []const usize) MemoryMappedList(u8) {
29-
const coverage_file = util.createFileBail(cache_dir, coverage_file_path, .{
190+
const coverage_file = createFileBail(cache_dir, coverage_file_path, .{
30191
.read = true,
31192
.truncate = false,
32193
});
@@ -184,6 +345,11 @@ pub const Fuzzer = struct {
184345
};
185346
}
186347

348+
pub fn deinit(f: *Fuzzer) void {
349+
f.input_pool.deinit();
350+
f.seen_pcs.deinit();
351+
}
352+
187353
fn readOptions(f: *Fuzzer, options: *const std.testing.FuzzInputOptions) void {
188354
// Otherwise the options corpus would be re-added every time we restart
189355
// the fuzzer
@@ -194,7 +360,7 @@ pub const Fuzzer = struct {
194360
}
195361
}
196362

197-
pub fn makeUpInitialCorpus(f: *Fuzzer) void {
363+
fn makeUpInitialCorpus(f: *Fuzzer) void {
198364
var buffer: [256]u8 = undefined;
199365
for (0..256) |len| {
200366
const slice = buffer[0..len];
@@ -283,10 +449,10 @@ pub const Fuzzer = struct {
283449

284450
fn analyzeLastRun(f: *Fuzzer) void {
285451
var features = feature_capture.values();
286-
util.sort(features);
287-
features = util.uniq(features);
452+
sort(features);
453+
features = uniq(features);
288454

289-
const analysis = util.cmp(features, f.all_features.items);
455+
const analysis = cmp(features, f.all_features.items);
290456

291457
if (analysis.only_a == 0) {
292458
return; // bad input
@@ -310,7 +476,7 @@ pub const Fuzzer = struct {
310476
}
311477

312478
var ar = f.all_features.toManaged(f.gpa);
313-
check(@src(), util.merge(&ar, features), .{});
479+
check(@src(), merge(&ar, features), .{});
314480
f.all_features = ar.moveToUnmanaged();
315481
updateGlobalCoverage(f.pc_counters, f.seen_pcs);
316482
}

lib/fuzzer/memory_mapped_list_posix.zig

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,10 @@
55
// previously mmaped pages become automatically valid without needing to
66
// mremap.
77
//
8-
// We don't bother munmaping since all current uses of this datastructure use
9-
// it for the entire duration of the program.
108

119
const std = @import("std");
1210
const assert = std.debug.assert;
13-
const util = @import("util.zig");
14-
const check = util.check;
11+
const check = @import("main.zig").check;
1512

1613
pub fn MemoryMappedList(comptime T: type) type {
1714
return struct {
@@ -48,6 +45,22 @@ pub fn MemoryMappedList(comptime T: type) type {
4845
};
4946
}
5047

48+
pub fn deinit(self: *Self) void {
49+
// volatileCast is safe. mem is never used to actually write to or
50+
// read from
51+
const startT: [*]align(std.mem.page_size) T = @volatileCast(self.items.ptr);
52+
const start8: [*]align(std.mem.page_size) u8 = @ptrCast(startT);
53+
const len8: usize = self.items.len * @sizeOf(T);
54+
// We don't bother munmaping since all current uses of this struct
55+
// use it until the end of the program. Even this msync is more of
56+
// a politeness than a necessity:
57+
// https://stackoverflow.com/questions/31539208/posix-shared-memory-and-msync
58+
check(@src(), std.posix.msync(start8[0..len8], std.posix.MSF.ASYNC), .{
59+
.ptr = start8,
60+
.len = len8,
61+
});
62+
}
63+
5164
pub fn append(self: *Self, item: T) void {
5265
return self.appendSlice(&[1]T{item});
5366
}

0 commit comments

Comments
 (0)