diff --git a/clang/docs/SanitizerCoverage.rst b/clang/docs/SanitizerCoverage.rst --- a/clang/docs/SanitizerCoverage.rst +++ b/clang/docs/SanitizerCoverage.rst @@ -312,11 +312,17 @@ // for every non-constant array index. void __sanitizer_cov_trace_gep(uintptr_t Idx); -Partially disabling instrumentation -=================================== +Disabling instrumentation with ``__attribute__((no_sanitize("coverage")))`` +=========================================================================== + +It is possible to disable coverage instrumentation for select functions via the +function attribute ``__attribute__((no_sanitize("coverage")))``. + +Disabling instrumentation without source modification +===================================================== It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the -functions in your target. +functions in your target without modifying source files. With ``-fsanitize-coverage-allowlist=allowlist.txt`` and ``-fsanitize-coverage-blocklist=blocklist.txt``, you can specify such a subset through the combination of an allowlist and a blocklist. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2897,6 +2897,10 @@ } return Mask; } + + bool hasCoverage() const { + return llvm::is_contained(sanitizers(), "coverage"); + } }]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2564,12 +2564,17 @@ let Content = [{ Use the ``no_sanitize`` attribute on a function or a global variable declaration to specify that a particular instrumentation or set of -instrumentations should not be applied. The attribute takes a list of -string literals, which have the same meaning as values accepted by the -``-fno-sanitize=`` flag. For example, -``__attribute__((no_sanitize("address", "thread")))`` specifies that -AddressSanitizer and ThreadSanitizer should not be applied to the -function or variable. +instrumentations should not be applied. + +The attribute takes a list of string literals with the following accepted +values: +* all values accepted by ``-fno-sanitize=``; +* ``coverage``, to disable SanitizerCoverage instrumentation. + +For example, ``__attribute__((no_sanitize("address", "thread")))`` specifies +that AddressSanitizer and ThreadSanitizer should not be applied to the function +or variable. Using ``__attribute__((no_sanitize("coverage")))`` specifies that +SanitizerCoverage should not be applied to the function. See :ref:`Controlling Code Generation ` for a full list of supported sanitizer flags. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -734,8 +734,10 @@ } while (0); if (D) { - // Apply the no_sanitize* attributes to SanOpts. + bool NoSanitizeCoverage = false; + for (auto Attr : D->specific_attrs()) { + // Apply the no_sanitize* attributes to SanOpts. SanitizerMask mask = Attr->getMask(); SanOpts.Mask &= ~mask; if (mask & SanitizerKind::Address) @@ -746,7 +748,14 @@ SanOpts.set(SanitizerKind::KernelHWAddress, false); if (mask & SanitizerKind::KernelHWAddress) SanOpts.set(SanitizerKind::HWAddress, false); + + // SanitizeCoverage is not handled by SanOpts. + if (Attr->hasCoverage()) + NoSanitizeCoverage = true; } + + if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage()) + Fn->addFnAttr(llvm::Attribute::NoSanitizeCoverage); } // Apply sanitizer attributes to the function. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7290,7 +7290,8 @@ return; if (parseSanitizerValue(SanitizerName, /*AllowGroups=*/true) == - SanitizerMask()) + SanitizerMask() && + SanitizerName != "coverage") S.Diag(LiteralLoc, diag::warn_unknown_sanitizer_ignored) << SanitizerName; else if (isGlobalVar(D) && SanitizerName != "address") S.Diag(D->getLocation(), diag::err_attribute_wrong_decl_type) diff --git a/clang/test/CodeGen/sanitize-coverage.c b/clang/test/CodeGen/sanitize-coverage.c --- a/clang/test/CodeGen/sanitize-coverage.c +++ b/clang/test/CodeGen/sanitize-coverage.c @@ -31,4 +31,59 @@ always_inlined_fn(n); } +// CHECK-LABEL: define dso_local void @test_no_sanitize_coverage( +__attribute__((no_sanitize("coverage"))) void test_no_sanitize_coverage(int n) { + // CHECK-NOT: call void @__sanitizer_cov_trace_pc + // CHECK-NOT: call void @__sanitizer_cov_trace_const_cmp + // ASAN-DAG: call void @__asan_report_store + // MSAN-DAG: call void @__msan_warning + // BOUNDS-DAG: call void @__ubsan_handle_out_of_bounds + // TSAN-DAG: call void @__tsan_func_entry + // UBSAN-DAG: call void @__ubsan_handle + if (n) + x[n] = 42; +} + + +// CHECK-LABEL: define dso_local void @test_no_sanitize_combined( +__attribute__((no_sanitize("address", "memory", "thread", "bounds", "undefined", "coverage"))) +void test_no_sanitize_combined(int n) { + // CHECK-NOT: call void @__sanitizer_cov_trace_pc + // CHECK-NOT: call void @__sanitizer_cov_trace_const_cmp + // ASAN-NOT: call void @__asan_report_store + // MSAN-NOT: call void @__msan_warning + // BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds + // TSAN-NOT: call void @__tsan_func_entry + // UBSAN-NOT: call void @__ubsan_handle + if (n) + x[n] = 42; +} + +// CHECK-LABEL: define dso_local void @test_no_sanitize_separate( +__attribute__((no_sanitize("address"))) +__attribute__((no_sanitize("memory"))) +__attribute__((no_sanitize("thread"))) +__attribute__((no_sanitize("bounds"))) +__attribute__((no_sanitize("undefined"))) +__attribute__((no_sanitize("coverage"))) +void test_no_sanitize_separate(int n) { + // CHECK-NOT: call void @__sanitizer_cov_trace_pc + // CHECK-NOT: call void @__sanitizer_cov_trace_const_cmp + // ASAN-NOT: call void @__asan_report_store + // MSAN-NOT: call void @__msan_warning + // BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds + // TSAN-NOT: call void @__tsan_func_entry + // UBSAN-NOT: call void @__ubsan_handle + if (n) + x[n] = 42; +} + +// CHECK-LABEL: define dso_local void @test_no_sanitize_always_inline( +__attribute__((no_sanitize("coverage"))) +void test_no_sanitize_always_inline(int n) { + // CHECK-NOT: call void @__sanitizer_cov_trace_pc + // CHECK-NOT: call void @__sanitizer_cov_trace_const_cmp + always_inlined_fn(n); +} + // CHECK-LABEL: declare void diff --git a/llvm/bindings/go/llvm/ir_test.go b/llvm/bindings/go/llvm/ir_test.go --- a/llvm/bindings/go/llvm/ir_test.go +++ b/llvm/bindings/go/llvm/ir_test.go @@ -69,6 +69,7 @@ "noredzone", "noreturn", "nounwind", + "nosanitize_coverage", "optnone", "optsize", "readnone", diff --git a/llvm/docs/BitCodeFormat.rst b/llvm/docs/BitCodeFormat.rst --- a/llvm/docs/BitCodeFormat.rst +++ b/llvm/docs/BitCodeFormat.rst @@ -1073,6 +1073,8 @@ * code 69: ``byref`` * code 70: ``mustprogress`` * code 74: ``vscale_range([, ])`` +* code 75: ``swiftasync`` +* code 76: ``nosanitize_coverage`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1691,6 +1691,9 @@ trap or generate asynchronous exceptions. Exception handling schemes that are recognized by LLVM to handle asynchronous exceptions, such as SEH, will still provide their implementation defined semantics. +``nosanitize_coverage`` + This attribute indicates that SanitizerCoverage instrumentation is disabled + for this function. ``null_pointer_is_valid`` If ``null_pointer_is_valid`` is set, then the ``null`` address in address-space 0 is considered to be a valid address for memory loads and diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -217,6 +217,7 @@ kw_nosync, kw_nocf_check, kw_nounwind, + kw_nosanitize_coverage, kw_null_pointer_is_valid, kw_optforfuzzing, kw_optnone, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -666,6 +666,7 @@ ATTR_KIND_NO_PROFILE = 73, ATTR_KIND_VSCALE_RANGE = 74, ATTR_KIND_SWIFT_ASYNC = 75, + ATTR_KIND_NO_SANITIZE_COVERAGE = 76, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -154,6 +154,9 @@ /// Function doesn't unwind stack. def NoUnwind : EnumAttr<"nounwind">; +/// No SanitizeCoverage instrumentation. +def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage">; + /// Null pointer in address space zero is valid. def NullPointerIsValid : EnumAttr<"null_pointer_is_valid">; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -671,6 +671,7 @@ KEYWORD(nocf_check); KEYWORD(noundef); KEYWORD(nounwind); + KEYWORD(nosanitize_coverage); KEYWORD(null_pointer_is_valid); KEYWORD(optforfuzzing); KEYWORD(optnone); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1398,6 +1398,9 @@ case lltok::kw_noprofile: B.addAttribute(Attribute::NoProfile); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; + case lltok::kw_nosanitize_coverage: + B.addAttribute(Attribute::NoSanitizeCoverage); + break; case lltok::kw_null_pointer_is_valid: B.addAttribute(Attribute::NullPointerIsValid); break; case lltok::kw_optforfuzzing: @@ -1825,6 +1828,7 @@ case lltok::kw_noreturn: case lltok::kw_nocf_check: case lltok::kw_nounwind: + case lltok::kw_nosanitize_coverage: case lltok::kw_optforfuzzing: case lltok::kw_optnone: case lltok::kw_optsize: @@ -1936,6 +1940,7 @@ case lltok::kw_noreturn: case lltok::kw_nocf_check: case lltok::kw_nounwind: + case lltok::kw_nosanitize_coverage: case lltok::kw_optforfuzzing: case lltok::kw_optnone: case lltok::kw_optsize: diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1474,6 +1474,8 @@ return Attribute::NoCfCheck; case bitc::ATTR_KIND_NO_UNWIND: return Attribute::NoUnwind; + case bitc::ATTR_KIND_NO_SANITIZE_COVERAGE: + return Attribute::NoSanitizeCoverage; case bitc::ATTR_KIND_NULL_POINTER_IS_VALID: return Attribute::NullPointerIsValid; case bitc::ATTR_KIND_OPT_FOR_FUZZING: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -686,6 +686,8 @@ return bitc::ATTR_KIND_NO_PROFILE; case Attribute::NoUnwind: return bitc::ATTR_KIND_NO_UNWIND; + case Attribute::NoSanitizeCoverage: + return bitc::ATTR_KIND_NO_SANITIZE_COVERAGE; case Attribute::NullPointerIsValid: return bitc::ATTR_KIND_NULL_POINTER_IS_VALID; case Attribute::OptForFuzzing: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -442,6 +442,8 @@ return "noprofile"; if (hasAttribute(Attribute::NoUnwind)) return "nounwind"; + if (hasAttribute(Attribute::NoSanitizeCoverage)) + return "nosanitize_coverage"; if (hasAttribute(Attribute::OptForFuzzing)) return "optforfuzzing"; if (hasAttribute(Attribute::OptimizeNone)) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1661,6 +1661,7 @@ case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: + case Attribute::NoSanitizeCoverage: case Attribute::AlwaysInline: case Attribute::OptimizeForSize: case Attribute::StackProtect: diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -53,6 +53,7 @@ .Case("nocf_check", Attribute::NoCfCheck) .Case("norecurse", Attribute::NoRecurse) .Case("nounwind", Attribute::NoUnwind) + .Case("nosanitize_coverage", Attribute::NoSanitizeCoverage) .Case("optforfuzzing", Attribute::OptForFuzzing) .Case("optnone", Attribute::OptimizeNone) .Case("optsize", Attribute::OptimizeForSize) diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp --- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -621,6 +621,8 @@ return; if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) return; + if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) + return; if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) SplitAllCriticalEdges(F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); SmallVector IndirCalls; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -954,6 +954,7 @@ case Attribute::NonLazyBind: case Attribute::NoRedZone: case Attribute::NoUnwind: + case Attribute::NoSanitizeCoverage: case Attribute::NullPointerIsValid: case Attribute::OptForFuzzing: case Attribute::OptimizeNone: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -453,6 +453,12 @@ ret void; } +; CHECK: define void @f77() #48 +define void @f77() nosanitize_coverage +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -501,4 +507,5 @@ ; CHECK: attributes #45 = { vscale_range(8,8) } ; CHECK: attributes #46 = { vscale_range(1,8) } ; CHECK: attributes #47 = { vscale_range(1,0) } +; CHECK: attributes #48 = { nosanitize_coverage } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1510,7 +1510,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #44 + ; CHECK: call void @f.nobuiltin() #45 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1904,6 +1904,9 @@ ret void } +declare void @f.nosanitize_coverage() nosanitize_coverage +; CHECK: declare void @f.nosanitize_coverage() #44 + ; immarg attribute declare void @llvm.test.immarg.intrinsic(i32 immarg) ; CHECK: declare void @llvm.test.immarg.intrinsic(i32 immarg) @@ -1961,7 +1964,8 @@ ; CHECK: attributes #41 = { writeonly } ; CHECK: attributes #42 = { speculatable } ; CHECK: attributes #43 = { strictfp } -; CHECK: attributes #44 = { builtin } +; CHECK: attributes #44 = { nosanitize_coverage } +; CHECK: attributes #45 = { builtin } ;; Metadata diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -25,7 +25,7 @@ '("alwaysinline" "argmemonly" "allocsize" "builtin" "cold" "convergent" "dereferenceable" "dereferenceable_or_null" "hot" "inaccessiblememonly" "inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull" "nocallback" "nocf_check" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn" - "norecurse" "nosync" "noundef" "nounwind" "null_pointer_is_valid" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice" + "norecurse" "nosync" "noundef" "nounwind" "nosanitize_coverage" "null_pointer_is_valid" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice" "shadowcallstack" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "immarg") 'symbols) . font-lock-constant-face) ;; Variables diff --git a/llvm/utils/llvm.grm b/llvm/utils/llvm.grm --- a/llvm/utils/llvm.grm +++ b/llvm/utils/llvm.grm @@ -176,6 +176,7 @@ | sanitize_thread | sanitize_memory | mustprogress + | nosanitize_coverage ; OptFuncAttrs ::= + _ | OptFuncAttrs FuncAttr ; diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -138,6 +138,7 @@ \ nosync \ noundef \ nounwind + \ nosanitize_coverage \ null_pointer_is_valid \ optforfuzzing \ optnone diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -237,6 +237,7 @@ \\bnosync\\b|\ \\bnoundef\\b|\ \\bnounwind\\b|\ + \\bnosanitize_coverage\\b|\ \\bnull_pointer_is_valid\\b|\ \\boptforfuzzing\\b|\ \\boptnone\\b|\