| author | |
| committer | |
| log | 54b7e144b126beb89e86dd8f3dc7ddc7a13871c9 |
| tree | c85d2d46f22ffe935223adc3d5c44379438bd89b |
| parent | eac7fd4da5992299a1f2fb59c5aa237c0c6c6761 |
* 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 #2070211 files changed, 133 insertions(+), 53 deletions(-)
lib/fuzzer.zig createdlib/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 | } |
| 638 | 638 | ||
| 639 | const backend_can_use_eql_bytes = switch (builtin.zig_backend) { | 639 | const 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 | }; |
| 646 | 648 | ||
| 647 | /// Compares two slices and returns whether they are equal. | 649 | /// Compares two slices and returns whether they are equal. |
| 648 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { | 650 | pub 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)); |
| 651 | 653 | ||
| 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 { |
| 660 | 662 | ||
| 661 | /// std.mem.eql heavily optimized for slices of bytes. | 663 | /// std.mem.eql heavily optimized for slices of bytes. |
| 662 | fn eqlBytes(a: []const u8, b: []const u8) bool { | 664 | fn 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 | } | ||
| 666 | 666 | ||
| 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, |
| 10 | error_tracing: bool, | 10 | error_tracing: bool, |
| 11 | valgrind: bool, | 11 | valgrind: bool, |
| 12 | sanitize_thread: bool, | 12 | sanitize_thread: bool, |
| 13 | fuzz: bool, | ||
| 13 | pic: bool, | 14 | pic: bool, |
| 14 | pie: bool, | 15 | pie: bool, |
| 15 | strip: bool, | 16 | strip: 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, |
| 190 | incremental: bool, | 190 | incremental: bool, |
| 191 | job_queued_compiler_rt_lib: bool = false, | 191 | job_queued_compiler_rt_lib: bool = false, |
| 192 | job_queued_compiler_rt_obj: bool = false, | 192 | job_queued_compiler_rt_obj: bool = false, |
| 193 | job_queued_fuzzer_lib: bool = false, | ||
| 193 | job_queued_update_builtin_zig: bool, | 194 | job_queued_update_builtin_zig: bool, |
| 194 | alloc_failure_occurred: bool = false, | 195 | alloc_failure_occurred: bool = false, |
| 195 | formatted_panics: bool = false, | 196 | formatted_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 indicated | 232 | /// 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(). |
| 233 | compiler_rt_obj: ?CRTFile = null, | 234 | compiler_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(). | ||
| 238 | fuzzer_lib: ?CRTFile = null, | ||
| 234 | 239 | ||
| 235 | glibc_so_files: ?glibc.BuiltSharedObjects = null, | 240 | glibc_so_files: ?glibc.BuiltSharedObjects = null, |
| 236 | wasi_emulated_libs: []const wasi_libc.CRTFile, | 241 | wasi_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; | ||
| 1305 | 1313 | ||
| 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; | ||
| 1566 | 1575 | ||
| 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 | } |
| 1910 | 1919 | ||
| 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 and | 1927 | 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 | } |
| 3643 | 3663 | ||
| 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 | } |
| 3654 | 3668 | ||
| 3655 | const JobError = Allocator.Error; | 3669 | const JobError = Allocator.Error; |
| ... | @@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource( | ... | @@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource( |
| 4655 | 4669 | ||
| 4656 | fn buildCompilerRtOneShot( | 4670 | fn 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 already | 4689 | 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 | } |
| 4677 | 4695 | ||
| ... | @@ -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 | } |
| 5604 | 5622 | ||
| 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 ABI | 5626 | 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 bytes | 5628 | 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 extern | 5631 | // 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 | } |
| 5623 | 5650 | ||
| 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. |
| 33 | any_error_tracing: bool, | 33 | any_error_tracing: bool, |
| 34 | any_sanitize_thread: bool, | 34 | any_sanitize_thread: bool, |
| 35 | any_fuzz: bool, | ||
| 35 | pie: bool, | 36 | pie: bool, |
| 36 | /// If this is true then linker code is responsible for making an LLVM IR | 37 | /// 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 together | 38 | /// 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, |
| 26 | red_zone: bool, | 26 | red_zone: bool, |
| 27 | sanitize_c: bool, | 27 | sanitize_c: bool, |
| 28 | sanitize_thread: bool, | 28 | sanitize_thread: bool, |
| 29 | fuzz: bool, | ||
| 29 | unwind_tables: bool, | 30 | unwind_tables: bool, |
| 30 | cc_argv: []const []const u8, | 31 | cc_argv: []const []const u8, |
| 31 | /// (SPIR-V) whether to generate a structured control flow graph or not | 32 | /// (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. |
| 107 | pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { | 109 | pub 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 | }; |
| 212 | 215 | ||
| 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 | }; |
| 1106 | 1107 | ||
| ... | @@ -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 builds | 499 | \\ -fno-stack-check Disable stack probing in safe builds |
| 500 | \\ -fstack-protector Enable stack protection in unsafe builds | 500 | \\ -fstack-protector Enable stack protection in unsafe builds |
| 501 | \\ -fno-stack-protector Disable stack protection in safe builds | 501 | \\ -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 builds | 502 | \\ -fvalgrind Include valgrind client requests in release builds |
| 505 | \\ -fno-valgrind Omit valgrind client requests in debug builds | 503 | \\ -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 Sanitizer | 506 | \\ -fsanitize-thread Enable Thread Sanitizer |
| 507 | \\ -fno-sanitize-thread Disable Thread Sanitizer | 507 | \\ -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 functions | 510 | \\ -funwind-tables Always produce unwind table entries for all functions |
| 509 | \\ -fno-unwind-tables Never produce unwind table entries | 511 | \\ -fno-unwind-tables Never produce unwind table entries |
| 510 | \\ -ferror-tracing Enable error tracing in ReleaseFast mode | 512 | \\ -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 namespace | 190 | } // end anonymous namespace |
| 190 | 191 | ||
| 192 | |||
| 191 | bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, | 193 | bool 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 | } |
| 305 | 307 | ||
| 306 | // Thread sanitizer | 308 | 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 | }); |
| 313 | 320 | ||
| 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 @@ |
| 26 | 26 | ||
| 27 | ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, | 27 | ZIG_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); |
| 32 | 32 |