@@ -9,12 +9,173 @@ const MemoryMappedList = @import("memory_mapped_list.zig").MemoryMappedList;
9
9
const mutate = @import ("mutate.zig" );
10
10
const InputPool = @import ("input_pool.zig" ).InputPool ;
11
11
const feature_capture = @import ("feature_capture.zig" );
12
- const util = @import ("util.zig" );
13
- const check = util .check ;
14
12
15
13
// current unused
16
14
export threadlocal var __sancov_lowest_stack : usize = std .math .maxInt (usize );
17
15
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
+
18
179
fn hashPCs (pcs : []const usize ) u64 {
19
180
var hasher = std .hash .Wyhash .init (0 );
20
181
hasher .update (std .mem .asBytes (pcs ));
@@ -26,7 +187,7 @@ fn hashPCs(pcs: []const usize) u64 {
26
187
/// - list of PC addresses (usize elements)
27
188
/// - list of hit flag, 1 bit per address (stored in u8 elements)
28
189
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 , .{
30
191
.read = true ,
31
192
.truncate = false ,
32
193
});
@@ -184,6 +345,11 @@ pub const Fuzzer = struct {
184
345
};
185
346
}
186
347
348
+ pub fn deinit (f : * Fuzzer ) void {
349
+ f .input_pool .deinit ();
350
+ f .seen_pcs .deinit ();
351
+ }
352
+
187
353
fn readOptions (f : * Fuzzer , options : * const std.testing.FuzzInputOptions ) void {
188
354
// Otherwise the options corpus would be re-added every time we restart
189
355
// the fuzzer
@@ -194,7 +360,7 @@ pub const Fuzzer = struct {
194
360
}
195
361
}
196
362
197
- pub fn makeUpInitialCorpus (f : * Fuzzer ) void {
363
+ fn makeUpInitialCorpus (f : * Fuzzer ) void {
198
364
var buffer : [256 ]u8 = undefined ;
199
365
for (0.. 256) | len | {
200
366
const slice = buffer [0.. len ];
@@ -283,10 +449,10 @@ pub const Fuzzer = struct {
283
449
284
450
fn analyzeLastRun (f : * Fuzzer ) void {
285
451
var features = feature_capture .values ();
286
- util . sort (features );
287
- features = util . uniq (features );
452
+ sort (features );
453
+ features = uniq (features );
288
454
289
- const analysis = util . cmp (features , f .all_features .items );
455
+ const analysis = cmp (features , f .all_features .items );
290
456
291
457
if (analysis .only_a == 0 ) {
292
458
return ; // bad input
@@ -310,7 +476,7 @@ pub const Fuzzer = struct {
310
476
}
311
477
312
478
var ar = f .all_features .toManaged (f .gpa );
313
- check (@src (), util . merge (& ar , features ), .{});
479
+ check (@src (), merge (& ar , features ), .{});
314
480
f .all_features = ar .moveToUnmanaged ();
315
481
updateGlobalCoverage (f .pc_counters , f .seen_pcs );
316
482
}
0 commit comments