| 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 | 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 | 640 | // The SPIR-V backend does not support the optimized path yet. |
| 641 | 641 | .stage2_spirv64 => false, |
| 642 | 642 | // The RISC-V does not support vectors. |
| 643 | 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 | 649 | /// Compares two slices and returns whether they are equal. |
| 648 | 650 | pub fn eql(comptime T: type, a: []const T, b: []const T) bool { |
| 649 | 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 | 654 | if (a.len != b.len) return false; |
| 653 | 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 | 662 | |
| 661 | 663 | /// std.mem.eql heavily optimized for slices of bytes. |
| 662 | 664 | fn eqlBytes(a: []const u8, b: []const u8) bool { |
| 663 | if (!backend_can_use_eql_bytes) { | |
| 664 | return eql(u8, a, b); | |
| 665 | } | |
| 665 | comptime assert(eqlBytes_allowed); | |
| 666 | 666 | |
| 667 | 667 | if (a.len != b.len) return false; |
| 668 | 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 | 10 | error_tracing: bool, |
| 11 | 11 | valgrind: bool, |
| 12 | 12 | sanitize_thread: bool, |
| 13 | fuzz: bool, | |
| 13 | 14 | pic: bool, |
| 14 | 15 | pie: bool, |
| 15 | 16 | strip: bool, |
| ... | ... | @@ -185,6 +186,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 185 | 186 | \\pub const have_error_return_tracing = {}; |
| 186 | 187 | \\pub const valgrind_support = {}; |
| 187 | 188 | \\pub const sanitize_thread = {}; |
| 189 | \\pub const fuzz = {}; | |
| 188 | 190 | \\pub const position_independent_code = {}; |
| 189 | 191 | \\pub const position_independent_executable = {}; |
| 190 | 192 | \\pub const strip_debug_info = {}; |
| ... | ... | @@ -199,6 +201,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 199 | 201 | opts.error_tracing, |
| 200 | 202 | opts.valgrind, |
| 201 | 203 | opts.sanitize_thread, |
| 204 | opts.fuzz, | |
| 202 | 205 | opts.pic, |
| 203 | 206 | opts.pie, |
| 204 | 207 | opts.strip, |
src/Compilation.zig+60-33| ... | ... | @@ -190,6 +190,7 @@ debug_compile_errors: bool, |
| 190 | 190 | incremental: bool, |
| 191 | 191 | job_queued_compiler_rt_lib: bool = false, |
| 192 | 192 | job_queued_compiler_rt_obj: bool = false, |
| 193 | job_queued_fuzzer_lib: bool = false, | |
| 193 | 194 | job_queued_update_builtin_zig: bool, |
| 194 | 195 | alloc_failure_occurred: bool = false, |
| 195 | 196 | formatted_panics: bool = false, |
| ... | ... | @@ -231,6 +232,10 @@ compiler_rt_lib: ?CRTFile = null, |
| 231 | 232 | /// Populated when we build the compiler_rt_obj object. A Job to build this is indicated |
| 232 | 233 | /// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush(). |
| 233 | 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 | 240 | glibc_so_files: ?glibc.BuiltSharedObjects = null, |
| 236 | 241 | wasi_emulated_libs: []const wasi_libc.CRTFile, |
| ... | ... | @@ -799,6 +804,7 @@ pub const MiscTask = enum { |
| 799 | 804 | libcxx, |
| 800 | 805 | libcxxabi, |
| 801 | 806 | libtsan, |
| 807 | libfuzzer, | |
| 802 | 808 | wasi_libc_crt_file, |
| 803 | 809 | compiler_rt, |
| 804 | 810 | zig_libc, |
| ... | ... | @@ -887,6 +893,7 @@ pub const cache_helpers = struct { |
| 887 | 893 | hh.add(mod.red_zone); |
| 888 | 894 | hh.add(mod.sanitize_c); |
| 889 | 895 | hh.add(mod.sanitize_thread); |
| 896 | hh.add(mod.fuzz); | |
| 890 | 897 | hh.add(mod.unwind_tables); |
| 891 | 898 | hh.add(mod.structured_cfg); |
| 892 | 899 | hh.addListOfBytes(mod.cc_argv); |
| ... | ... | @@ -1302,6 +1309,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1302 | 1309 | const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables; |
| 1303 | 1310 | const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded; |
| 1304 | 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 | 1314 | const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; |
| 1307 | 1315 | const build_id = options.build_id orelse .none; |
| ... | ... | @@ -1563,6 +1571,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 1563 | 1571 | comp.config.any_unwind_tables = any_unwind_tables; |
| 1564 | 1572 | comp.config.any_non_single_threaded = any_non_single_threaded; |
| 1565 | 1573 | comp.config.any_sanitize_thread = any_sanitize_thread; |
| 1574 | comp.config.any_fuzz = any_fuzz; | |
| 1566 | 1575 | |
| 1567 | 1576 | const lf_open_opts: link.File.OpenOptions = .{ |
| 1568 | 1577 | .linker_script = options.linker_script, |
| ... | ... | @@ -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 | 1927 | if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and |
| 1912 | 1928 | !comp.config.link_libc and capable_of_building_zig_libc) |
| 1913 | 1929 | { |
| ... | ... | @@ -1956,6 +1972,9 @@ pub fn destroy(comp: *Compilation) void { |
| 1956 | 1972 | if (comp.compiler_rt_obj) |*crt_file| { |
| 1957 | 1973 | crt_file.deinit(gpa); |
| 1958 | 1974 | } |
| 1975 | if (comp.fuzzer_lib) |*crt_file| { | |
| 1976 | crt_file.deinit(gpa); | |
| 1977 | } | |
| 1959 | 1978 | if (comp.libc_static_lib) |*crt_file| { |
| 1960 | 1979 | crt_file.deinit(gpa); |
| 1961 | 1980 | } |
| ... | ... | @@ -2721,6 +2740,7 @@ pub fn emitLlvmObject( |
| 2721 | 2740 | .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, |
| 2722 | 2741 | .time_report = comp.time_report, |
| 2723 | 2742 | .sanitize_thread = comp.config.any_sanitize_thread, |
| 2743 | .fuzz = comp.config.any_fuzz, | |
| 2724 | 2744 | .lto = comp.config.lto, |
| 2725 | 2745 | }); |
| 2726 | 2746 | } |
| ... | ... | @@ -3641,15 +3661,9 @@ fn performAllTheWorkInner( |
| 3641 | 3661 | break; |
| 3642 | 3662 | } |
| 3643 | 3663 | |
| 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); | |
| 3653 | 3667 | } |
| 3654 | 3668 | |
| 3655 | 3669 | const JobError = Allocator.Error; |
| ... | ... | @@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource( |
| 4655 | 4669 | |
| 4656 | 4670 | fn buildCompilerRtOneShot( |
| 4657 | 4671 | comp: *Compilation, |
| 4672 | job_queued: *bool, | |
| 4673 | root_source_name: []const u8, | |
| 4674 | misc_task: MiscTask, | |
| 4658 | 4675 | output_mode: std.builtin.OutputMode, |
| 4659 | 4676 | out: *?CRTFile, |
| 4660 | 4677 | prog_node: std.Progress.Node, |
| 4661 | 4678 | ) void { |
| 4679 | if (!job_queued.*) return; | |
| 4680 | job_queued.* = false; | |
| 4681 | ||
| 4662 | 4682 | comp.buildOutputFromZig( |
| 4663 | "compiler_rt.zig", | |
| 4683 | root_source_name, | |
| 4664 | 4684 | output_mode, |
| 4665 | 4685 | out, |
| 4666 | .compiler_rt, | |
| 4686 | misc_task, | |
| 4667 | 4687 | prog_node, |
| 4668 | 4688 | ) catch |err| switch (err) { |
| 4669 | 4689 | 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 | }), | |
| 4675 | 4693 | }; |
| 4676 | 4694 | } |
| 4677 | 4695 | |
| ... | ... | @@ -5602,23 +5620,32 @@ pub fn addCCArgs( |
| 5602 | 5620 | try argv.append("-mthumb"); |
| 5603 | 5621 | } |
| 5604 | 5622 | |
| 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 | try argv.append("-fsanitize-trap=undefined"); | |
| 5630 | // It is very common, and well-defined, for a pointer on one side of a C ABI | |
| 5631 | // to have a different but compatible element type. Examples include: | |
| 5632 | // `char*` vs `uint8_t*` on a system with 8-bit bytes | |
| 5633 | // `const char*` vs `char*` | |
| 5634 | // `char*` vs `unsigned char*` | |
| 5635 | // Without this flag, Clang would invoke UBSAN when such an extern | |
| 5636 | // function was called. | |
| 5637 | try argv.append("-fno-sanitize=function"); | |
| 5638 | } | |
| 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 | 5651 | if (mod.red_zone) { |
src/Compilation/Config.zig+3| ... | ... | @@ -32,6 +32,7 @@ any_non_single_threaded: bool, |
| 32 | 32 | /// per-Module setting. |
| 33 | 33 | any_error_tracing: bool, |
| 34 | 34 | any_sanitize_thread: bool, |
| 35 | any_fuzz: bool, | |
| 35 | 36 | pie: bool, |
| 36 | 37 | /// If this is true then linker code is responsible for making an LLVM IR |
| 37 | 38 | /// Module, outputting it to an object file, and then linking that together |
| ... | ... | @@ -82,6 +83,7 @@ pub const Options = struct { |
| 82 | 83 | ensure_libcpp_on_non_freestanding: bool = false, |
| 83 | 84 | any_non_single_threaded: bool = false, |
| 84 | 85 | any_sanitize_thread: bool = false, |
| 86 | any_fuzz: bool = false, | |
| 85 | 87 | any_unwind_tables: bool = false, |
| 86 | 88 | any_dyn_libs: bool = false, |
| 87 | 89 | any_c_source_files: bool = false, |
| ... | ... | @@ -486,6 +488,7 @@ pub fn resolve(options: Options) ResolveError!Config { |
| 486 | 488 | .any_non_single_threaded = options.any_non_single_threaded, |
| 487 | 489 | .any_error_tracing = any_error_tracing, |
| 488 | 490 | .any_sanitize_thread = options.any_sanitize_thread, |
| 491 | .any_fuzz = options.any_fuzz, | |
| 489 | 492 | .root_error_tracing = root_error_tracing, |
| 490 | 493 | .pie = pie, |
| 491 | 494 | .lto = lto, |
src/Package/Module.zig+13| ... | ... | @@ -26,6 +26,7 @@ stack_protector: u32, |
| 26 | 26 | red_zone: bool, |
| 27 | 27 | sanitize_c: bool, |
| 28 | 28 | sanitize_thread: bool, |
| 29 | fuzz: bool, | |
| 29 | 30 | unwind_tables: bool, |
| 30 | 31 | cc_argv: []const []const u8, |
| 31 | 32 | /// (SPIR-V) whether to generate a structured control flow graph or not |
| ... | ... | @@ -92,6 +93,7 @@ pub const CreateOptions = struct { |
| 92 | 93 | unwind_tables: ?bool = null, |
| 93 | 94 | sanitize_c: ?bool = null, |
| 94 | 95 | sanitize_thread: ?bool = null, |
| 96 | fuzz: ?bool = null, | |
| 95 | 97 | structured_cfg: ?bool = null, |
| 96 | 98 | }; |
| 97 | 99 | }; |
| ... | ... | @@ -106,6 +108,7 @@ pub const ResolvedTarget = struct { |
| 106 | 108 | /// At least one of `parent` and `resolved_target` must be non-null. |
| 107 | 109 | pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 108 | 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 | 112 | if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded); |
| 110 | 113 | if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables); |
| 111 | 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 | 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 | 222 | const code_model = b: { |
| 214 | 223 | if (options.inherited.code_model) |x| break :b x; |
| 215 | 224 | if (options.parent) |p| break :b p.code_model; |
| ... | ... | @@ -337,6 +346,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 337 | 346 | .red_zone = red_zone, |
| 338 | 347 | .sanitize_c = sanitize_c, |
| 339 | 348 | .sanitize_thread = sanitize_thread, |
| 349 | .fuzz = fuzz, | |
| 340 | 350 | .unwind_tables = unwind_tables, |
| 341 | 351 | .cc_argv = options.cc_argv, |
| 342 | 352 | .structured_cfg = structured_cfg, |
| ... | ... | @@ -359,6 +369,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 359 | 369 | .error_tracing = error_tracing, |
| 360 | 370 | .valgrind = valgrind, |
| 361 | 371 | .sanitize_thread = sanitize_thread, |
| 372 | .fuzz = fuzz, | |
| 362 | 373 | .pic = pic, |
| 363 | 374 | .pie = options.global.pie, |
| 364 | 375 | .strip = strip, |
| ... | ... | @@ -427,6 +438,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 427 | 438 | .red_zone = red_zone, |
| 428 | 439 | .sanitize_c = sanitize_c, |
| 429 | 440 | .sanitize_thread = sanitize_thread, |
| 441 | .fuzz = fuzz, | |
| 430 | 442 | .unwind_tables = unwind_tables, |
| 431 | 443 | .cc_argv = &.{}, |
| 432 | 444 | .structured_cfg = structured_cfg, |
| ... | ... | @@ -485,6 +497,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P |
| 485 | 497 | .red_zone = undefined, |
| 486 | 498 | .sanitize_c = undefined, |
| 487 | 499 | .sanitize_thread = undefined, |
| 500 | .fuzz = undefined, | |
| 488 | 501 | .unwind_tables = undefined, |
| 489 | 502 | .cc_argv = undefined, |
| 490 | 503 | .structured_cfg = undefined, |
src/codegen/llvm.zig+6| ... | ... | @@ -1101,6 +1101,7 @@ pub const Object = struct { |
| 1101 | 1101 | is_small: bool, |
| 1102 | 1102 | time_report: bool, |
| 1103 | 1103 | sanitize_thread: bool, |
| 1104 | fuzz: bool, | |
| 1104 | 1105 | lto: bool, |
| 1105 | 1106 | }; |
| 1106 | 1107 | |
| ... | ... | @@ -1287,6 +1288,7 @@ pub const Object = struct { |
| 1287 | 1288 | options.is_small, |
| 1288 | 1289 | options.time_report, |
| 1289 | 1290 | options.sanitize_thread, |
| 1291 | options.fuzz, | |
| 1290 | 1292 | options.lto, |
| 1291 | 1293 | null, |
| 1292 | 1294 | emit_bin_path, |
| ... | ... | @@ -1311,6 +1313,7 @@ pub const Object = struct { |
| 1311 | 1313 | options.is_small, |
| 1312 | 1314 | options.time_report, |
| 1313 | 1315 | options.sanitize_thread, |
| 1316 | options.fuzz, | |
| 1314 | 1317 | options.lto, |
| 1315 | 1318 | options.asm_path, |
| 1316 | 1319 | emit_bin_path, |
| ... | ... | @@ -2982,6 +2985,9 @@ pub const Object = struct { |
| 2982 | 2985 | if (owner_mod.sanitize_thread) { |
| 2983 | 2986 | try attributes.addFnAttr(.sanitize_thread, &o.builder); |
| 2984 | 2987 | } |
| 2988 | if (owner_mod.fuzz) { | |
| 2989 | try attributes.addFnAttr(.optforfuzzing, &o.builder); | |
| 2990 | } | |
| 2985 | 2991 | const target = owner_mod.resolved_target.result; |
| 2986 | 2992 | if (target.cpu.model.llvm_name) |s| { |
| 2987 | 2993 | try attributes.addFnAttr(.{ .string = .{ |
src/codegen/llvm/bindings.zig+1| ... | ... | @@ -93,6 +93,7 @@ pub const TargetMachine = opaque { |
| 93 | 93 | is_small: bool, |
| 94 | 94 | time_report: bool, |
| 95 | 95 | tsan: bool, |
| 96 | sancov: bool, | |
| 96 | 97 | lto: bool, |
| 97 | 98 | asm_filename: ?[*:0]const u8, |
| 98 | 99 | bin_filename: ?[*:0]const u8, |
src/main.zig+27-7| ... | ... | @@ -499,12 +499,14 @@ const usage_build_generic = |
| 499 | 499 | \\ -fno-stack-check Disable stack probing in safe builds |
| 500 | 500 | \\ -fstack-protector Enable stack protection in unsafe builds |
| 501 | 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 | 502 | \\ -fvalgrind Include valgrind client requests in release builds |
| 505 | 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 | 506 | \\ -fsanitize-thread Enable Thread Sanitizer |
| 507 | 507 | \\ -fno-sanitize-thread Disable Thread Sanitizer |
| 508 | \\ -ffuzz Enable fuzz testing instrumentation | |
| 509 | \\ -fno-fuzz Disable fuzz testing instrumentation | |
| 508 | 510 | \\ -funwind-tables Always produce unwind table entries for all functions |
| 509 | 511 | \\ -fno-unwind-tables Never produce unwind table entries |
| 510 | 512 | \\ -ferror-tracing Enable error tracing in ReleaseFast mode |
| ... | ... | @@ -1429,6 +1431,10 @@ fn buildOutputType( |
| 1429 | 1431 | mod_opts.sanitize_thread = true; |
| 1430 | 1432 | } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) { |
| 1431 | 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 | 1438 | } else if (mem.eql(u8, arg, "-fllvm")) { |
| 1433 | 1439 | create_module.opts.use_llvm = true; |
| 1434 | 1440 | } else if (mem.eql(u8, arg, "-fno-llvm")) { |
| ... | ... | @@ -2060,11 +2066,21 @@ fn buildOutputType( |
| 2060 | 2066 | create_module.opts.debug_format = .{ .dwarf = .@"64" }; |
| 2061 | 2067 | }, |
| 2062 | 2068 | .sanitize => { |
| 2063 | if (mem.eql(u8, it.only_arg, "undefined")) { | |
| 2064 | mod_opts.sanitize_c = true; | |
| 2065 | } else if (mem.eql(u8, it.only_arg, "thread")) { | |
| 2066 | mod_opts.sanitize_thread = true; | |
| 2067 | } else { | |
| 2069 | var san_it = std.mem.splitScalar(u8, it.only_arg, ','); | |
| 2070 | var recognized_any = false; | |
| 2071 | while (san_it.next()) |sub_arg| { | |
| 2072 | if (mem.eql(u8, sub_arg, "undefined")) { | |
| 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 | 2084 | try cc_argv.appendSlice(arena, it.other_args); |
| 2069 | 2085 | } |
| 2070 | 2086 | }, |
| ... | ... | @@ -2642,6 +2658,8 @@ fn buildOutputType( |
| 2642 | 2658 | create_module.opts.any_non_single_threaded = true; |
| 2643 | 2659 | if (mod_opts.sanitize_thread == true) |
| 2644 | 2660 | create_module.opts.any_sanitize_thread = true; |
| 2661 | if (mod_opts.fuzz == true) | |
| 2662 | create_module.opts.any_fuzz = true; | |
| 2645 | 2663 | if (mod_opts.unwind_tables == true) |
| 2646 | 2664 | create_module.opts.any_unwind_tables = true; |
| 2647 | 2665 | if (mod_opts.strip == false) |
| ... | ... | @@ -7491,6 +7509,8 @@ fn handleModArg( |
| 7491 | 7509 | create_module.opts.any_non_single_threaded = true; |
| 7492 | 7510 | if (mod_opts.sanitize_thread == true) |
| 7493 | 7511 | create_module.opts.any_sanitize_thread = true; |
| 7512 | if (mod_opts.fuzz == true) | |
| 7513 | create_module.opts.any_fuzz = true; | |
| 7494 | 7514 | if (mod_opts.unwind_tables == true) |
| 7495 | 7515 | create_module.opts.any_unwind_tables = true; |
| 7496 | 7516 | if (mod_opts.strip == false) |
src/zig_llvm.cpp+13-6| ... | ... | @@ -54,6 +54,7 @@ |
| 54 | 54 | #include <llvm/Transforms/IPO.h> |
| 55 | 55 | #include <llvm/Transforms/IPO/AlwaysInliner.h> |
| 56 | 56 | #include <llvm/Transforms/Instrumentation/ThreadSanitizer.h> |
| 57 | #include <llvm/Transforms/Instrumentation/SanitizerCoverage.h> | |
| 57 | 58 | #include <llvm/Transforms/Scalar.h> |
| 58 | 59 | #include <llvm/Transforms/Utils.h> |
| 59 | 60 | #include <llvm/Transforms/Utils/AddDiscriminators.h> |
| ... | ... | @@ -188,9 +189,10 @@ struct TimeTracerRAII { |
| 188 | 189 | }; |
| 189 | 190 | } // end anonymous namespace |
| 190 | 191 | |
| 192 | ||
| 191 | 193 | bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, |
| 192 | 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 | 196 | const char *asm_filename, const char *bin_filename, |
| 195 | 197 | const char *llvm_ir_filename, const char *bitcode_filename) |
| 196 | 198 | { |
| ... | ... | @@ -303,13 +305,18 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM |
| 303 | 305 | }); |
| 304 | 306 | } |
| 305 | 307 | |
| 306 | // Thread sanitizer | |
| 307 | if (tsan) { | |
| 308 | pass_builder.registerOptimizerLastEPCallback([](ModulePassManager &module_pm, OptimizationLevel level) { | |
| 308 | pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) { | |
| 309 | // Code coverage instrumentation. | |
| 310 | if (sancov) { | |
| 311 | module_pm.addPass(SanitizerCoveragePass()); | |
| 312 | } | |
| 313 | ||
| 314 | // Thread sanitizer | |
| 315 | if (tsan) { | |
| 309 | 316 | module_pm.addPass(ModuleThreadSanitizerPass()); |
| 310 | 317 | module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); |
| 311 | }); | |
| 312 | } | |
| 318 | } | |
| 319 | }); | |
| 313 | 320 | |
| 314 | 321 | ModulePassManager module_pm; |
| 315 | 322 | OptimizationLevel opt_level; |
src/zig_llvm.h+1-1| ... | ... | @@ -26,7 +26,7 @@ |
| 26 | 26 | |
| 27 | 27 | ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, |
| 28 | 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 | 30 | const char *asm_filename, const char *bin_filename, |
| 31 | 31 | const char *llvm_ir_filename, const char *bitcode_filename); |
| 32 | 32 |