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,
2828stack_check: ?bool,
2929sanitize_c: ?bool,
3030sanitize_thread: ?bool,
31fuzz: ?bool,
3132code_model: std.builtin.CodeModel,
3233valgrind: ?bool,
3334pic: ?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
......@@ -636,18 +636,20 @@ test lessThan {
636636 try testing.expect(lessThan(u8, "", "a"));
637637}
638638
639const backend_can_use_eql_bytes = switch (builtin.zig_backend) {
639const 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.
648650pub 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.
662664fn 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
......@@ -71,6 +71,7 @@ fn getDynamicSymbol() [*]elf.Dyn {
7171
7272pub 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
......@@ -110,6 +110,8 @@ const TLSImage = struct {
110110pub var tls_image: TLSImage = undefined;
111111
112112pub 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
191193fn 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,7 +241,7 @@ 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;
......@@ -244,14 +249,14 @@ fn initTLS(phdrs: []elf.Phdr) void {
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.
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.
273289inline 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.
279297pub 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 {
310330var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
311331
312332pub 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
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 {
411411}
412412
413413fn 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
......@@ -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
......@@ -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
......@@ -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 .{
lib/std/zig/Zir.zig+3-1
......@@ -1553,7 +1553,7 @@ pub const Inst = struct {
15531553 => false,
15541554
15551555 .extended => switch (data.extended.opcode) {
1556 .fence, .set_cold, .breakpoint => true,
1556 .fence, .set_cold, .breakpoint, .disable_instrumentation => true,
15571557 else => false,
15581558 },
15591559 };
......@@ -1973,6 +1973,8 @@ pub const Inst = struct {
19731973 /// Implements `@breakpoint`.
19741974 /// `operand` is `src_node: i32`.
19751975 breakpoint,
1976 /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`.
1977 disable_instrumentation,
19761978 /// Implements the `@select` builtin.
19771979 /// `operand` is payload index to `Select`.
19781980 select,
src/Builtin.zig+3
......@@ -10,6 +10,7 @@ optimize_mode: std.builtin.OptimizeMode,
1010error_tracing: bool,
1111valgrind: bool,
1212sanitize_thread: bool,
13fuzz: bool,
1314pic: bool,
1415pie: bool,
1516strip: bool,
......@@ -185,6 +186,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
185186 \\pub const have_error_return_tracing = {};
186187 \\pub const valgrind_support = {};
187188 \\pub const sanitize_thread = {};
189 \\pub const fuzz = {};
188190 \\pub const position_independent_code = {};
189191 \\pub const position_independent_executable = {};
190192 \\pub const strip_debug_info = {};
......@@ -199,6 +201,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
199201 opts.error_tracing,
200202 opts.valgrind,
201203 opts.sanitize_thread,
204 opts.fuzz,
202205 opts.pic,
203206 opts.pie,
204207 opts.strip,
src/Compilation.zig+67-33
......@@ -191,6 +191,7 @@ debug_compile_errors: bool,
191191incremental: bool,
192192job_queued_compiler_rt_lib: bool = false,
193193job_queued_compiler_rt_obj: bool = false,
194job_queued_fuzzer_lib: bool = false,
194195job_queued_update_builtin_zig: bool,
195196alloc_failure_occurred: bool = false,
196197formatted_panics: bool = false,
......@@ -232,6 +233,10 @@ compiler_rt_lib: ?CRTFile = null,
232233/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
233234/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
234235compiler_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
236241glibc_so_files: ?glibc.BuiltSharedObjects = null,
237242wasi_emulated_libs: []const wasi_libc.CRTFile,
......@@ -800,6 +805,7 @@ pub const MiscTask = enum {
800805 libcxx,
801806 libcxxabi,
802807 libtsan,
808 libfuzzer,
803809 wasi_libc_crt_file,
804810 compiler_rt,
805811 zig_libc,
......@@ -888,6 +894,7 @@ pub const cache_helpers = struct {
888894 hh.add(mod.red_zone);
889895 hh.add(mod.sanitize_c);
890896 hh.add(mod.sanitize_thread);
897 hh.add(mod.fuzz);
891898 hh.add(mod.unwind_tables);
892899 hh.add(mod.structured_cfg);
893900 hh.addListOfBytes(mod.cc_argv);
......@@ -1303,6 +1310,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
13031310 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;
13041311 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
13051312 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
13071315 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
13081316 const build_id = options.build_id orelse .none;
......@@ -1564,6 +1572,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15641572 comp.config.any_unwind_tables = any_unwind_tables;
15651573 comp.config.any_non_single_threaded = any_non_single_threaded;
15661574 comp.config.any_sanitize_thread = any_sanitize_thread;
1575 comp.config.any_fuzz = any_fuzz;
15671576
15681577 const lf_open_opts: link.File.OpenOptions = .{
15691578 .linker_script = options.linker_script,
......@@ -1909,6 +1918,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
19091918 }
19101919 }
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
19121928 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
19131929 !comp.config.link_libc and capable_of_building_zig_libc)
19141930 {
......@@ -1957,6 +1973,9 @@ pub fn destroy(comp: *Compilation) void {
19571973 if (comp.compiler_rt_obj) |*crt_file| {
19581974 crt_file.deinit(gpa);
19591975 }
1976 if (comp.fuzzer_lib) |*crt_file| {
1977 crt_file.deinit(gpa);
1978 }
19601979 if (comp.libc_static_lib) |*crt_file| {
19611980 crt_file.deinit(gpa);
19621981 }
......@@ -2722,6 +2741,7 @@ pub fn emitLlvmObject(
27222741 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
27232742 .time_report = comp.time_report,
27242743 .sanitize_thread = comp.config.any_sanitize_thread,
2744 .fuzz = comp.config.any_fuzz,
27252745 .lto = comp.config.lto,
27262746 });
27272747}
......@@ -3641,15 +3661,9 @@ fn performAllTheWorkInner(
36413661 break;
36423662 }
36433663
3644 if (comp.job_queued_compiler_rt_lib) {
3645 comp.job_queued_compiler_rt_lib = false;
3646 buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_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 }
3664 buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_lib, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node);
3665 buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_obj, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node);
3666 buildCompilerRtOneShot(comp, &comp.job_queued_fuzzer_lib, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node);
36533667}
36543668
36553669const JobError = Allocator.Error;
......@@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource(
46554669
46564670fn buildCompilerRtOneShot(
46574671 comp: *Compilation,
4672 job_queued: *bool,
4673 root_source_name: []const u8,
4674 misc_task: MiscTask,
46584675 output_mode: std.builtin.OutputMode,
46594676 out: *?CRTFile,
46604677 prog_node: std.Progress.Node,
46614678) void {
4679 if (!job_queued.*) return;
4680 job_queued.* = false;
4681
46624682 comp.buildOutputFromZig(
4663 "compiler_rt.zig",
4683 root_source_name,
46644684 output_mode,
46654685 out,
4666 .compiler_rt,
4686 misc_task,
46674687 prog_node,
46684688 ) catch |err| switch (err) {
46694689 error.SubCompilationFailed => return, // error reported already
4670 else => comp.lockAndSetMiscFailure(
4671 .compiler_rt,
4672 "unable to build compiler_rt: {s}",
4673 .{@errorName(err)},
4674 ),
4690 else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
4691 @tagName(misc_task), @errorName(err),
4692 }),
46754693 };
46764694}
46774695
......@@ -5602,23 +5620,39 @@ pub fn addCCArgs(
56025620 try argv.append("-mthumb");
56035621 }
56045622
5605 if (mod.sanitize_c and !mod.sanitize_thread) {
5606 try argv.append("-fsanitize=undefined");
5607 try argv.append("-fsanitize-trap=undefined");
5608 // It is very common, and well-defined, for a pointer on one side of a C ABI
5609 // to have a different but compatible element type. Examples include:
5610 // `char*` vs `uint8_t*` on a system with 8-bit bytes
5611 // `const char*` vs `char*`
5612 // `char*` vs `unsigned char*`
5613 // Without this flag, Clang would invoke UBSAN when such an extern
5614 // function was called.
5615 try argv.append("-fno-sanitize=function");
5616 } else if (mod.sanitize_c and mod.sanitize_thread) {
5617 try argv.append("-fsanitize=undefined,thread");
5618 try argv.append("-fsanitize-trap=undefined");
5619 try argv.append("-fno-sanitize=function");
5620 } else if (!mod.sanitize_c and mod.sanitize_thread) {
5621 try argv.append("-fsanitize=thread");
5623 {
5624 var san_arg: std.ArrayListUnmanaged(u8) = .{};
5625 const prefix = "-fsanitize=";
5626 if (mod.sanitize_c) {
5627 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5628 try san_arg.appendSlice(arena, "undefined,");
5629 }
5630 if (mod.sanitize_thread) {
5631 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5632 try san_arg.appendSlice(arena, "thread,");
5633 }
5634 if (mod.fuzz) {
5635 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5636 try san_arg.appendSlice(arena, "fuzzer-no-link,");
5637 }
5638 // Chop off the trailing comma and append to argv.
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 }
56225656 }
56235657
56245658 if (mod.red_zone) {
src/Compilation/Config.zig+3
......@@ -32,6 +32,7 @@ any_non_single_threaded: bool,
3232/// per-Module setting.
3333any_error_tracing: bool,
3434any_sanitize_thread: bool,
35any_fuzz: bool,
3536pie: bool,
3637/// If this is true then linker code is responsible for making an LLVM IR
3738/// Module, outputting it to an object file, and then linking that together
......@@ -82,6 +83,7 @@ pub const Options = struct {
8283 ensure_libcpp_on_non_freestanding: bool = false,
8384 any_non_single_threaded: bool = false,
8485 any_sanitize_thread: bool = false,
86 any_fuzz: bool = false,
8587 any_unwind_tables: bool = false,
8688 any_dyn_libs: bool = false,
8789 any_c_source_files: bool = false,
......@@ -486,6 +488,7 @@ pub fn resolve(options: Options) ResolveError!Config {
486488 .any_non_single_threaded = options.any_non_single_threaded,
487489 .any_error_tracing = any_error_tracing,
488490 .any_sanitize_thread = options.any_sanitize_thread,
491 .any_fuzz = options.any_fuzz,
489492 .root_error_tracing = root_error_tracing,
490493 .pie = pie,
491494 .lto = lto,
src/InternPool.zig+18-2
......@@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) {
51845184 is_noinline: bool,
51855185 calls_or_awaits_errorable_fn: bool,
51865186 stack_alignment: Alignment,
5187
51885187 /// True if this function has an inferred error set.
51895188 inferred_error_set: bool,
5189 disable_instrumentation: bool,
51905190
5191 _: u14 = 0,
5191 _: u13 = 0,
51925192
51935193 pub const State = enum(u8) {
51945194 /// This function has not yet undergone analysis, because we have not
......@@ -8111,6 +8111,7 @@ pub fn getFuncDecl(
81118111 .calls_or_awaits_errorable_fn = false,
81128112 .stack_alignment = .none,
81138113 .inferred_error_set = false,
8114 .disable_instrumentation = false,
81148115 },
81158116 .owner_decl = key.owner_decl,
81168117 .ty = key.ty,
......@@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes(
82148215 .calls_or_awaits_errorable_fn = false,
82158216 .stack_alignment = .none,
82168217 .inferred_error_set = true,
8218 .disable_instrumentation = false,
82178219 },
82188220 .owner_decl = key.owner_decl,
82198221 .ty = func_ty,
......@@ -8405,6 +8407,7 @@ pub fn getFuncInstance(
84058407 .calls_or_awaits_errorable_fn = false,
84068408 .stack_alignment = .none,
84078409 .inferred_error_set = false,
8410 .disable_instrumentation = false,
84088411 },
84098412 // This is populated after we create the Decl below. It is not read
84108413 // by equality or hashing functions.
......@@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes(
85048507 .calls_or_awaits_errorable_fn = false,
85058508 .stack_alignment = .none,
85068509 .inferred_error_set = true,
8510 .disable_instrumentation = false,
85078511 },
85088512 // This is populated after we create the Decl below. It is not read
85098513 // by equality or hashing functions.
......@@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void {
1122511229 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
1122611230}
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
1122811244pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {
1122911245 const unwrapped_func = func.unwrap(ip);
1123011246 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
src/Package/Module.zig+13
......@@ -26,6 +26,7 @@ stack_protector: u32,
2626red_zone: bool,
2727sanitize_c: bool,
2828sanitize_thread: bool,
29fuzz: bool,
2930unwind_tables: bool,
3031cc_argv: []const []const u8,
3132/// (SPIR-V) whether to generate a structured control flow graph or not
......@@ -92,6 +93,7 @@ pub const CreateOptions = struct {
9293 unwind_tables: ?bool = null,
9394 sanitize_c: ?bool = null,
9495 sanitize_thread: ?bool = null,
96 fuzz: ?bool = null,
9597 structured_cfg: ?bool = null,
9698 };
9799};
......@@ -106,6 +108,7 @@ pub const ResolvedTarget = struct {
106108/// At least one of `parent` and `resolved_target` must be non-null.
107109pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
108110 if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);
111 if (options.inherited.fuzz == true) assert(options.global.any_fuzz);
109112 if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);
110113 if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables);
111114 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 {
210213 break :b false;
211214 };
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
213222 const code_model = b: {
214223 if (options.inherited.code_model) |x| break :b x;
215224 if (options.parent) |p| break :b p.code_model;
......@@ -337,6 +346,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
337346 .red_zone = red_zone,
338347 .sanitize_c = sanitize_c,
339348 .sanitize_thread = sanitize_thread,
349 .fuzz = fuzz,
340350 .unwind_tables = unwind_tables,
341351 .cc_argv = options.cc_argv,
342352 .structured_cfg = structured_cfg,
......@@ -359,6 +369,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
359369 .error_tracing = error_tracing,
360370 .valgrind = valgrind,
361371 .sanitize_thread = sanitize_thread,
372 .fuzz = fuzz,
362373 .pic = pic,
363374 .pie = options.global.pie,
364375 .strip = strip,
......@@ -427,6 +438,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
427438 .red_zone = red_zone,
428439 .sanitize_c = sanitize_c,
429440 .sanitize_thread = sanitize_thread,
441 .fuzz = fuzz,
430442 .unwind_tables = unwind_tables,
431443 .cc_argv = &.{},
432444 .structured_cfg = structured_cfg,
......@@ -485,6 +497,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
485497 .red_zone = undefined,
486498 .sanitize_c = undefined,
487499 .sanitize_thread = undefined,
500 .fuzz = undefined,
488501 .unwind_tables = undefined,
489502 .cc_argv = undefined,
490503 .structured_cfg = undefined,
src/Sema.zig+13
......@@ -1316,6 +1316,11 @@ fn analyzeBodyInner(
13161316 i += 1;
13171317 continue;
13181318 },
1319 .disable_instrumentation => {
1320 try sema.zirDisableInstrumentation();
1321 i += 1;
1322 continue;
1323 },
13191324 .restore_err_ret_index => {
13201325 try sema.zirRestoreErrRetIndex(block, extended);
13211326 i += 1;
......@@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
65766581 ip.funcSetCold(sema.func_index, is_cold);
65776582}
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
65796592fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
65806593 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
65816594 const src = block.builtinCallArgSrc(extra.node, 0);
src/codegen/llvm.zig+22-3
......@@ -1101,6 +1101,7 @@ pub const Object = struct {
11011101 is_small: bool,
11021102 time_report: bool,
11031103 sanitize_thread: bool,
1104 fuzz: bool,
11041105 lto: bool,
11051106 };
11061107
......@@ -1287,6 +1288,7 @@ pub const Object = struct {
12871288 options.is_small,
12881289 options.time_report,
12891290 options.sanitize_thread,
1291 options.fuzz,
12901292 options.lto,
12911293 null,
12921294 emit_bin_path,
......@@ -1311,6 +1313,7 @@ pub const Object = struct {
13111313 options.is_small,
13121314 options.time_report,
13131315 options.sanitize_thread,
1316 options.fuzz,
13141317 options.lto,
13151318 options.asm_path,
13161319 emit_bin_path,
......@@ -1380,6 +1383,25 @@ pub const Object = struct {
13801383 _ = try attributes.removeFnAttr(.cold);
13811384 }
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
13831405 // TODO: disable this if safety is off for the function scope
13841406 const ssp_buf_size = owner_mod.stack_protector;
13851407 if (ssp_buf_size != 0) {
......@@ -2979,9 +3001,6 @@ pub const Object = struct {
29793001 try attributes.addFnAttr(.minsize, &o.builder);
29803002 try attributes.addFnAttr(.optsize, &o.builder);
29813003 }
2982 if (owner_mod.sanitize_thread) {
2983 try attributes.addFnAttr(.sanitize_thread, &o.builder);
2984 }
29853004 const target = owner_mod.resolved_target.result;
29863005 if (target.cpu.model.llvm_name) |s| {
29873006 try attributes.addFnAttr(.{ .string = .{
src/codegen/llvm/bindings.zig+1
......@@ -93,6 +93,7 @@ pub const TargetMachine = opaque {
9393 is_small: bool,
9494 time_report: bool,
9595 tsan: bool,
96 sancov: bool,
9697 lto: bool,
9798 asm_filename: ?[*:0]const u8,
9899 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
460460 try argv.append(comp.libunwind_static_lib.?.full_object_path);
461461 }
462462
463 if (comp.config.any_fuzz) {
464 try argv.append(comp.fuzzer_lib.?.full_object_path);
465 }
466
463467 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
464468 if (!comp.config.link_libc) {
465469 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
11441144 _ = try rpath_table.put(rpath, {});
11451145 }
11461146
1147 // TSAN
11481147 if (comp.config.any_sanitize_thread) {
11491148 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
11501149 }
11511150
1151 if (comp.config.any_fuzz) {
1152 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
1153 }
1154
11521155 // libc
11531156 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
11541157 if (comp.libc_static_lib) |lib| {
......@@ -1607,6 +1610,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
16071610 try argv.append(comp.tsan_lib.?.full_object_path);
16081611 }
16091612
1613 if (comp.config.any_fuzz) {
1614 try argv.append(comp.fuzzer_lib.?.full_object_path);
1615 }
1616
16101617 // libc
16111618 if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
16121619 if (comp.libc_static_lib) |lib| {
......@@ -2272,6 +2279,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
22722279 man.hash.add(self.bind_global_refs_locally);
22732280 man.hash.add(self.compress_debug_sections);
22742281 man.hash.add(comp.config.any_sanitize_thread);
2282 man.hash.add(comp.config.any_fuzz);
22752283 man.hash.addOptionalBytes(comp.sysroot);
22762284
22772285 // 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
26162624 try argv.append(comp.tsan_lib.?.full_object_path);
26172625 }
26182626
2627 if (comp.config.any_fuzz) {
2628 try argv.append(comp.fuzzer_lib.?.full_object_path);
2629 }
2630
26192631 // libc
26202632 if (is_exe_or_dyn_lib and
26212633 !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
387387
388388 if (module_obj_path) |path| try positionals.append(.{ .path = path });
389389
390 // TSAN
391390 if (comp.config.any_sanitize_thread) {
392391 try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
393392 }
394393
394 if (comp.config.any_fuzz) {
395 try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
396 }
397
395398 for (positionals.items) |obj| {
396399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
397400 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 {
725728 try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." });
726729 }
727730
731 if (comp.config.any_fuzz) {
732 try argv.append(comp.fuzzer_lib.?.full_object_path);
733 }
734
728735 for (self.lib_dirs) |lib_dir| {
729736 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
730737 try argv.append(arg);
src/main.zig+27-7
......@@ -502,12 +502,14 @@ const usage_build_generic =
502502 \\ -fno-stack-check Disable stack probing in safe builds
503503 \\ -fstack-protector Enable stack protection in unsafe builds
504504 \\ -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
507505 \\ -fvalgrind Include valgrind client requests in release builds
508506 \\ -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
509509 \\ -fsanitize-thread Enable Thread Sanitizer
510510 \\ -fno-sanitize-thread Disable Thread Sanitizer
511 \\ -ffuzz Enable fuzz testing instrumentation
512 \\ -fno-fuzz Disable fuzz testing instrumentation
511513 \\ -funwind-tables Always produce unwind table entries for all functions
512514 \\ -fno-unwind-tables Never produce unwind table entries
513515 \\ -ferror-tracing Enable error tracing in ReleaseFast mode
......@@ -1432,6 +1434,10 @@ fn buildOutputType(
14321434 mod_opts.sanitize_thread = true;
14331435 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
14341436 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;
14351441 } else if (mem.eql(u8, arg, "-fllvm")) {
14361442 create_module.opts.use_llvm = true;
14371443 } else if (mem.eql(u8, arg, "-fno-llvm")) {
......@@ -2063,11 +2069,21 @@ fn buildOutputType(
20632069 create_module.opts.debug_format = .{ .dwarf = .@"64" };
20642070 },
20652071 .sanitize => {
2066 if (mem.eql(u8, it.only_arg, "undefined")) {
2067 mod_opts.sanitize_c = true;
2068 } else if (mem.eql(u8, it.only_arg, "thread")) {
2069 mod_opts.sanitize_thread = true;
2070 } else {
2072 var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2073 var recognized_any = false;
2074 while (san_it.next()) |sub_arg| {
2075 if (mem.eql(u8, sub_arg, "undefined")) {
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) {
20712087 try cc_argv.appendSlice(arena, it.other_args);
20722088 }
20732089 },
......@@ -2645,6 +2661,8 @@ fn buildOutputType(
26452661 create_module.opts.any_non_single_threaded = true;
26462662 if (mod_opts.sanitize_thread == true)
26472663 create_module.opts.any_sanitize_thread = true;
2664 if (mod_opts.fuzz == true)
2665 create_module.opts.any_fuzz = true;
26482666 if (mod_opts.unwind_tables == true)
26492667 create_module.opts.any_unwind_tables = true;
26502668 if (mod_opts.strip == false)
......@@ -7494,6 +7512,8 @@ fn handleModArg(
74947512 create_module.opts.any_non_single_threaded = true;
74957513 if (mod_opts.sanitize_thread == true)
74967514 create_module.opts.any_sanitize_thread = true;
7515 if (mod_opts.fuzz == true)
7516 create_module.opts.any_fuzz = true;
74977517 if (mod_opts.unwind_tables == true)
74987518 create_module.opts.any_unwind_tables = true;
74997519 if (mod_opts.strip == false)
src/print_zir.zig+1
......@@ -524,6 +524,7 @@ const Writer = struct {
524524 .frame,
525525 .frame_address,
526526 .breakpoint,
527 .disable_instrumentation,
527528 .c_va_start,
528529 .in_comptime,
529530 .value_placeholder,
src/zig_llvm.cpp+52-30
......@@ -54,6 +54,7 @@
5454#include <llvm/Transforms/IPO.h>
5555#include <llvm/Transforms/IPO/AlwaysInliner.h>
5656#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
57#include <llvm/Transforms/Instrumentation/SanitizerCoverage.h>
5758#include <llvm/Transforms/Scalar.h>
5859#include <llvm/Transforms/Utils.h>
5960#include <llvm/Transforms/Utils/AddDiscriminators.h>
......@@ -188,9 +189,31 @@ struct TimeTracerRAII {
188189};
189190} // 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
191214bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
192215 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,
194217 const char *asm_filename, const char *bin_filename,
195218 const char *llvm_ir_filename, const char *bitcode_filename)
196219{
......@@ -277,39 +300,38 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
277300 pass_builder.registerCGSCCAnalyses(cgscc_am);
278301 pass_builder.registerFunctionAnalyses(function_am);
279302 pass_builder.registerLoopAnalyses(loop_am);
280 pass_builder.crossRegisterProxies(loop_am, function_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 }
303 pass_builder.crossRegisterProxies(loop_am, function_am, cgscc_am, module_am);
296304
297 // Passes specific for release build
298 if (!is_debug) {
299 pass_builder.registerPipelineStartEPCallback(
300 [](ModulePassManager &module_pm, OptimizationLevel OL) {
301 module_pm.addPass(
302 createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
303 });
304 }
305 pass_builder.registerPipelineStartEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
306 // Verify the input
307 if (assertions_on) {
308 module_pm.addPass(VerifierPass());
309 }
310
311 if (!is_debug) {
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 sanitizer
307 if (tsan) {
308 pass_builder.registerOptimizerLastEPCallback([](ModulePassManager &module_pm, OptimizationLevel level) {
322 // Thread sanitizer
323 if (tsan) {
309324 module_pm.addPass(ModuleThreadSanitizerPass());
310325 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
311 });
312 }
326 }
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
314336 ModulePassManager module_pm;
315337 OptimizationLevel opt_level;
src/zig_llvm.h+1-1
......@@ -26,7 +26,7 @@
2626
2727ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
2828 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,
3030 const char *asm_filename, const char *bin_filename,
3131 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