Skip to content

Commit 6f3e993

Browse files
authored
Merge pull request #20725 from ziglang/fuzz
initial support for integrated fuzzing
2 parents 255547d + 61ad1be commit 6f3e993

26 files changed

+406
-105
lines changed

lib/fuzzer.zig

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const std = @import("std");
2+
3+
export threadlocal var __sancov_lowest_stack: usize = 0;
4+
5+
export fn __sanitizer_cov_8bit_counters_init(start: [*]u8, stop: [*]u8) void {
6+
std.debug.print("__sanitizer_cov_8bit_counters_init start={*}, stop={*}\n", .{ start, stop });
7+
}
8+
9+
export fn __sanitizer_cov_pcs_init(pcs_beg: [*]const usize, pcs_end: [*]const usize) void {
10+
std.debug.print("__sanitizer_cov_pcs_init pcs_beg={*}, pcs_end={*}\n", .{ pcs_beg, pcs_end });
11+
}
12+
13+
export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {
14+
handleCmp(@returnAddress(), arg1, arg2);
15+
}
16+
17+
export fn __sanitizer_cov_trace_cmp1(arg1: u8, arg2: u8) void {
18+
handleCmp(@returnAddress(), arg1, arg2);
19+
}
20+
21+
export fn __sanitizer_cov_trace_const_cmp2(arg1: u16, arg2: u16) void {
22+
handleCmp(@returnAddress(), arg1, arg2);
23+
}
24+
25+
export fn __sanitizer_cov_trace_cmp2(arg1: u16, arg2: u16) void {
26+
handleCmp(@returnAddress(), arg1, arg2);
27+
}
28+
29+
export fn __sanitizer_cov_trace_const_cmp4(arg1: u32, arg2: u32) void {
30+
handleCmp(@returnAddress(), arg1, arg2);
31+
}
32+
33+
export fn __sanitizer_cov_trace_cmp4(arg1: u32, arg2: u32) void {
34+
handleCmp(@returnAddress(), arg1, arg2);
35+
}
36+
37+
export fn __sanitizer_cov_trace_const_cmp8(arg1: u64, arg2: u64) void {
38+
handleCmp(@returnAddress(), arg1, arg2);
39+
}
40+
41+
export fn __sanitizer_cov_trace_cmp8(arg1: u64, arg2: u64) void {
42+
handleCmp(@returnAddress(), arg1, arg2);
43+
}
44+
45+
export fn __sanitizer_cov_trace_switch(val: u64, cases_ptr: [*]u64) void {
46+
const pc = @returnAddress();
47+
const len = cases_ptr[0];
48+
const val_size_in_bits = cases_ptr[1];
49+
const cases = cases_ptr[2..][0..len];
50+
std.debug.print("0x{x}: switch on value {d} ({d} bits) with {d} cases\n", .{
51+
pc, val, val_size_in_bits, cases.len,
52+
});
53+
}
54+
55+
export fn __sanitizer_cov_trace_pc_indir(callee: usize) void {
56+
const pc = @returnAddress();
57+
std.debug.print("0x{x}: indirect call to 0x{x}\n", .{ pc, callee });
58+
}
59+
60+
fn handleCmp(pc: usize, arg1: u64, arg2: u64) void {
61+
std.debug.print("0x{x}: comparison of {d} and {d}\n", .{ pc, arg1, arg2 });
62+
}

lib/std/Build/Module.zig

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ stack_protector: ?bool,
2828
stack_check: ?bool,
2929
sanitize_c: ?bool,
3030
sanitize_thread: ?bool,
31+
fuzz: ?bool,
3132
code_model: std.builtin.CodeModel,
3233
valgrind: ?bool,
3334
pic: ?bool,
@@ -186,6 +187,7 @@ pub const CreateOptions = struct {
186187
stack_check: ?bool = null,
187188
sanitize_c: ?bool = null,
188189
sanitize_thread: ?bool = null,
190+
fuzz: ?bool = null,
189191
/// Whether to emit machine code that integrates with Valgrind.
190192
valgrind: ?bool = null,
191193
/// Position Independent Code
@@ -228,6 +230,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
228230
.stack_check = options.stack_check,
229231
.sanitize_c = options.sanitize_c,
230232
.sanitize_thread = options.sanitize_thread,
233+
.fuzz = options.fuzz,
231234
.code_model = options.code_model,
232235
.valgrind = options.valgrind,
233236
.pic = options.pic,
@@ -642,6 +645,7 @@ pub fn appendZigProcessFlags(
642645
try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
643646
try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");
644647
try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
648+
try addFlag(zig_args, m.fuzz, "-ffuzz", "-fno-fuzz");
645649
try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
646650
try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
647651
try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");

lib/std/mem.zig

+6-6
Original file line numberDiff line numberDiff line change
@@ -636,18 +636,20 @@ test lessThan {
636636
try testing.expect(lessThan(u8, "", "a"));
637637
}
638638

639-
const backend_can_use_eql_bytes = switch (builtin.zig_backend) {
639+
const eqlBytes_allowed = switch (builtin.zig_backend) {
640640
// The SPIR-V backend does not support the optimized path yet.
641641
.stage2_spirv64 => false,
642642
// The RISC-V does not support vectors.
643643
.stage2_riscv64 => false,
644-
else => true,
644+
// The naive memory comparison implementation is more useful for fuzzers to
645+
// find interesting inputs.
646+
else => !builtin.fuzz,
645647
};
646648

647649
/// Compares two slices and returns whether they are equal.
648650
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
649651
if (@sizeOf(T) == 0) return true;
650-
if (!@inComptime() and std.meta.hasUniqueRepresentation(T) and backend_can_use_eql_bytes) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
652+
if (!@inComptime() and std.meta.hasUniqueRepresentation(T) and eqlBytes_allowed) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
651653

652654
if (a.len != b.len) return false;
653655
if (a.len == 0 or a.ptr == b.ptr) return true;
@@ -660,9 +662,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
660662

661663
/// std.mem.eql heavily optimized for slices of bytes.
662664
fn eqlBytes(a: []const u8, b: []const u8) bool {
663-
if (!backend_can_use_eql_bytes) {
664-
return eql(u8, a, b);
665-
}
665+
comptime assert(eqlBytes_allowed);
666666

667667
if (a.len != b.len) return false;
668668
if (a.len == 0 or a.ptr == b.ptr) return true;

lib/std/os/linux/start_pie.zig

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ fn getDynamicSymbol() [*]elf.Dyn {
7171

7272
pub fn relocate(phdrs: []elf.Phdr) void {
7373
@setRuntimeSafety(false);
74+
@disableInstrumentation();
7475

7576
const dynv = getDynamicSymbol();
7677
// Recover the delta applied by the loader by comparing the effective and

lib/std/os/linux/tls.zig

+60-12
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ const TLSImage = struct {
110110
pub var tls_image: TLSImage = undefined;
111111

112112
pub fn setThreadPointer(addr: usize) void {
113+
@setRuntimeSafety(false);
114+
@disableInstrumentation();
113115
switch (native_arch) {
114116
.x86 => {
115117
var user_desc: linux.user_desc = .{
@@ -125,7 +127,7 @@ pub fn setThreadPointer(addr: usize) void {
125127
.useable = 1,
126128
},
127129
};
128-
const rc = linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
130+
const rc = @call(.always_inline, linux.syscall1, .{ .set_thread_area, @intFromPtr(&user_desc) });
129131
assert(rc == 0);
130132

131133
const gdt_entry_number = user_desc.entry_number;
@@ -138,7 +140,7 @@ pub fn setThreadPointer(addr: usize) void {
138140
);
139141
},
140142
.x86_64 => {
141-
const rc = linux.syscall2(.arch_prctl, linux.ARCH.SET_FS, addr);
143+
const rc = @call(.always_inline, linux.syscall2, .{ .arch_prctl, linux.ARCH.SET_FS, addr });
142144
assert(rc == 0);
143145
},
144146
.aarch64, .aarch64_be => {
@@ -149,7 +151,7 @@ pub fn setThreadPointer(addr: usize) void {
149151
);
150152
},
151153
.arm, .thumb => {
152-
const rc = linux.syscall1(.set_tls, addr);
154+
const rc = @call(.always_inline, linux.syscall1, .{ .set_tls, addr });
153155
assert(rc == 0);
154156
},
155157
.riscv64 => {
@@ -160,7 +162,7 @@ pub fn setThreadPointer(addr: usize) void {
160162
);
161163
},
162164
.mips, .mipsel, .mips64, .mips64el => {
163-
const rc = linux.syscall1(.set_thread_area, addr);
165+
const rc = @call(.always_inline, linux.syscall1, .{ .set_thread_area, addr });
164166
assert(rc == 0);
165167
},
166168
.powerpc, .powerpcle => {
@@ -189,6 +191,9 @@ pub fn setThreadPointer(addr: usize) void {
189191
}
190192

191193
fn initTLS(phdrs: []elf.Phdr) void {
194+
@setRuntimeSafety(false);
195+
@disableInstrumentation();
196+
192197
var tls_phdr: ?*elf.Phdr = null;
193198
var img_base: usize = 0;
194199

@@ -236,22 +241,22 @@ fn initTLS(phdrs: []elf.Phdr) void {
236241
l += tls_align_factor - delta;
237242
l += @sizeOf(CustomData);
238243
tcb_offset = l;
239-
l += mem.alignForward(usize, tls_tcb_size, tls_align_factor);
244+
l += alignForward(tls_tcb_size, tls_align_factor);
240245
data_offset = l;
241246
l += tls_data_alloc_size;
242247
break :blk l;
243248
},
244249
.VariantII => blk: {
245250
var l: usize = 0;
246251
data_offset = l;
247-
l += mem.alignForward(usize, tls_data_alloc_size, tls_align_factor);
252+
l += alignForward(tls_data_alloc_size, tls_align_factor);
248253
// The thread pointer is aligned to p_align
249254
tcb_offset = l;
250255
l += tls_tcb_size;
251256
// The CustomData structure is right after the TCB with no padding
252257
// in between so it can be easily found
253258
l += @sizeOf(CustomData);
254-
l = mem.alignForward(usize, l, @alignOf(DTV));
259+
l = alignForward(l, @alignOf(DTV));
255260
dtv_offset = l;
256261
l += @sizeOf(DTV);
257262
break :blk l;
@@ -270,13 +275,28 @@ fn initTLS(phdrs: []elf.Phdr) void {
270275
};
271276
}
272277

278+
/// Inline because TLS is not set up yet.
279+
inline fn alignForward(addr: usize, alignment: usize) usize {
280+
return alignBackward(addr + (alignment - 1), alignment);
281+
}
282+
283+
/// Inline because TLS is not set up yet.
284+
inline fn alignBackward(addr: usize, alignment: usize) usize {
285+
return addr & ~(alignment - 1);
286+
}
287+
288+
/// Inline because TLS is not set up yet.
273289
inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {
274290
return @ptrCast(@alignCast(ptr));
275291
}
276292

277293
/// Initializes all the fields of the static TLS area and returns the computed
278294
/// architecture-specific value of the thread-pointer register
295+
///
296+
/// This function is inline because thread local storage is not set up yet.
279297
pub fn prepareTLS(area: []u8) usize {
298+
@setRuntimeSafety(false);
299+
@disableInstrumentation();
280300
// Clear the area we're going to use, just to be safe
281301
@memset(area, 0);
282302
// Prepare the DTV
@@ -310,6 +330,9 @@ pub fn prepareTLS(area: []u8) usize {
310330
var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
311331

312332
pub fn initStaticTLS(phdrs: []elf.Phdr) void {
333+
@setRuntimeSafety(false);
334+
@disableInstrumentation();
335+
313336
initTLS(phdrs);
314337

315338
const tls_area = blk: {
@@ -321,22 +344,47 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {
321344
break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
322345
}
323346

324-
const alloc_tls_area = posix.mmap(
347+
const begin_addr = mmap(
325348
null,
326349
tls_image.alloc_size + tls_image.alloc_align - 1,
327350
posix.PROT.READ | posix.PROT.WRITE,
328351
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
329352
-1,
330353
0,
331-
) catch posix.abort();
354+
);
355+
if (@as(isize, @bitCast(begin_addr)) < 0) @trap();
356+
const alloc_tls_area: [*]align(mem.page_size) u8 = @ptrFromInt(begin_addr);
332357

333358
// Make sure the slice is correctly aligned.
334-
const begin_addr = @intFromPtr(alloc_tls_area.ptr);
335-
const begin_aligned_addr = mem.alignForward(usize, begin_addr, tls_image.alloc_align);
359+
const begin_aligned_addr = alignForward(begin_addr, tls_image.alloc_align);
336360
const start = begin_aligned_addr - begin_addr;
337-
break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
361+
break :blk alloc_tls_area[start..][0..tls_image.alloc_size];
338362
};
339363

340364
const tp_value = prepareTLS(tls_area);
341365
setThreadPointer(tp_value);
342366
}
367+
368+
inline fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: linux.MAP, fd: i32, offset: i64) usize {
369+
if (@hasField(linux.SYS, "mmap2")) {
370+
return @call(.always_inline, linux.syscall6, .{
371+
.mmap2,
372+
@intFromPtr(address),
373+
length,
374+
prot,
375+
@as(u32, @bitCast(flags)),
376+
@as(usize, @bitCast(@as(isize, fd))),
377+
@as(usize, @truncate(@as(u64, @bitCast(offset)) / linux.MMAP2_UNIT)),
378+
});
379+
} else {
380+
return @call(.always_inline, linux.syscall6, .{
381+
.mmap,
382+
@intFromPtr(address),
383+
length,
384+
prot,
385+
@as(u32, @bitCast(flags)),
386+
@as(usize, @bitCast(@as(isize, fd))),
387+
@as(u64, @bitCast(offset)),
388+
});
389+
}
390+
}

lib/std/start.zig

+6-2
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
411411
}
412412

413413
fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
414+
// We're not ready to panic until thread local storage is initialized.
415+
@setRuntimeSafety(false);
416+
// Code coverage instrumentation might try to use thread local variables.
417+
@disableInstrumentation();
414418
const argc = argc_argv_ptr[0];
415419
const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1));
416420

@@ -453,9 +457,9 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
453457
if (comptime native_arch.isARM()) {
454458
if (at_hwcap & std.os.linux.HWCAP.TLS == 0) {
455459
// FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
456-
// For the time being use a simple abort instead of a @panic call to
460+
// For the time being use a simple trap instead of a @panic call to
457461
// keep the binary bloat under control.
458-
std.posix.abort();
462+
@trap();
459463
}
460464
}
461465

lib/std/zig/AstGen.zig

+8-6
Original file line numberDiff line numberDiff line change
@@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28172817

28182818
.extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
28192819
.breakpoint,
2820+
.disable_instrumentation,
28202821
.fence,
28212822
.set_float_mode,
28222823
.set_align_stack,
@@ -9305,12 +9306,13 @@ fn builtinCall(
93059306
},
93069307

93079308
// zig fmt: off
9308-
.This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
9309-
.return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
9310-
.error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
9311-
.frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
9312-
.frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
9313-
.breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
9309+
.This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
9310+
.return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
9311+
.error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
9312+
.frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
9313+
.frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
9314+
.breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
9315+
.disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
93149316

93159317
.type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
93169318
.size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),

lib/std/zig/AstRlAnnotate.zig

+1
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
877877
.error_return_trace,
878878
.frame,
879879
.breakpoint,
880+
.disable_instrumentation,
880881
.in_comptime,
881882
.panic,
882883
.trap,

lib/std/zig/BuiltinFn.zig

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub const Tag = enum {
1515
int_from_bool,
1616
bit_size_of,
1717
breakpoint,
18+
disable_instrumentation,
1819
mul_add,
1920
byte_swap,
2021
bit_reverse,
@@ -263,6 +264,14 @@ pub const list = list: {
263264
.illegal_outside_function = true,
264265
},
265266
},
267+
.{
268+
"@disableInstrumentation",
269+
.{
270+
.tag = .disable_instrumentation,
271+
.param_count = 0,
272+
.illegal_outside_function = true,
273+
},
274+
},
266275
.{
267276
"@mulAdd",
268277
.{

0 commit comments

Comments
 (0)