authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-23 11:39:19-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-23 11:39:19-07:00
log6f3e9939d0389539570d4a7cad95b1e96bc8f0d4
treeabf951c8dc19393655df93f23a6911ce6fad660d
parent255547d7a6a1acee9c9b65d251ec4935433b6878
parent61ad1be6bd7d27f79773e7da891898449a45a80e
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20725 from ziglang/fuzz

initial support for integrated fuzzing

26 files changed, 406 insertions(+), 105 deletions(-)

lib/fuzzer.zig created+62
...@@ -0,0 +1,62 @@
1const std = @import("std");
2
3export threadlocal var __sancov_lowest_stack: usize = 0;
4
5export 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
9export 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
13export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {
14 handleCmp(@returnAddress(), arg1, arg2);
15}
16
17export fn __sanitizer_cov_trace_cmp1(arg1: u8, arg2: u8) void {
18 handleCmp(@returnAddress(), arg1, arg2);
19}
20
21export fn __sanitizer_cov_trace_const_cmp2(arg1: u16, arg2: u16) void {
22 handleCmp(@returnAddress(), arg1, arg2);
23}
24
25export fn __sanitizer_cov_trace_cmp2(arg1: u16, arg2: u16) void {
26 handleCmp(@returnAddress(), arg1, arg2);
27}
28
29export fn __sanitizer_cov_trace_const_cmp4(arg1: u32, arg2: u32) void {
30 handleCmp(@returnAddress(), arg1, arg2);
31}
32
33export fn __sanitizer_cov_trace_cmp4(arg1: u32, arg2: u32) void {
34 handleCmp(@returnAddress(), arg1, arg2);
35}
36
37export fn __sanitizer_cov_trace_const_cmp8(arg1: u64, arg2: u64) void {
38 handleCmp(@returnAddress(), arg1, arg2);
39}
40
41export fn __sanitizer_cov_trace_cmp8(arg1: u64, arg2: u64) void {
42 handleCmp(@returnAddress(), arg1, arg2);
43}
44
45export 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
55export 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
60fn 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
...@@ -28,6 +28,7 @@ stack_protector: ?bool,...@@ -28,6 +28,7 @@ stack_protector: ?bool,
28stack_check: ?bool,28stack_check: ?bool,
29sanitize_c: ?bool,29sanitize_c: ?bool,
30sanitize_thread: ?bool,30sanitize_thread: ?bool,
31fuzz: ?bool,
31code_model: std.builtin.CodeModel,32code_model: std.builtin.CodeModel,
32valgrind: ?bool,33valgrind: ?bool,
33pic: ?bool,34pic: ?bool,
...@@ -186,6 +187,7 @@ pub const CreateOptions = struct {...@@ -186,6 +187,7 @@ pub const CreateOptions = struct {
186 stack_check: ?bool = null,187 stack_check: ?bool = null,
187 sanitize_c: ?bool = null,188 sanitize_c: ?bool = null,
188 sanitize_thread: ?bool = null,189 sanitize_thread: ?bool = null,
190 fuzz: ?bool = null,
189 /// Whether to emit machine code that integrates with Valgrind.191 /// Whether to emit machine code that integrates with Valgrind.
190 valgrind: ?bool = null,192 valgrind: ?bool = null,
191 /// Position Independent Code193 /// Position Independent Code
...@@ -228,6 +230,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St...@@ -228,6 +230,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
228 .stack_check = options.stack_check,230 .stack_check = options.stack_check,
229 .sanitize_c = options.sanitize_c,231 .sanitize_c = options.sanitize_c,
230 .sanitize_thread = options.sanitize_thread,232 .sanitize_thread = options.sanitize_thread,
233 .fuzz = options.fuzz,
231 .code_model = options.code_model,234 .code_model = options.code_model,
232 .valgrind = options.valgrind,235 .valgrind = options.valgrind,
233 .pic = options.pic,236 .pic = options.pic,
...@@ -642,6 +645,7 @@ pub fn appendZigProcessFlags(...@@ -642,6 +645,7 @@ pub fn appendZigProcessFlags(
642 try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");645 try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
643 try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");646 try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");
644 try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");647 try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
648 try addFlag(zig_args, m.fuzz, "-ffuzz", "-fno-fuzz");
645 try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");649 try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
646 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");650 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
647 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");651 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
lib/std/mem.zig+6-6
...@@ -636,18 +636,20 @@ test lessThan {...@@ -636,18 +636,20 @@ test lessThan {
636 try testing.expect(lessThan(u8, "", "a"));636 try testing.expect(lessThan(u8, "", "a"));
637}637}
638638
639const backend_can_use_eql_bytes = switch (builtin.zig_backend) {639const eqlBytes_allowed = switch (builtin.zig_backend) {
640 // The SPIR-V backend does not support the optimized path yet.640 // The SPIR-V backend does not support the optimized path yet.
641 .stage2_spirv64 => false,641 .stage2_spirv64 => false,
642 // The RISC-V does not support vectors.642 // The RISC-V does not support vectors.
643 .stage2_riscv64 => false,643 .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,
645};647};
646648
647/// Compares two slices and returns whether they are equal.649/// Compares two slices and returns whether they are equal.
648pub fn eql(comptime T: type, a: []const T, b: []const T) bool {650pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
649 if (@sizeOf(T) == 0) return true;651 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
652 if (a.len != b.len) return false;654 if (a.len != b.len) return false;
653 if (a.len == 0 or a.ptr == b.ptr) return true;655 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 {...@@ -660,9 +662,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
660662
661/// std.mem.eql heavily optimized for slices of bytes.663/// std.mem.eql heavily optimized for slices of bytes.
662fn eqlBytes(a: []const u8, b: []const u8) bool {664fn eqlBytes(a: []const u8, b: []const u8) bool {
663 if (!backend_can_use_eql_bytes) {665 comptime assert(eqlBytes_allowed);
664 return eql(u8, a, b);
665 }
666666
667 if (a.len != b.len) return false;667 if (a.len != b.len) return false;
668 if (a.len == 0 or a.ptr == b.ptr) return true;668 if (a.len == 0 or a.ptr == b.ptr) return true;
lib/std/os/linux/start_pie.zig+1
...@@ -71,6 +71,7 @@ fn getDynamicSymbol() [*]elf.Dyn {...@@ -71,6 +71,7 @@ fn getDynamicSymbol() [*]elf.Dyn {
7171
72pub fn relocate(phdrs: []elf.Phdr) void {72pub fn relocate(phdrs: []elf.Phdr) void {
73 @setRuntimeSafety(false);73 @setRuntimeSafety(false);
74 @disableInstrumentation();
7475
75 const dynv = getDynamicSymbol();76 const dynv = getDynamicSymbol();
76 // Recover the delta applied by the loader by comparing the effective and77 // Recover the delta applied by the loader by comparing the effective and
lib/std/os/linux/tls.zig+60-12
...@@ -110,6 +110,8 @@ const TLSImage = struct {...@@ -110,6 +110,8 @@ const TLSImage = struct {
110pub var tls_image: TLSImage = undefined;110pub var tls_image: TLSImage = undefined;
111111
112pub fn setThreadPointer(addr: usize) void {112pub fn setThreadPointer(addr: usize) void {
113 @setRuntimeSafety(false);
114 @disableInstrumentation();
113 switch (native_arch) {115 switch (native_arch) {
114 .x86 => {116 .x86 => {
115 var user_desc: linux.user_desc = .{117 var user_desc: linux.user_desc = .{
...@@ -125,7 +127,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -125,7 +127,7 @@ pub fn setThreadPointer(addr: usize) void {
125 .useable = 1,127 .useable = 1,
126 },128 },
127 };129 };
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) });
129 assert(rc == 0);131 assert(rc == 0);
130132
131 const gdt_entry_number = user_desc.entry_number;133 const gdt_entry_number = user_desc.entry_number;
...@@ -138,7 +140,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -138,7 +140,7 @@ pub fn setThreadPointer(addr: usize) void {
138 );140 );
139 },141 },
140 .x86_64 => {142 .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 });
142 assert(rc == 0);144 assert(rc == 0);
143 },145 },
144 .aarch64, .aarch64_be => {146 .aarch64, .aarch64_be => {
...@@ -149,7 +151,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -149,7 +151,7 @@ pub fn setThreadPointer(addr: usize) void {
149 );151 );
150 },152 },
151 .arm, .thumb => {153 .arm, .thumb => {
152 const rc = linux.syscall1(.set_tls, addr);154 const rc = @call(.always_inline, linux.syscall1, .{ .set_tls, addr });
153 assert(rc == 0);155 assert(rc == 0);
154 },156 },
155 .riscv64 => {157 .riscv64 => {
...@@ -160,7 +162,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -160,7 +162,7 @@ pub fn setThreadPointer(addr: usize) void {
160 );162 );
161 },163 },
162 .mips, .mipsel, .mips64, .mips64el => {164 .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 });
164 assert(rc == 0);166 assert(rc == 0);
165 },167 },
166 .powerpc, .powerpcle => {168 .powerpc, .powerpcle => {
...@@ -189,6 +191,9 @@ pub fn setThreadPointer(addr: usize) void {...@@ -189,6 +191,9 @@ pub fn setThreadPointer(addr: usize) void {
189}191}
190192
191fn initTLS(phdrs: []elf.Phdr) void {193fn initTLS(phdrs: []elf.Phdr) void {
194 @setRuntimeSafety(false);
195 @disableInstrumentation();
196
192 var tls_phdr: ?*elf.Phdr = null;197 var tls_phdr: ?*elf.Phdr = null;
193 var img_base: usize = 0;198 var img_base: usize = 0;
194199
...@@ -236,7 +241,7 @@ fn initTLS(phdrs: []elf.Phdr) void {...@@ -236,7 +241,7 @@ fn initTLS(phdrs: []elf.Phdr) void {
236 l += tls_align_factor - delta;241 l += tls_align_factor - delta;
237 l += @sizeOf(CustomData);242 l += @sizeOf(CustomData);
238 tcb_offset = l;243 tcb_offset = l;
239 l += mem.alignForward(usize, tls_tcb_size, tls_align_factor);244 l += alignForward(tls_tcb_size, tls_align_factor);
240 data_offset = l;245 data_offset = l;
241 l += tls_data_alloc_size;246 l += tls_data_alloc_size;
242 break :blk l;247 break :blk l;
...@@ -244,14 +249,14 @@ fn initTLS(phdrs: []elf.Phdr) void {...@@ -244,14 +249,14 @@ fn initTLS(phdrs: []elf.Phdr) void {
244 .VariantII => blk: {249 .VariantII => blk: {
245 var l: usize = 0;250 var l: usize = 0;
246 data_offset = l;251 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);
248 // The thread pointer is aligned to p_align253 // The thread pointer is aligned to p_align
249 tcb_offset = l;254 tcb_offset = l;
250 l += tls_tcb_size;255 l += tls_tcb_size;
251 // The CustomData structure is right after the TCB with no padding256 // The CustomData structure is right after the TCB with no padding
252 // in between so it can be easily found257 // in between so it can be easily found
253 l += @sizeOf(CustomData);258 l += @sizeOf(CustomData);
254 l = mem.alignForward(usize, l, @alignOf(DTV));259 l = alignForward(l, @alignOf(DTV));
255 dtv_offset = l;260 dtv_offset = l;
256 l += @sizeOf(DTV);261 l += @sizeOf(DTV);
257 break :blk l;262 break :blk l;
...@@ -270,13 +275,28 @@ fn initTLS(phdrs: []elf.Phdr) void {...@@ -270,13 +275,28 @@ fn initTLS(phdrs: []elf.Phdr) void {
270 };275 };
271}276}
272277
278/// Inline because TLS is not set up yet.
279inline 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.
284inline fn alignBackward(addr: usize, alignment: usize) usize {
285 return addr & ~(alignment - 1);
286}
287
288/// Inline because TLS is not set up yet.
273inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {289inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {
274 return @ptrCast(@alignCast(ptr));290 return @ptrCast(@alignCast(ptr));
275}291}
276292
277/// Initializes all the fields of the static TLS area and returns the computed293/// Initializes all the fields of the static TLS area and returns the computed
278/// architecture-specific value of the thread-pointer register294/// architecture-specific value of the thread-pointer register
295///
296/// This function is inline because thread local storage is not set up yet.
279pub fn prepareTLS(area: []u8) usize {297pub fn prepareTLS(area: []u8) usize {
298 @setRuntimeSafety(false);
299 @disableInstrumentation();
280 // Clear the area we're going to use, just to be safe300 // Clear the area we're going to use, just to be safe
281 @memset(area, 0);301 @memset(area, 0);
282 // Prepare the DTV302 // Prepare the DTV
...@@ -310,6 +330,9 @@ pub fn prepareTLS(area: []u8) usize {...@@ -310,6 +330,9 @@ pub fn prepareTLS(area: []u8) usize {
310var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;330var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
311331
312pub fn initStaticTLS(phdrs: []elf.Phdr) void {332pub fn initStaticTLS(phdrs: []elf.Phdr) void {
333 @setRuntimeSafety(false);
334 @disableInstrumentation();
335
313 initTLS(phdrs);336 initTLS(phdrs);
314337
315 const tls_area = blk: {338 const tls_area = blk: {
...@@ -321,22 +344,47 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {...@@ -321,22 +344,47 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {
321 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];344 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
322 }345 }
323346
324 const alloc_tls_area = posix.mmap(347 const begin_addr = mmap(
325 null,348 null,
326 tls_image.alloc_size + tls_image.alloc_align - 1,349 tls_image.alloc_size + tls_image.alloc_align - 1,
327 posix.PROT.READ | posix.PROT.WRITE,350 posix.PROT.READ | posix.PROT.WRITE,
328 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },351 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
329 -1,352 -1,
330 0,353 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
333 // Make sure the slice is correctly aligned.358 // Make sure the slice is correctly aligned.
334 const begin_addr = @intFromPtr(alloc_tls_area.ptr);359 const begin_aligned_addr = alignForward(begin_addr, tls_image.alloc_align);
335 const begin_aligned_addr = mem.alignForward(usize, begin_addr, tls_image.alloc_align);
336 const start = begin_aligned_addr - begin_addr;360 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];
338 };362 };
339363
340 const tp_value = prepareTLS(tls_area);364 const tp_value = prepareTLS(tls_area);
341 setThreadPointer(tp_value);365 setThreadPointer(tp_value);
342}366}
367
368inline 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
...@@ -411,6 +411,10 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {...@@ -411,6 +411,10 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
411}411}
412412
413fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {413fn 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();
414 const argc = argc_argv_ptr[0];418 const argc = argc_argv_ptr[0];
415 const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1));419 const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1));
416420
...@@ -453,9 +457,9 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {...@@ -453,9 +457,9 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
453 if (comptime native_arch.isARM()) {457 if (comptime native_arch.isARM()) {
454 if (at_hwcap & std.os.linux.HWCAP.TLS == 0) {458 if (at_hwcap & std.os.linux.HWCAP.TLS == 0) {
455 // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls459 // 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 to460 // For the time being use a simple trap instead of a @panic call to
457 // keep the binary bloat under control.461 // keep the binary bloat under control.
458 std.posix.abort();462 @trap();
459 }463 }
460 }464 }
461465
lib/std/zig/AstGen.zig+8-6
...@@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28172817
2818 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {2818 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
2819 .breakpoint,2819 .breakpoint,
2820 .disable_instrumentation,
2820 .fence,2821 .fence,
2821 .set_float_mode,2822 .set_float_mode,
2822 .set_align_stack,2823 .set_align_stack,
...@@ -9305,12 +9306,13 @@ fn builtinCall(...@@ -9305,12 +9306,13 @@ fn builtinCall(
9305 },9306 },
93069307
9307 // zig fmt: off9308 // zig fmt: off
9308 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),9309 .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 .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 .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 => 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 .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),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
9315 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),9317 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
9316 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),9318 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
lib/std/zig/AstRlAnnotate.zig+1
...@@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
877 .error_return_trace,877 .error_return_trace,
878 .frame,878 .frame,
879 .breakpoint,879 .breakpoint,
880 .disable_instrumentation,
880 .in_comptime,881 .in_comptime,
881 .panic,882 .panic,
882 .trap,883 .trap,
lib/std/zig/BuiltinFn.zig+9
...@@ -15,6 +15,7 @@ pub const Tag = enum {...@@ -15,6 +15,7 @@ pub const Tag = enum {
15 int_from_bool,15 int_from_bool,
16 bit_size_of,16 bit_size_of,
17 breakpoint,17 breakpoint,
18 disable_instrumentation,
18 mul_add,19 mul_add,
19 byte_swap,20 byte_swap,
20 bit_reverse,21 bit_reverse,
...@@ -263,6 +264,14 @@ pub const list = list: {...@@ -263,6 +264,14 @@ pub const list = list: {
263 .illegal_outside_function = true,264 .illegal_outside_function = true,
264 },265 },
265 },266 },
267 .{
268 "@disableInstrumentation",
269 .{
270 .tag = .disable_instrumentation,
271 .param_count = 0,
272 .illegal_outside_function = true,
273 },
274 },
266 .{275 .{
267 "@mulAdd",276 "@mulAdd",
268 .{277 .{
lib/std/zig/Zir.zig+3-1
...@@ -1553,7 +1553,7 @@ pub const Inst = struct {...@@ -1553,7 +1553,7 @@ pub const Inst = struct {
1553 => false,1553 => false,
15541554
1555 .extended => switch (data.extended.opcode) {1555 .extended => switch (data.extended.opcode) {
1556 .fence, .set_cold, .breakpoint => true,1556 .fence, .set_cold, .breakpoint, .disable_instrumentation => true,
1557 else => false,1557 else => false,
1558 },1558 },
1559 };1559 };
...@@ -1973,6 +1973,8 @@ pub const Inst = struct {...@@ -1973,6 +1973,8 @@ pub const Inst = struct {
1973 /// Implements `@breakpoint`.1973 /// Implements `@breakpoint`.
1974 /// `operand` is `src_node: i32`.1974 /// `operand` is `src_node: i32`.
1975 breakpoint,1975 breakpoint,
1976 /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`.
1977 disable_instrumentation,
1976 /// Implements the `@select` builtin.1978 /// Implements the `@select` builtin.
1977 /// `operand` is payload index to `Select`.1979 /// `operand` is payload index to `Select`.
1978 select,1980 select,
src/Builtin.zig+3
...@@ -10,6 +10,7 @@ optimize_mode: std.builtin.OptimizeMode,...@@ -10,6 +10,7 @@ optimize_mode: std.builtin.OptimizeMode,
10error_tracing: bool,10error_tracing: bool,
11valgrind: bool,11valgrind: bool,
12sanitize_thread: bool,12sanitize_thread: bool,
13fuzz: bool,
13pic: bool,14pic: bool,
14pie: bool,15pie: bool,
15strip: bool,16strip: bool,
...@@ -185,6 +186,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -185,6 +186,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
185 \\pub const have_error_return_tracing = {};186 \\pub const have_error_return_tracing = {};
186 \\pub const valgrind_support = {};187 \\pub const valgrind_support = {};
187 \\pub const sanitize_thread = {};188 \\pub const sanitize_thread = {};
189 \\pub const fuzz = {};
188 \\pub const position_independent_code = {};190 \\pub const position_independent_code = {};
189 \\pub const position_independent_executable = {};191 \\pub const position_independent_executable = {};
190 \\pub const strip_debug_info = {};192 \\pub const strip_debug_info = {};
...@@ -199,6 +201,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -199,6 +201,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
199 opts.error_tracing,201 opts.error_tracing,
200 opts.valgrind,202 opts.valgrind,
201 opts.sanitize_thread,203 opts.sanitize_thread,
204 opts.fuzz,
202 opts.pic,205 opts.pic,
203 opts.pie,206 opts.pie,
204 opts.strip,207 opts.strip,
src/Compilation.zig+67-33
...@@ -191,6 +191,7 @@ debug_compile_errors: bool,...@@ -191,6 +191,7 @@ debug_compile_errors: bool,
191incremental: bool,191incremental: bool,
192job_queued_compiler_rt_lib: bool = false,192job_queued_compiler_rt_lib: bool = false,
193job_queued_compiler_rt_obj: bool = false,193job_queued_compiler_rt_obj: bool = false,
194job_queued_fuzzer_lib: bool = false,
194job_queued_update_builtin_zig: bool,195job_queued_update_builtin_zig: bool,
195alloc_failure_occurred: bool = false,196alloc_failure_occurred: bool = false,
196formatted_panics: bool = false,197formatted_panics: bool = false,
...@@ -232,6 +233,10 @@ compiler_rt_lib: ?CRTFile = null,...@@ -232,6 +233,10 @@ compiler_rt_lib: ?CRTFile = null,
232/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated233/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
233/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().234/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
234compiler_rt_obj: ?CRTFile = null,235compiler_rt_obj: ?CRTFile = null,
236/// Populated when we build the libfuzzer static library. A Job to build this
237/// is indicated by setting `job_queued_fuzzer_lib` and resolved before
238/// calling linker.flush().
239fuzzer_lib: ?CRTFile = null,
235240
236glibc_so_files: ?glibc.BuiltSharedObjects = null,241glibc_so_files: ?glibc.BuiltSharedObjects = null,
237wasi_emulated_libs: []const wasi_libc.CRTFile,242wasi_emulated_libs: []const wasi_libc.CRTFile,
...@@ -800,6 +805,7 @@ pub const MiscTask = enum {...@@ -800,6 +805,7 @@ pub const MiscTask = enum {
800 libcxx,805 libcxx,
801 libcxxabi,806 libcxxabi,
802 libtsan,807 libtsan,
808 libfuzzer,
803 wasi_libc_crt_file,809 wasi_libc_crt_file,
804 compiler_rt,810 compiler_rt,
805 zig_libc,811 zig_libc,
...@@ -888,6 +894,7 @@ pub const cache_helpers = struct {...@@ -888,6 +894,7 @@ pub const cache_helpers = struct {
888 hh.add(mod.red_zone);894 hh.add(mod.red_zone);
889 hh.add(mod.sanitize_c);895 hh.add(mod.sanitize_c);
890 hh.add(mod.sanitize_thread);896 hh.add(mod.sanitize_thread);
897 hh.add(mod.fuzz);
891 hh.add(mod.unwind_tables);898 hh.add(mod.unwind_tables);
892 hh.add(mod.structured_cfg);899 hh.add(mod.structured_cfg);
893 hh.addListOfBytes(mod.cc_argv);900 hh.addListOfBytes(mod.cc_argv);
...@@ -1303,6 +1310,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1303,6 +1310,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1303 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;1310 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;
1304 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;1311 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
1305 const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;1312 const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
1313 const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
13061314
1307 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;1315 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
1308 const build_id = options.build_id orelse .none;1316 const build_id = options.build_id orelse .none;
...@@ -1564,6 +1572,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1564,6 +1572,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1564 comp.config.any_unwind_tables = any_unwind_tables;1572 comp.config.any_unwind_tables = any_unwind_tables;
1565 comp.config.any_non_single_threaded = any_non_single_threaded;1573 comp.config.any_non_single_threaded = any_non_single_threaded;
1566 comp.config.any_sanitize_thread = any_sanitize_thread;1574 comp.config.any_sanitize_thread = any_sanitize_thread;
1575 comp.config.any_fuzz = any_fuzz;
15671576
1568 const lf_open_opts: link.File.OpenOptions = .{1577 const lf_open_opts: link.File.OpenOptions = .{
1569 .linker_script = options.linker_script,1578 .linker_script = options.linker_script,
...@@ -1909,6 +1918,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1909,6 +1918,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1909 }1918 }
1910 }1919 }
19111920
1921 if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
1922 if (is_exe_or_dyn_lib) {
1923 log.debug("queuing a job to build libfuzzer", .{});
1924 comp.job_queued_fuzzer_lib = true;
1925 }
1926 }
1927
1912 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and1928 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
1913 !comp.config.link_libc and capable_of_building_zig_libc)1929 !comp.config.link_libc and capable_of_building_zig_libc)
1914 {1930 {
...@@ -1957,6 +1973,9 @@ pub fn destroy(comp: *Compilation) void {...@@ -1957,6 +1973,9 @@ pub fn destroy(comp: *Compilation) void {
1957 if (comp.compiler_rt_obj) |*crt_file| {1973 if (comp.compiler_rt_obj) |*crt_file| {
1958 crt_file.deinit(gpa);1974 crt_file.deinit(gpa);
1959 }1975 }
1976 if (comp.fuzzer_lib) |*crt_file| {
1977 crt_file.deinit(gpa);
1978 }
1960 if (comp.libc_static_lib) |*crt_file| {1979 if (comp.libc_static_lib) |*crt_file| {
1961 crt_file.deinit(gpa);1980 crt_file.deinit(gpa);
1962 }1981 }
...@@ -2722,6 +2741,7 @@ pub fn emitLlvmObject(...@@ -2722,6 +2741,7 @@ pub fn emitLlvmObject(
2722 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,2741 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
2723 .time_report = comp.time_report,2742 .time_report = comp.time_report,
2724 .sanitize_thread = comp.config.any_sanitize_thread,2743 .sanitize_thread = comp.config.any_sanitize_thread,
2744 .fuzz = comp.config.any_fuzz,
2725 .lto = comp.config.lto,2745 .lto = comp.config.lto,
2726 });2746 });
2727}2747}
...@@ -3641,15 +3661,9 @@ fn performAllTheWorkInner(...@@ -3641,15 +3661,9 @@ fn performAllTheWorkInner(
3641 break;3661 break;
3642 }3662 }
36433663
3644 if (comp.job_queued_compiler_rt_lib) {3664 buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_lib, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node);
3645 comp.job_queued_compiler_rt_lib = false;3665 buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_obj, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node);
3646 buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node);3666 buildCompilerRtOneShot(comp, &comp.job_queued_fuzzer_lib, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node);
3647 }
3648
3649 if (comp.job_queued_compiler_rt_obj) {
3650 comp.job_queued_compiler_rt_obj = false;
3651 buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node);
3652 }
3653}3667}
36543668
3655const JobError = Allocator.Error;3669const JobError = Allocator.Error;
...@@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource(...@@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource(
46554669
4656fn buildCompilerRtOneShot(4670fn buildCompilerRtOneShot(
4657 comp: *Compilation,4671 comp: *Compilation,
4672 job_queued: *bool,
4673 root_source_name: []const u8,
4674 misc_task: MiscTask,
4658 output_mode: std.builtin.OutputMode,4675 output_mode: std.builtin.OutputMode,
4659 out: *?CRTFile,4676 out: *?CRTFile,
4660 prog_node: std.Progress.Node,4677 prog_node: std.Progress.Node,
4661) void {4678) void {
4679 if (!job_queued.*) return;
4680 job_queued.* = false;
4681
4662 comp.buildOutputFromZig(4682 comp.buildOutputFromZig(
4663 "compiler_rt.zig",4683 root_source_name,
4664 output_mode,4684 output_mode,
4665 out,4685 out,
4666 .compiler_rt,4686 misc_task,
4667 prog_node,4687 prog_node,
4668 ) catch |err| switch (err) {4688 ) catch |err| switch (err) {
4669 error.SubCompilationFailed => return, // error reported already4689 error.SubCompilationFailed => return, // error reported already
4670 else => comp.lockAndSetMiscFailure(4690 else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
4671 .compiler_rt,4691 @tagName(misc_task), @errorName(err),
4672 "unable to build compiler_rt: {s}",4692 }),
4673 .{@errorName(err)},
4674 ),
4675 };4693 };
4676}4694}
46774695
...@@ -5602,23 +5620,39 @@ pub fn addCCArgs(...@@ -5602,23 +5620,39 @@ pub fn addCCArgs(
5602 try argv.append("-mthumb");5620 try argv.append("-mthumb");
5603 }5621 }
56045622
5605 if (mod.sanitize_c and !mod.sanitize_thread) {5623 {
5606 try argv.append("-fsanitize=undefined");5624 var san_arg: std.ArrayListUnmanaged(u8) = .{};
5607 try argv.append("-fsanitize-trap=undefined");5625 const prefix = "-fsanitize=";
5608 // It is very common, and well-defined, for a pointer on one side of a C ABI5626 if (mod.sanitize_c) {
5609 // to have a different but compatible element type. Examples include:5627 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5610 // `char*` vs `uint8_t*` on a system with 8-bit bytes5628 try san_arg.appendSlice(arena, "undefined,");
5611 // `const char*` vs `char*`5629 }
5612 // `char*` vs `unsigned char*`5630 if (mod.sanitize_thread) {
5613 // Without this flag, Clang would invoke UBSAN when such an extern5631 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5614 // function was called.5632 try san_arg.appendSlice(arena, "thread,");
5615 try argv.append("-fno-sanitize=function");5633 }
5616 } else if (mod.sanitize_c and mod.sanitize_thread) {5634 if (mod.fuzz) {
5617 try argv.append("-fsanitize=undefined,thread");5635 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5618 try argv.append("-fsanitize-trap=undefined");5636 try san_arg.appendSlice(arena, "fuzzer-no-link,");
5619 try argv.append("-fno-sanitize=function");5637 }
5620 } else if (!mod.sanitize_c and mod.sanitize_thread) {5638 // Chop off the trailing comma and append to argv.
5621 try argv.append("-fsanitize=thread");5639 if (san_arg.popOrNull()) |_| {
5640 try argv.append(san_arg.items);
5641
5642 // These args have to be added after the `-fsanitize` arg or
5643 // they won't take effect.
5644 if (mod.sanitize_c) {
5645 try argv.append("-fsanitize-trap=undefined");
5646 // It is very common, and well-defined, for a pointer on one side of a C ABI
5647 // to have a different but compatible element type. Examples include:
5648 // `char*` vs `uint8_t*` on a system with 8-bit bytes
5649 // `const char*` vs `char*`
5650 // `char*` vs `unsigned char*`
5651 // Without this flag, Clang would invoke UBSAN when such an extern
5652 // function was called.
5653 try argv.append("-fno-sanitize=function");
5654 }
5655 }
5622 }5656 }
56235657
5624 if (mod.red_zone) {5658 if (mod.red_zone) {
src/Compilation/Config.zig+3
...@@ -32,6 +32,7 @@ any_non_single_threaded: bool,...@@ -32,6 +32,7 @@ any_non_single_threaded: bool,
32/// per-Module setting.32/// per-Module setting.
33any_error_tracing: bool,33any_error_tracing: bool,
34any_sanitize_thread: bool,34any_sanitize_thread: bool,
35any_fuzz: bool,
35pie: bool,36pie: bool,
36/// If this is true then linker code is responsible for making an LLVM IR37/// If this is true then linker code is responsible for making an LLVM IR
37/// Module, outputting it to an object file, and then linking that together38/// Module, outputting it to an object file, and then linking that together
...@@ -82,6 +83,7 @@ pub const Options = struct {...@@ -82,6 +83,7 @@ pub const Options = struct {
82 ensure_libcpp_on_non_freestanding: bool = false,83 ensure_libcpp_on_non_freestanding: bool = false,
83 any_non_single_threaded: bool = false,84 any_non_single_threaded: bool = false,
84 any_sanitize_thread: bool = false,85 any_sanitize_thread: bool = false,
86 any_fuzz: bool = false,
85 any_unwind_tables: bool = false,87 any_unwind_tables: bool = false,
86 any_dyn_libs: bool = false,88 any_dyn_libs: bool = false,
87 any_c_source_files: bool = false,89 any_c_source_files: bool = false,
...@@ -486,6 +488,7 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -486,6 +488,7 @@ pub fn resolve(options: Options) ResolveError!Config {
486 .any_non_single_threaded = options.any_non_single_threaded,488 .any_non_single_threaded = options.any_non_single_threaded,
487 .any_error_tracing = any_error_tracing,489 .any_error_tracing = any_error_tracing,
488 .any_sanitize_thread = options.any_sanitize_thread,490 .any_sanitize_thread = options.any_sanitize_thread,
491 .any_fuzz = options.any_fuzz,
489 .root_error_tracing = root_error_tracing,492 .root_error_tracing = root_error_tracing,
490 .pie = pie,493 .pie = pie,
491 .lto = lto,494 .lto = lto,
src/InternPool.zig+18-2
...@@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) {...@@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) {
5184 is_noinline: bool,5184 is_noinline: bool,
5185 calls_or_awaits_errorable_fn: bool,5185 calls_or_awaits_errorable_fn: bool,
5186 stack_alignment: Alignment,5186 stack_alignment: Alignment,
5187
5188 /// True if this function has an inferred error set.5187 /// True if this function has an inferred error set.
5189 inferred_error_set: bool,5188 inferred_error_set: bool,
5189 disable_instrumentation: bool,
51905190
5191 _: u14 = 0,5191 _: u13 = 0,
51925192
5193 pub const State = enum(u8) {5193 pub const State = enum(u8) {
5194 /// This function has not yet undergone analysis, because we have not5194 /// This function has not yet undergone analysis, because we have not
...@@ -8111,6 +8111,7 @@ pub fn getFuncDecl(...@@ -8111,6 +8111,7 @@ pub fn getFuncDecl(
8111 .calls_or_awaits_errorable_fn = false,8111 .calls_or_awaits_errorable_fn = false,
8112 .stack_alignment = .none,8112 .stack_alignment = .none,
8113 .inferred_error_set = false,8113 .inferred_error_set = false,
8114 .disable_instrumentation = false,
8114 },8115 },
8115 .owner_decl = key.owner_decl,8116 .owner_decl = key.owner_decl,
8116 .ty = key.ty,8117 .ty = key.ty,
...@@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes(...@@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes(
8214 .calls_or_awaits_errorable_fn = false,8215 .calls_or_awaits_errorable_fn = false,
8215 .stack_alignment = .none,8216 .stack_alignment = .none,
8216 .inferred_error_set = true,8217 .inferred_error_set = true,
8218 .disable_instrumentation = false,
8217 },8219 },
8218 .owner_decl = key.owner_decl,8220 .owner_decl = key.owner_decl,
8219 .ty = func_ty,8221 .ty = func_ty,
...@@ -8405,6 +8407,7 @@ pub fn getFuncInstance(...@@ -8405,6 +8407,7 @@ pub fn getFuncInstance(
8405 .calls_or_awaits_errorable_fn = false,8407 .calls_or_awaits_errorable_fn = false,
8406 .stack_alignment = .none,8408 .stack_alignment = .none,
8407 .inferred_error_set = false,8409 .inferred_error_set = false,
8410 .disable_instrumentation = false,
8408 },8411 },
8409 // This is populated after we create the Decl below. It is not read8412 // This is populated after we create the Decl below. It is not read
8410 // by equality or hashing functions.8413 // by equality or hashing functions.
...@@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes(...@@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes(
8504 .calls_or_awaits_errorable_fn = false,8507 .calls_or_awaits_errorable_fn = false,
8505 .stack_alignment = .none,8508 .stack_alignment = .none,
8506 .inferred_error_set = true,8509 .inferred_error_set = true,
8510 .disable_instrumentation = false,
8507 },8511 },
8508 // This is populated after we create the Decl below. It is not read8512 // This is populated after we create the Decl below. It is not read
8509 // by equality or hashing functions.8513 // by equality or hashing functions.
...@@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void {...@@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void {
11225 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);11229 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
11226}11230}
1122711231
11232pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
11233 const unwrapped_func = func.unwrap(ip);
11234 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
11235 extra_mutex.lock();
11236 defer extra_mutex.unlock();
11237
11238 const analysis_ptr = ip.funcAnalysisPtr(func);
11239 var analysis = analysis_ptr.*;
11240 analysis.disable_instrumentation = true;
11241 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
11242}
11243
11228pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {11244pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {
11229 const unwrapped_func = func.unwrap(ip);11245 const unwrapped_func = func.unwrap(ip);
11230 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;11246 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
src/Package/Module.zig+13
...@@ -26,6 +26,7 @@ stack_protector: u32,...@@ -26,6 +26,7 @@ stack_protector: u32,
26red_zone: bool,26red_zone: bool,
27sanitize_c: bool,27sanitize_c: bool,
28sanitize_thread: bool,28sanitize_thread: bool,
29fuzz: bool,
29unwind_tables: bool,30unwind_tables: bool,
30cc_argv: []const []const u8,31cc_argv: []const []const u8,
31/// (SPIR-V) whether to generate a structured control flow graph or not32/// (SPIR-V) whether to generate a structured control flow graph or not
...@@ -92,6 +93,7 @@ pub const CreateOptions = struct {...@@ -92,6 +93,7 @@ pub const CreateOptions = struct {
92 unwind_tables: ?bool = null,93 unwind_tables: ?bool = null,
93 sanitize_c: ?bool = null,94 sanitize_c: ?bool = null,
94 sanitize_thread: ?bool = null,95 sanitize_thread: ?bool = null,
96 fuzz: ?bool = null,
95 structured_cfg: ?bool = null,97 structured_cfg: ?bool = null,
96 };98 };
97};99};
...@@ -106,6 +108,7 @@ pub const ResolvedTarget = struct {...@@ -106,6 +108,7 @@ pub const ResolvedTarget = struct {
106/// At least one of `parent` and `resolved_target` must be non-null.108/// At least one of `parent` and `resolved_target` must be non-null.
107pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {109pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
108 if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);110 if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);
111 if (options.inherited.fuzz == true) assert(options.global.any_fuzz);
109 if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);112 if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);
110 if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables);113 if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables);
111 if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing);114 if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing);
...@@ -210,6 +213,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -210,6 +213,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
210 break :b false;213 break :b false;
211 };214 };
212215
216 const fuzz = b: {
217 if (options.inherited.fuzz) |x| break :b x;
218 if (options.parent) |p| break :b p.fuzz;
219 break :b false;
220 };
221
213 const code_model = b: {222 const code_model = b: {
214 if (options.inherited.code_model) |x| break :b x;223 if (options.inherited.code_model) |x| break :b x;
215 if (options.parent) |p| break :b p.code_model;224 if (options.parent) |p| break :b p.code_model;
...@@ -337,6 +346,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -337,6 +346,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
337 .red_zone = red_zone,346 .red_zone = red_zone,
338 .sanitize_c = sanitize_c,347 .sanitize_c = sanitize_c,
339 .sanitize_thread = sanitize_thread,348 .sanitize_thread = sanitize_thread,
349 .fuzz = fuzz,
340 .unwind_tables = unwind_tables,350 .unwind_tables = unwind_tables,
341 .cc_argv = options.cc_argv,351 .cc_argv = options.cc_argv,
342 .structured_cfg = structured_cfg,352 .structured_cfg = structured_cfg,
...@@ -359,6 +369,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -359,6 +369,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
359 .error_tracing = error_tracing,369 .error_tracing = error_tracing,
360 .valgrind = valgrind,370 .valgrind = valgrind,
361 .sanitize_thread = sanitize_thread,371 .sanitize_thread = sanitize_thread,
372 .fuzz = fuzz,
362 .pic = pic,373 .pic = pic,
363 .pie = options.global.pie,374 .pie = options.global.pie,
364 .strip = strip,375 .strip = strip,
...@@ -427,6 +438,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -427,6 +438,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
427 .red_zone = red_zone,438 .red_zone = red_zone,
428 .sanitize_c = sanitize_c,439 .sanitize_c = sanitize_c,
429 .sanitize_thread = sanitize_thread,440 .sanitize_thread = sanitize_thread,
441 .fuzz = fuzz,
430 .unwind_tables = unwind_tables,442 .unwind_tables = unwind_tables,
431 .cc_argv = &.{},443 .cc_argv = &.{},
432 .structured_cfg = structured_cfg,444 .structured_cfg = structured_cfg,
...@@ -485,6 +497,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P...@@ -485,6 +497,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
485 .red_zone = undefined,497 .red_zone = undefined,
486 .sanitize_c = undefined,498 .sanitize_c = undefined,
487 .sanitize_thread = undefined,499 .sanitize_thread = undefined,
500 .fuzz = undefined,
488 .unwind_tables = undefined,501 .unwind_tables = undefined,
489 .cc_argv = undefined,502 .cc_argv = undefined,
490 .structured_cfg = undefined,503 .structured_cfg = undefined,
src/Sema.zig+13
...@@ -1316,6 +1316,11 @@ fn analyzeBodyInner(...@@ -1316,6 +1316,11 @@ fn analyzeBodyInner(
1316 i += 1;1316 i += 1;
1317 continue;1317 continue;
1318 },1318 },
1319 .disable_instrumentation => {
1320 try sema.zirDisableInstrumentation();
1321 i += 1;
1322 continue;
1323 },
1319 .restore_err_ret_index => {1324 .restore_err_ret_index => {
1320 try sema.zirRestoreErrRetIndex(block, extended);1325 try sema.zirRestoreErrRetIndex(block, extended);
1321 i += 1;1326 i += 1;
...@@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)...@@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
6576 ip.funcSetCold(sema.func_index, is_cold);6581 ip.funcSetCold(sema.func_index, is_cold);
6577}6582}
65786583
6584fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
6585 const pt = sema.pt;
6586 const mod = pt.zcu;
6587 const ip = &mod.intern_pool;
6588 if (sema.func_index == .none) return; // does nothing outside a function
6589 ip.funcSetDisableInstrumentation(sema.func_index);
6590}
6591
6579fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {6592fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
6580 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;6593 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
6581 const src = block.builtinCallArgSrc(extra.node, 0);6594 const src = block.builtinCallArgSrc(extra.node, 0);
src/codegen/llvm.zig+22-3
...@@ -1101,6 +1101,7 @@ pub const Object = struct {...@@ -1101,6 +1101,7 @@ pub const Object = struct {
1101 is_small: bool,1101 is_small: bool,
1102 time_report: bool,1102 time_report: bool,
1103 sanitize_thread: bool,1103 sanitize_thread: bool,
1104 fuzz: bool,
1104 lto: bool,1105 lto: bool,
1105 };1106 };
11061107
...@@ -1287,6 +1288,7 @@ pub const Object = struct {...@@ -1287,6 +1288,7 @@ pub const Object = struct {
1287 options.is_small,1288 options.is_small,
1288 options.time_report,1289 options.time_report,
1289 options.sanitize_thread,1290 options.sanitize_thread,
1291 options.fuzz,
1290 options.lto,1292 options.lto,
1291 null,1293 null,
1292 emit_bin_path,1294 emit_bin_path,
...@@ -1311,6 +1313,7 @@ pub const Object = struct {...@@ -1311,6 +1313,7 @@ pub const Object = struct {
1311 options.is_small,1313 options.is_small,
1312 options.time_report,1314 options.time_report,
1313 options.sanitize_thread,1315 options.sanitize_thread,
1316 options.fuzz,
1314 options.lto,1317 options.lto,
1315 options.asm_path,1318 options.asm_path,
1316 emit_bin_path,1319 emit_bin_path,
...@@ -1380,6 +1383,25 @@ pub const Object = struct {...@@ -1380,6 +1383,25 @@ pub const Object = struct {
1380 _ = try attributes.removeFnAttr(.cold);1383 _ = try attributes.removeFnAttr(.cold);
1381 }1384 }
13821385
1386 if (owner_mod.sanitize_thread and !func_analysis.disable_instrumentation) {
1387 try attributes.addFnAttr(.sanitize_thread, &o.builder);
1388 } else {
1389 _ = try attributes.removeFnAttr(.sanitize_thread);
1390 }
1391 if (owner_mod.fuzz and !func_analysis.disable_instrumentation) {
1392 try attributes.addFnAttr(.optforfuzzing, &o.builder);
1393 if (comp.config.any_fuzz) {
1394 _ = try attributes.removeFnAttr(.skipprofile);
1395 _ = try attributes.removeFnAttr(.nosanitize_coverage);
1396 }
1397 } else {
1398 _ = try attributes.removeFnAttr(.optforfuzzing);
1399 if (comp.config.any_fuzz) {
1400 try attributes.addFnAttr(.skipprofile, &o.builder);
1401 try attributes.addFnAttr(.nosanitize_coverage, &o.builder);
1402 }
1403 }
1404
1383 // TODO: disable this if safety is off for the function scope1405 // TODO: disable this if safety is off for the function scope
1384 const ssp_buf_size = owner_mod.stack_protector;1406 const ssp_buf_size = owner_mod.stack_protector;
1385 if (ssp_buf_size != 0) {1407 if (ssp_buf_size != 0) {
...@@ -2979,9 +3001,6 @@ pub const Object = struct {...@@ -2979,9 +3001,6 @@ pub const Object = struct {
2979 try attributes.addFnAttr(.minsize, &o.builder);3001 try attributes.addFnAttr(.minsize, &o.builder);
2980 try attributes.addFnAttr(.optsize, &o.builder);3002 try attributes.addFnAttr(.optsize, &o.builder);
2981 }3003 }
2982 if (owner_mod.sanitize_thread) {
2983 try attributes.addFnAttr(.sanitize_thread, &o.builder);
2984 }
2985 const target = owner_mod.resolved_target.result;3004 const target = owner_mod.resolved_target.result;
2986 if (target.cpu.model.llvm_name) |s| {3005 if (target.cpu.model.llvm_name) |s| {
2987 try attributes.addFnAttr(.{ .string = .{3006 try attributes.addFnAttr(.{ .string = .{
src/codegen/llvm/bindings.zig+1
...@@ -93,6 +93,7 @@ pub const TargetMachine = opaque {...@@ -93,6 +93,7 @@ pub const TargetMachine = opaque {
93 is_small: bool,93 is_small: bool,
94 time_report: bool,94 time_report: bool,
95 tsan: bool,95 tsan: bool,
96 sancov: bool,
96 lto: bool,97 lto: bool,
97 asm_filename: ?[*:0]const u8,98 asm_filename: ?[*:0]const u8,
98 bin_filename: ?[*:0]const u8,99 bin_filename: ?[*:0]const u8,
src/link/Coff/lld.zig+4
...@@ -460,6 +460,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -460,6 +460,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
460 try argv.append(comp.libunwind_static_lib.?.full_object_path);460 try argv.append(comp.libunwind_static_lib.?.full_object_path);
461 }461 }
462462
463 if (comp.config.any_fuzz) {
464 try argv.append(comp.fuzzer_lib.?.full_object_path);
465 }
466
463 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {467 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
464 if (!comp.config.link_libc) {468 if (!comp.config.link_libc) {
465 if (comp.libc_static_lib) |lib| {469 if (comp.libc_static_lib) |lib| {
src/link/Elf.zig+13-1
...@@ -1144,11 +1144,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1144,11 +1144,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1144 _ = try rpath_table.put(rpath, {});1144 _ = try rpath_table.put(rpath, {});
1145 }1145 }
11461146
1147 // TSAN
1148 if (comp.config.any_sanitize_thread) {1147 if (comp.config.any_sanitize_thread) {
1149 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });1148 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
1150 }1149 }
11511150
1151 if (comp.config.any_fuzz) {
1152 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
1153 }
1154
1152 // libc1155 // libc
1153 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {1156 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
1154 if (comp.libc_static_lib) |lib| {1157 if (comp.libc_static_lib) |lib| {
...@@ -1607,6 +1610,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1607,6 +1610,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1607 try argv.append(comp.tsan_lib.?.full_object_path);1610 try argv.append(comp.tsan_lib.?.full_object_path);
1608 }1611 }
16091612
1613 if (comp.config.any_fuzz) {
1614 try argv.append(comp.fuzzer_lib.?.full_object_path);
1615 }
1616
1610 // libc1617 // libc
1611 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {1618 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
1612 if (comp.libc_static_lib) |lib| {1619 if (comp.libc_static_lib) |lib| {
...@@ -2272,6 +2279,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2272,6 +2279,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2272 man.hash.add(self.bind_global_refs_locally);2279 man.hash.add(self.bind_global_refs_locally);
2273 man.hash.add(self.compress_debug_sections);2280 man.hash.add(self.compress_debug_sections);
2274 man.hash.add(comp.config.any_sanitize_thread);2281 man.hash.add(comp.config.any_sanitize_thread);
2282 man.hash.add(comp.config.any_fuzz);
2275 man.hash.addOptionalBytes(comp.sysroot);2283 man.hash.addOptionalBytes(comp.sysroot);
22762284
2277 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.2285 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
...@@ -2616,6 +2624,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -2616,6 +2624,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2616 try argv.append(comp.tsan_lib.?.full_object_path);2624 try argv.append(comp.tsan_lib.?.full_object_path);
2617 }2625 }
26182626
2627 if (comp.config.any_fuzz) {
2628 try argv.append(comp.fuzzer_lib.?.full_object_path);
2629 }
2630
2619 // libc2631 // libc
2620 if (is_exe_or_dyn_lib and2632 if (is_exe_or_dyn_lib and
2621 !comp.skip_linker_dependencies and2633 !comp.skip_linker_dependencies and
src/link/MachO.zig+8-1
...@@ -387,11 +387,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -387,11 +387,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
387387
388 if (module_obj_path) |path| try positionals.append(.{ .path = path });388 if (module_obj_path) |path| try positionals.append(.{ .path = path });
389389
390 // TSAN
391 if (comp.config.any_sanitize_thread) {390 if (comp.config.any_sanitize_thread) {
392 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });391 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
393 }392 }
394393
394 if (comp.config.any_fuzz) {
395 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
396 }
397
395 for (positionals.items) |obj| {398 for (positionals.items) |obj| {
396 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
397 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),400 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
...@@ -725,6 +728,10 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -725,6 +728,10 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
725 try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." });728 try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." });
726 }729 }
727730
731 if (comp.config.any_fuzz) {
732 try argv.append(comp.fuzzer_lib.?.full_object_path);
733 }
734
728 for (self.lib_dirs) |lib_dir| {735 for (self.lib_dirs) |lib_dir| {
729 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});736 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
730 try argv.append(arg);737 try argv.append(arg);
src/main.zig+27-7
...@@ -502,12 +502,14 @@ const usage_build_generic =...@@ -502,12 +502,14 @@ const usage_build_generic =
502 \\ -fno-stack-check Disable stack probing in safe builds502 \\ -fno-stack-check Disable stack probing in safe builds
503 \\ -fstack-protector Enable stack protection in unsafe builds503 \\ -fstack-protector Enable stack protection in unsafe builds
504 \\ -fno-stack-protector Disable stack protection in safe builds504 \\ -fno-stack-protector Disable stack protection in safe builds
505 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
506 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
507 \\ -fvalgrind Include valgrind client requests in release builds505 \\ -fvalgrind Include valgrind client requests in release builds
508 \\ -fno-valgrind Omit valgrind client requests in debug builds506 \\ -fno-valgrind Omit valgrind client requests in debug builds
507 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
508 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
509 \\ -fsanitize-thread Enable Thread Sanitizer509 \\ -fsanitize-thread Enable Thread Sanitizer
510 \\ -fno-sanitize-thread Disable Thread Sanitizer510 \\ -fno-sanitize-thread Disable Thread Sanitizer
511 \\ -ffuzz Enable fuzz testing instrumentation
512 \\ -fno-fuzz Disable fuzz testing instrumentation
511 \\ -funwind-tables Always produce unwind table entries for all functions513 \\ -funwind-tables Always produce unwind table entries for all functions
512 \\ -fno-unwind-tables Never produce unwind table entries514 \\ -fno-unwind-tables Never produce unwind table entries
513 \\ -ferror-tracing Enable error tracing in ReleaseFast mode515 \\ -ferror-tracing Enable error tracing in ReleaseFast mode
...@@ -1432,6 +1434,10 @@ fn buildOutputType(...@@ -1432,6 +1434,10 @@ fn buildOutputType(
1432 mod_opts.sanitize_thread = true;1434 mod_opts.sanitize_thread = true;
1433 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {1435 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
1434 mod_opts.sanitize_thread = false;1436 mod_opts.sanitize_thread = false;
1437 } else if (mem.eql(u8, arg, "-ffuzz")) {
1438 mod_opts.fuzz = true;
1439 } else if (mem.eql(u8, arg, "-fno-fuzz")) {
1440 mod_opts.fuzz = false;
1435 } else if (mem.eql(u8, arg, "-fllvm")) {1441 } else if (mem.eql(u8, arg, "-fllvm")) {
1436 create_module.opts.use_llvm = true;1442 create_module.opts.use_llvm = true;
1437 } else if (mem.eql(u8, arg, "-fno-llvm")) {1443 } else if (mem.eql(u8, arg, "-fno-llvm")) {
...@@ -2063,11 +2069,21 @@ fn buildOutputType(...@@ -2063,11 +2069,21 @@ fn buildOutputType(
2063 create_module.opts.debug_format = .{ .dwarf = .@"64" };2069 create_module.opts.debug_format = .{ .dwarf = .@"64" };
2064 },2070 },
2065 .sanitize => {2071 .sanitize => {
2066 if (mem.eql(u8, it.only_arg, "undefined")) {2072 var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2067 mod_opts.sanitize_c = true;2073 var recognized_any = false;
2068 } else if (mem.eql(u8, it.only_arg, "thread")) {2074 while (san_it.next()) |sub_arg| {
2069 mod_opts.sanitize_thread = true;2075 if (mem.eql(u8, sub_arg, "undefined")) {
2070 } else {2076 mod_opts.sanitize_c = true;
2077 recognized_any = true;
2078 } else if (mem.eql(u8, sub_arg, "thread")) {
2079 mod_opts.sanitize_thread = true;
2080 recognized_any = true;
2081 } else if (mem.eql(u8, sub_arg, "fuzzer") or mem.eql(u8, sub_arg, "fuzzer-no-link")) {
2082 mod_opts.fuzz = true;
2083 recognized_any = true;
2084 }
2085 }
2086 if (!recognized_any) {
2071 try cc_argv.appendSlice(arena, it.other_args);2087 try cc_argv.appendSlice(arena, it.other_args);
2072 }2088 }
2073 },2089 },
...@@ -2645,6 +2661,8 @@ fn buildOutputType(...@@ -2645,6 +2661,8 @@ fn buildOutputType(
2645 create_module.opts.any_non_single_threaded = true;2661 create_module.opts.any_non_single_threaded = true;
2646 if (mod_opts.sanitize_thread == true)2662 if (mod_opts.sanitize_thread == true)
2647 create_module.opts.any_sanitize_thread = true;2663 create_module.opts.any_sanitize_thread = true;
2664 if (mod_opts.fuzz == true)
2665 create_module.opts.any_fuzz = true;
2648 if (mod_opts.unwind_tables == true)2666 if (mod_opts.unwind_tables == true)
2649 create_module.opts.any_unwind_tables = true;2667 create_module.opts.any_unwind_tables = true;
2650 if (mod_opts.strip == false)2668 if (mod_opts.strip == false)
...@@ -7494,6 +7512,8 @@ fn handleModArg(...@@ -7494,6 +7512,8 @@ fn handleModArg(
7494 create_module.opts.any_non_single_threaded = true;7512 create_module.opts.any_non_single_threaded = true;
7495 if (mod_opts.sanitize_thread == true)7513 if (mod_opts.sanitize_thread == true)
7496 create_module.opts.any_sanitize_thread = true;7514 create_module.opts.any_sanitize_thread = true;
7515 if (mod_opts.fuzz == true)
7516 create_module.opts.any_fuzz = true;
7497 if (mod_opts.unwind_tables == true)7517 if (mod_opts.unwind_tables == true)
7498 create_module.opts.any_unwind_tables = true;7518 create_module.opts.any_unwind_tables = true;
7499 if (mod_opts.strip == false)7519 if (mod_opts.strip == false)
src/print_zir.zig+1
...@@ -524,6 +524,7 @@ const Writer = struct {...@@ -524,6 +524,7 @@ const Writer = struct {
524 .frame,524 .frame,
525 .frame_address,525 .frame_address,
526 .breakpoint,526 .breakpoint,
527 .disable_instrumentation,
527 .c_va_start,528 .c_va_start,
528 .in_comptime,529 .in_comptime,
529 .value_placeholder,530 .value_placeholder,
src/zig_llvm.cpp+52-30
...@@ -54,6 +54,7 @@...@@ -54,6 +54,7 @@
54#include <llvm/Transforms/IPO.h>54#include <llvm/Transforms/IPO.h>
55#include <llvm/Transforms/IPO/AlwaysInliner.h>55#include <llvm/Transforms/IPO/AlwaysInliner.h>
56#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>56#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
57#include <llvm/Transforms/Instrumentation/SanitizerCoverage.h>
57#include <llvm/Transforms/Scalar.h>58#include <llvm/Transforms/Scalar.h>
58#include <llvm/Transforms/Utils.h>59#include <llvm/Transforms/Utils.h>
59#include <llvm/Transforms/Utils/AddDiscriminators.h>60#include <llvm/Transforms/Utils/AddDiscriminators.h>
...@@ -188,9 +189,31 @@ struct TimeTracerRAII {...@@ -188,9 +189,31 @@ struct TimeTracerRAII {
188};189};
189} // end anonymous namespace190} // end anonymous namespace
190191
192static SanitizerCoverageOptions getSanCovOptions(void) {
193 SanitizerCoverageOptions o;
194 o.CoverageType = SanitizerCoverageOptions::SCK_Edge;
195 o.IndirectCalls = true;
196 o.TraceBB = false;
197 o.TraceCmp = true;
198 o.TraceDiv = false;
199 o.TraceGep = false;
200 o.Use8bitCounters = false;
201 o.TracePC = false;
202 o.TracePCGuard = false;
203 o.Inline8bitCounters = true;
204 o.InlineBoolFlag = false;
205 o.PCTable = true;
206 o.NoPrune = false;
207 o.StackDepth = true;
208 o.TraceLoads = false;
209 o.TraceStores = false;
210 o.CollectControlFlow = false;
211 return o;
212}
213
191bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,214bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
192 char **error_message, bool is_debug,215 char **error_message, bool is_debug,
193 bool is_small, bool time_report, bool tsan, bool lto,216 bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
194 const char *asm_filename, const char *bin_filename,217 const char *asm_filename, const char *bin_filename,
195 const char *llvm_ir_filename, const char *bitcode_filename)218 const char *llvm_ir_filename, const char *bitcode_filename)
196{219{
...@@ -277,39 +300,38 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -277,39 +300,38 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
277 pass_builder.registerCGSCCAnalyses(cgscc_am);300 pass_builder.registerCGSCCAnalyses(cgscc_am);
278 pass_builder.registerFunctionAnalyses(function_am);301 pass_builder.registerFunctionAnalyses(function_am);
279 pass_builder.registerLoopAnalyses(loop_am);302 pass_builder.registerLoopAnalyses(loop_am);
280 pass_builder.crossRegisterProxies(loop_am, function_am,303 pass_builder.crossRegisterProxies(loop_am, function_am, cgscc_am, module_am);
281 cgscc_am, module_am);
282
283 // IR verification
284 if (assertions_on) {
285 // Verify the input
286 pass_builder.registerPipelineStartEPCallback(
287 [](ModulePassManager &module_pm, OptimizationLevel OL) {
288 module_pm.addPass(VerifierPass());
289 });
290 // Verify the output
291 pass_builder.registerOptimizerLastEPCallback(
292 [](ModulePassManager &module_pm, OptimizationLevel OL) {
293 module_pm.addPass(VerifierPass());
294 });
295 }
296304
297 // Passes specific for release build305 pass_builder.registerPipelineStartEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
298 if (!is_debug) {306 // Verify the input
299 pass_builder.registerPipelineStartEPCallback(307 if (assertions_on) {
300 [](ModulePassManager &module_pm, OptimizationLevel OL) {308 module_pm.addPass(VerifierPass());
301 module_pm.addPass(309 }
302 createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));310
303 });311 if (!is_debug) {
304 }312 module_pm.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
313 }
314 });
315
316 pass_builder.registerOptimizerEarlyEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
317 // Code coverage instrumentation.
318 if (sancov) {
319 module_pm.addPass(SanitizerCoveragePass(getSanCovOptions()));
320 }
305321
306 // Thread sanitizer322 // Thread sanitizer
307 if (tsan) {323 if (tsan) {
308 pass_builder.registerOptimizerLastEPCallback([](ModulePassManager &module_pm, OptimizationLevel level) {
309 module_pm.addPass(ModuleThreadSanitizerPass());324 module_pm.addPass(ModuleThreadSanitizerPass());
310 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));325 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
311 });326 }
312 }327 });
328
329 pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) {
330 // Verify the output
331 if (assertions_on) {
332 module_pm.addPass(VerifierPass());
333 }
334 });
313335
314 ModulePassManager module_pm;336 ModulePassManager module_pm;
315 OptimizationLevel opt_level;337 OptimizationLevel opt_level;
src/zig_llvm.h+1-1
...@@ -26,7 +26,7 @@...@@ -26,7 +26,7 @@
2626
27ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,27ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
28 char **error_message, bool is_debug,28 char **error_message, bool is_debug,
29 bool is_small, bool time_report, bool tsan, bool lto,29 bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
30 const char *asm_filename, const char *bin_filename,30 const char *asm_filename, const char *bin_filename,
31 const char *llvm_ir_filename, const char *bitcode_filename);31 const char *llvm_ir_filename, const char *bitcode_filename);
3232
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ