authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-21 18:12:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-22 13:07:02-07:00
log54b7e144b126beb89e86dd8f3dc7ddc7a13871c9
treec85d2d46f22ffe935223adc3d5c44379438bd89b
parenteac7fd4da5992299a1f2fb59c5aa237c0c6c6761

initial support for integrated fuzzing

* Add the `-ffuzz` and `-fno-fuzz` CLI arguments. * Detect fuzz testing flags from zig cc. * Set the correct clang flags when fuzz testing is requested. It can be combined with TSAN and UBSAN. * Compilation: build fuzzer library when needed which is currently an empty zig file. * Add optforfuzzing to every function in the llvm backend for modules that have requested fuzzing. * In ZigLLVMTargetMachineEmitToFile, add the optimization passes for sanitizer coverage. * std.mem.eql uses a naive implementation optimized for fuzzing when builtin.fuzz is true. Tracked by #20702

11 files changed, 133 insertions(+), 53 deletions(-)

lib/fuzzer.zig created
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;
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+60-33
...@@ -190,6 +190,7 @@ debug_compile_errors: bool,...@@ -190,6 +190,7 @@ debug_compile_errors: bool,
190incremental: bool,190incremental: bool,
191job_queued_compiler_rt_lib: bool = false,191job_queued_compiler_rt_lib: bool = false,
192job_queued_compiler_rt_obj: bool = false,192job_queued_compiler_rt_obj: bool = false,
193job_queued_fuzzer_lib: bool = false,
193job_queued_update_builtin_zig: bool,194job_queued_update_builtin_zig: bool,
194alloc_failure_occurred: bool = false,195alloc_failure_occurred: bool = false,
195formatted_panics: bool = false,196formatted_panics: bool = false,
...@@ -231,6 +232,10 @@ compiler_rt_lib: ?CRTFile = null,...@@ -231,6 +232,10 @@ compiler_rt_lib: ?CRTFile = null,
231/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated232/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
232/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().233/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
233compiler_rt_obj: ?CRTFile = null,234compiler_rt_obj: ?CRTFile = null,
235/// Populated when we build the libfuzzer static library. A Job to build this
236/// is indicated by setting `job_queued_fuzzer_lib` and resolved before
237/// calling linker.flush().
238fuzzer_lib: ?CRTFile = null,
234239
235glibc_so_files: ?glibc.BuiltSharedObjects = null,240glibc_so_files: ?glibc.BuiltSharedObjects = null,
236wasi_emulated_libs: []const wasi_libc.CRTFile,241wasi_emulated_libs: []const wasi_libc.CRTFile,
...@@ -799,6 +804,7 @@ pub const MiscTask = enum {...@@ -799,6 +804,7 @@ pub const MiscTask = enum {
799 libcxx,804 libcxx,
800 libcxxabi,805 libcxxabi,
801 libtsan,806 libtsan,
807 libfuzzer,
802 wasi_libc_crt_file,808 wasi_libc_crt_file,
803 compiler_rt,809 compiler_rt,
804 zig_libc,810 zig_libc,
...@@ -887,6 +893,7 @@ pub const cache_helpers = struct {...@@ -887,6 +893,7 @@ pub const cache_helpers = struct {
887 hh.add(mod.red_zone);893 hh.add(mod.red_zone);
888 hh.add(mod.sanitize_c);894 hh.add(mod.sanitize_c);
889 hh.add(mod.sanitize_thread);895 hh.add(mod.sanitize_thread);
896 hh.add(mod.fuzz);
890 hh.add(mod.unwind_tables);897 hh.add(mod.unwind_tables);
891 hh.add(mod.structured_cfg);898 hh.add(mod.structured_cfg);
892 hh.addListOfBytes(mod.cc_argv);899 hh.addListOfBytes(mod.cc_argv);
...@@ -1302,6 +1309,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1302,6 +1309,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1302 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;1309 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;
1303 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;1310 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
1304 const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;1311 const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
1312 const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
13051313
1306 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;1314 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
1307 const build_id = options.build_id orelse .none;1315 const build_id = options.build_id orelse .none;
...@@ -1563,6 +1571,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1563,6 +1571,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1563 comp.config.any_unwind_tables = any_unwind_tables;1571 comp.config.any_unwind_tables = any_unwind_tables;
1564 comp.config.any_non_single_threaded = any_non_single_threaded;1572 comp.config.any_non_single_threaded = any_non_single_threaded;
1565 comp.config.any_sanitize_thread = any_sanitize_thread;1573 comp.config.any_sanitize_thread = any_sanitize_thread;
1574 comp.config.any_fuzz = any_fuzz;
15661575
1567 const lf_open_opts: link.File.OpenOptions = .{1576 const lf_open_opts: link.File.OpenOptions = .{
1568 .linker_script = options.linker_script,1577 .linker_script = options.linker_script,
...@@ -1908,6 +1917,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1908,6 +1917,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1908 }1917 }
1909 }1918 }
19101919
1920 if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
1921 if (is_exe_or_dyn_lib) {
1922 log.debug("queuing a job to build libfuzzer", .{});
1923 comp.job_queued_fuzzer_lib = true;
1924 }
1925 }
1926
1911 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and1927 if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
1912 !comp.config.link_libc and capable_of_building_zig_libc)1928 !comp.config.link_libc and capable_of_building_zig_libc)
1913 {1929 {
...@@ -1956,6 +1972,9 @@ pub fn destroy(comp: *Compilation) void {...@@ -1956,6 +1972,9 @@ pub fn destroy(comp: *Compilation) void {
1956 if (comp.compiler_rt_obj) |*crt_file| {1972 if (comp.compiler_rt_obj) |*crt_file| {
1957 crt_file.deinit(gpa);1973 crt_file.deinit(gpa);
1958 }1974 }
1975 if (comp.fuzzer_lib) |*crt_file| {
1976 crt_file.deinit(gpa);
1977 }
1959 if (comp.libc_static_lib) |*crt_file| {1978 if (comp.libc_static_lib) |*crt_file| {
1960 crt_file.deinit(gpa);1979 crt_file.deinit(gpa);
1961 }1980 }
...@@ -2721,6 +2740,7 @@ pub fn emitLlvmObject(...@@ -2721,6 +2740,7 @@ pub fn emitLlvmObject(
2721 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,2740 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
2722 .time_report = comp.time_report,2741 .time_report = comp.time_report,
2723 .sanitize_thread = comp.config.any_sanitize_thread,2742 .sanitize_thread = comp.config.any_sanitize_thread,
2743 .fuzz = comp.config.any_fuzz,
2724 .lto = comp.config.lto,2744 .lto = comp.config.lto,
2725 });2745 });
2726}2746}
...@@ -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,32 @@ pub fn addCCArgs(...@@ -5602,23 +5620,32 @@ 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 try argv.append("-fsanitize-trap=undefined");
5612 // `char*` vs `unsigned char*`5630 // It is very common, and well-defined, for a pointer on one side of a C ABI
5613 // Without this flag, Clang would invoke UBSAN when such an extern5631 // to have a different but compatible element type. Examples include:
5614 // function was called.5632 // `char*` vs `uint8_t*` on a system with 8-bit bytes
5615 try argv.append("-fno-sanitize=function");5633 // `const char*` vs `char*`
5616 } else if (mod.sanitize_c and mod.sanitize_thread) {5634 // `char*` vs `unsigned char*`
5617 try argv.append("-fsanitize=undefined,thread");5635 // Without this flag, Clang would invoke UBSAN when such an extern
5618 try argv.append("-fsanitize-trap=undefined");5636 // function was called.
5619 try argv.append("-fno-sanitize=function");5637 try argv.append("-fno-sanitize=function");
5620 } else if (!mod.sanitize_c and mod.sanitize_thread) {5638 }
5621 try argv.append("-fsanitize=thread");5639 if (mod.sanitize_thread) {
5640 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5641 try san_arg.appendSlice(arena, "thread,");
5642 }
5643 if (mod.fuzz) {
5644 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
5645 try san_arg.appendSlice(arena, "fuzzer-no-link,");
5646 }
5647 // Chop off the trailing comma and append to argv.
5648 if (san_arg.popOrNull()) |_| try argv.append(san_arg.items);
5622 }5649 }
56235650
5624 if (mod.red_zone) {5651 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/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/codegen/llvm.zig+6
...@@ -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,
...@@ -2982,6 +2985,9 @@ pub const Object = struct {...@@ -2982,6 +2985,9 @@ pub const Object = struct {
2982 if (owner_mod.sanitize_thread) {2985 if (owner_mod.sanitize_thread) {
2983 try attributes.addFnAttr(.sanitize_thread, &o.builder);2986 try attributes.addFnAttr(.sanitize_thread, &o.builder);
2984 }2987 }
2988 if (owner_mod.fuzz) {
2989 try attributes.addFnAttr(.optforfuzzing, &o.builder);
2990 }
2985 const target = owner_mod.resolved_target.result;2991 const target = owner_mod.resolved_target.result;
2986 if (target.cpu.model.llvm_name) |s| {2992 if (target.cpu.model.llvm_name) |s| {
2987 try attributes.addFnAttr(.{ .string = .{2993 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/main.zig+27-7
...@@ -499,12 +499,14 @@ const usage_build_generic =...@@ -499,12 +499,14 @@ const usage_build_generic =
499 \\ -fno-stack-check Disable stack probing in safe builds499 \\ -fno-stack-check Disable stack probing in safe builds
500 \\ -fstack-protector Enable stack protection in unsafe builds500 \\ -fstack-protector Enable stack protection in unsafe builds
501 \\ -fno-stack-protector Disable stack protection in safe builds501 \\ -fno-stack-protector Disable stack protection in safe builds
502 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
503 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
504 \\ -fvalgrind Include valgrind client requests in release builds502 \\ -fvalgrind Include valgrind client requests in release builds
505 \\ -fno-valgrind Omit valgrind client requests in debug builds503 \\ -fno-valgrind Omit valgrind client requests in debug builds
504 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
505 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
506 \\ -fsanitize-thread Enable Thread Sanitizer506 \\ -fsanitize-thread Enable Thread Sanitizer
507 \\ -fno-sanitize-thread Disable Thread Sanitizer507 \\ -fno-sanitize-thread Disable Thread Sanitizer
508 \\ -ffuzz Enable fuzz testing instrumentation
509 \\ -fno-fuzz Disable fuzz testing instrumentation
508 \\ -funwind-tables Always produce unwind table entries for all functions510 \\ -funwind-tables Always produce unwind table entries for all functions
509 \\ -fno-unwind-tables Never produce unwind table entries511 \\ -fno-unwind-tables Never produce unwind table entries
510 \\ -ferror-tracing Enable error tracing in ReleaseFast mode512 \\ -ferror-tracing Enable error tracing in ReleaseFast mode
...@@ -1429,6 +1431,10 @@ fn buildOutputType(...@@ -1429,6 +1431,10 @@ fn buildOutputType(
1429 mod_opts.sanitize_thread = true;1431 mod_opts.sanitize_thread = true;
1430 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {1432 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
1431 mod_opts.sanitize_thread = false;1433 mod_opts.sanitize_thread = false;
1434 } else if (mem.eql(u8, arg, "-ffuzz")) {
1435 mod_opts.fuzz = true;
1436 } else if (mem.eql(u8, arg, "-fno-fuzz")) {
1437 mod_opts.fuzz = false;
1432 } else if (mem.eql(u8, arg, "-fllvm")) {1438 } else if (mem.eql(u8, arg, "-fllvm")) {
1433 create_module.opts.use_llvm = true;1439 create_module.opts.use_llvm = true;
1434 } else if (mem.eql(u8, arg, "-fno-llvm")) {1440 } else if (mem.eql(u8, arg, "-fno-llvm")) {
...@@ -2060,11 +2066,21 @@ fn buildOutputType(...@@ -2060,11 +2066,21 @@ fn buildOutputType(
2060 create_module.opts.debug_format = .{ .dwarf = .@"64" };2066 create_module.opts.debug_format = .{ .dwarf = .@"64" };
2061 },2067 },
2062 .sanitize => {2068 .sanitize => {
2063 if (mem.eql(u8, it.only_arg, "undefined")) {2069 var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2064 mod_opts.sanitize_c = true;2070 var recognized_any = false;
2065 } else if (mem.eql(u8, it.only_arg, "thread")) {2071 while (san_it.next()) |sub_arg| {
2066 mod_opts.sanitize_thread = true;2072 if (mem.eql(u8, sub_arg, "undefined")) {
2067 } else {2073 mod_opts.sanitize_c = true;
2074 recognized_any = true;
2075 } else if (mem.eql(u8, sub_arg, "thread")) {
2076 mod_opts.sanitize_thread = true;
2077 recognized_any = true;
2078 } else if (mem.eql(u8, sub_arg, "fuzzer") or mem.eql(u8, sub_arg, "fuzzer-no-link")) {
2079 mod_opts.fuzz = true;
2080 recognized_any = true;
2081 }
2082 }
2083 if (!recognized_any) {
2068 try cc_argv.appendSlice(arena, it.other_args);2084 try cc_argv.appendSlice(arena, it.other_args);
2069 }2085 }
2070 },2086 },
...@@ -2642,6 +2658,8 @@ fn buildOutputType(...@@ -2642,6 +2658,8 @@ fn buildOutputType(
2642 create_module.opts.any_non_single_threaded = true;2658 create_module.opts.any_non_single_threaded = true;
2643 if (mod_opts.sanitize_thread == true)2659 if (mod_opts.sanitize_thread == true)
2644 create_module.opts.any_sanitize_thread = true;2660 create_module.opts.any_sanitize_thread = true;
2661 if (mod_opts.fuzz == true)
2662 create_module.opts.any_fuzz = true;
2645 if (mod_opts.unwind_tables == true)2663 if (mod_opts.unwind_tables == true)
2646 create_module.opts.any_unwind_tables = true;2664 create_module.opts.any_unwind_tables = true;
2647 if (mod_opts.strip == false)2665 if (mod_opts.strip == false)
...@@ -7491,6 +7509,8 @@ fn handleModArg(...@@ -7491,6 +7509,8 @@ fn handleModArg(
7491 create_module.opts.any_non_single_threaded = true;7509 create_module.opts.any_non_single_threaded = true;
7492 if (mod_opts.sanitize_thread == true)7510 if (mod_opts.sanitize_thread == true)
7493 create_module.opts.any_sanitize_thread = true;7511 create_module.opts.any_sanitize_thread = true;
7512 if (mod_opts.fuzz == true)
7513 create_module.opts.any_fuzz = true;
7494 if (mod_opts.unwind_tables == true)7514 if (mod_opts.unwind_tables == true)
7495 create_module.opts.any_unwind_tables = true;7515 create_module.opts.any_unwind_tables = true;
7496 if (mod_opts.strip == false)7516 if (mod_opts.strip == false)
src/zig_llvm.cpp+13-6
...@@ -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,10 @@ struct TimeTracerRAII {...@@ -188,9 +189,10 @@ struct TimeTracerRAII {
188};189};
189} // end anonymous namespace190} // end anonymous namespace
190191
192
191bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,193bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
192 char **error_message, bool is_debug,194 char **error_message, bool is_debug,
193 bool is_small, bool time_report, bool tsan, bool lto,195 bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
194 const char *asm_filename, const char *bin_filename,196 const char *asm_filename, const char *bin_filename,
195 const char *llvm_ir_filename, const char *bitcode_filename)197 const char *llvm_ir_filename, const char *bitcode_filename)
196{198{
...@@ -303,13 +305,18 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -303,13 +305,18 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
303 });305 });
304 }306 }
305307
306 // Thread sanitizer308 pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) {
307 if (tsan) {309 // Code coverage instrumentation.
308 pass_builder.registerOptimizerLastEPCallback([](ModulePassManager &module_pm, OptimizationLevel level) {310 if (sancov) {
311 module_pm.addPass(SanitizerCoveragePass());
312 }
313
314 // Thread sanitizer
315 if (tsan) {
309 module_pm.addPass(ModuleThreadSanitizerPass());316 module_pm.addPass(ModuleThreadSanitizerPass());
310 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));317 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
311 });318 }
312 }319 });
313320
314 ModulePassManager module_pm;321 ModulePassManager module_pm;
315 OptimizationLevel opt_level;322 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