[SanitizeCoverage] Add support for NoSanitizeCoverage function attribute
We really ought to support no_sanitize("coverage") in line with other sanitizers. This came up again in discussions on the Linux-kernel mailing lists, because we currently do workarounds using objtool to remove coverage instrumentation. Since that support is only on x86, to continue support coverage instrumentation on other architectures, we must support selectively disabling coverage instrumentation via function attributes. Unfortunately, for SanitizeCoverage, it has not been implemented as a sanitizer via fsanitize= and associated options in Sanitizers.def, but rolls its own option fsanitize-coverage. This meant that we never got "automatic" no_sanitize attribute support. Implement no_sanitize attribute support by special-casing the string "coverage" in the NoSanitizeAttr implementation. To keep the feature as unintrusive to existing IR generation as possible, define a new negative function attribute NoSanitizeCoverage to propagate the information through to the instrumentation pass. Fixes: https://bugs.llvm.org/show_bug.cgi?id=49035 Reviewed By: vitalybuka, morehouse Differential Revision: https://reviews.llvm.org/D102772
This commit is contained in:
parent
85feebf5a3
commit
280333021e
|
@ -312,11 +312,17 @@ will not be instrumented.
|
|||
// 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.
|
||||
|
|
|
@ -2897,6 +2897,10 @@ def NoSanitize : InheritableAttr {
|
|||
}
|
||||
return Mask;
|
||||
}
|
||||
|
||||
bool hasCoverage() const {
|
||||
return llvm::is_contained(sanitizers(), "coverage");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -2564,12 +2564,17 @@ def NoSanitizeDocs : Documentation {
|
|||
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 <controlling-code-generation>` for a
|
||||
full list of supported sanitizer flags.
|
||||
|
|
|
@ -734,8 +734,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
|
|||
} while (0);
|
||||
|
||||
if (D) {
|
||||
// Apply the no_sanitize* attributes to SanOpts.
|
||||
bool NoSanitizeCoverage = false;
|
||||
|
||||
for (auto Attr : D->specific_attrs<NoSanitizeAttr>()) {
|
||||
// Apply the no_sanitize* attributes to SanOpts.
|
||||
SanitizerMask mask = Attr->getMask();
|
||||
SanOpts.Mask &= ~mask;
|
||||
if (mask & SanitizerKind::Address)
|
||||
|
@ -746,7 +748,14 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
|
|||
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.
|
||||
|
|
|
@ -7290,7 +7290,8 @@ static void handleNoSanitizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
|||
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)
|
||||
|
|
|
@ -31,4 +31,59 @@ void test_always_inline(int n) {
|
|||
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
|
||||
|
|
|
@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
|
|||
"noredzone",
|
||||
"noreturn",
|
||||
"nounwind",
|
||||
"nosanitize_coverage",
|
||||
"optnone",
|
||||
"optsize",
|
||||
"readnone",
|
||||
|
|
|
@ -1073,6 +1073,8 @@ The integer codes are mapped to well-known attributes as follows.
|
|||
* code 69: ``byref``
|
||||
* code 70: ``mustprogress``
|
||||
* code 74: ``vscale_range(<Min>[, <Max>])``
|
||||
* code 75: ``swiftasync``
|
||||
* code 76: ``nosanitize_coverage``
|
||||
|
||||
.. note::
|
||||
The ``allocsize`` attribute has a special encoding for its arguments. Its two
|
||||
|
|
|
@ -1691,6 +1691,9 @@ example:
|
|||
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
|
||||
|
|
|
@ -217,6 +217,7 @@ enum Kind {
|
|||
kw_nosync,
|
||||
kw_nocf_check,
|
||||
kw_nounwind,
|
||||
kw_nosanitize_coverage,
|
||||
kw_null_pointer_is_valid,
|
||||
kw_optforfuzzing,
|
||||
kw_optnone,
|
||||
|
|
|
@ -666,6 +666,7 @@ enum AttributeKindCodes {
|
|||
ATTR_KIND_NO_PROFILE = 73,
|
||||
ATTR_KIND_VSCALE_RANGE = 74,
|
||||
ATTR_KIND_SWIFT_ASYNC = 75,
|
||||
ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
|
|
@ -154,6 +154,9 @@ def NoProfile : EnumAttr<"noprofile">;
|
|||
/// 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">;
|
||||
|
||||
|
|
|
@ -671,6 +671,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
|||
KEYWORD(nocf_check);
|
||||
KEYWORD(noundef);
|
||||
KEYWORD(nounwind);
|
||||
KEYWORD(nosanitize_coverage);
|
||||
KEYWORD(null_pointer_is_valid);
|
||||
KEYWORD(optforfuzzing);
|
||||
KEYWORD(optnone);
|
||||
|
|
|
@ -1398,6 +1398,9 @@ bool LLParser::parseFnAttributeValuePairs(AttrBuilder &B,
|
|||
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 @@ bool LLParser::parseOptionalParamAttrs(AttrBuilder &B) {
|
|||
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 @@ bool LLParser::parseOptionalReturnAttrs(AttrBuilder &B) {
|
|||
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:
|
||||
|
|
|
@ -1474,6 +1474,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
|||
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:
|
||||
|
|
|
@ -686,6 +686,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
|||
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:
|
||||
|
|
|
@ -442,6 +442,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
|||
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))
|
||||
|
|
|
@ -1661,6 +1661,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
|
|||
case Attribute::NoCfCheck:
|
||||
case Attribute::NoUnwind:
|
||||
case Attribute::NoInline:
|
||||
case Attribute::NoSanitizeCoverage:
|
||||
case Attribute::AlwaysInline:
|
||||
case Attribute::OptimizeForSize:
|
||||
case Attribute::StackProtect:
|
||||
|
|
|
@ -53,6 +53,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
|
|||
.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)
|
||||
|
|
|
@ -621,6 +621,8 @@ void ModuleSanitizerCoverage::instrumentFunction(
|
|||
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<Instruction *, 8> IndirCalls;
|
||||
|
|
|
@ -954,6 +954,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
|||
case Attribute::NonLazyBind:
|
||||
case Attribute::NoRedZone:
|
||||
case Attribute::NoUnwind:
|
||||
case Attribute::NoSanitizeCoverage:
|
||||
case Attribute::NullPointerIsValid:
|
||||
case Attribute::OptForFuzzing:
|
||||
case Attribute::OptimizeNone:
|
||||
|
|
|
@ -453,6 +453,12 @@ define void @f76(i8* swiftasync %0)
|
|||
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 @@ define void @f76(i8* swiftasync %0)
|
|||
; 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 }
|
||||
|
|
|
@ -1510,7 +1510,7 @@ exit:
|
|||
; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
|
||||
|
||||
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 @@ define void @instructions.strictfp() strictfp {
|
|||
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 @@ declare void @byval_named_type(%named_type* byval(%named_type))
|
|||
; 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -176,6 +176,7 @@ FuncAttr ::= noreturn
|
|||
| sanitize_thread
|
||||
| sanitize_memory
|
||||
| mustprogress
|
||||
| nosanitize_coverage
|
||||
;
|
||||
|
||||
OptFuncAttrs ::= + _ | OptFuncAttrs FuncAttr ;
|
||||
|
|
|
@ -138,6 +138,7 @@ syn keyword llvmKeyword
|
|||
\ nosync
|
||||
\ noundef
|
||||
\ nounwind
|
||||
\ nosanitize_coverage
|
||||
\ null_pointer_is_valid
|
||||
\ optforfuzzing
|
||||
\ optnone
|
||||
|
|
|
@ -237,6 +237,7 @@ patterns:
|
|||
\\bnosync\\b|\
|
||||
\\bnoundef\\b|\
|
||||
\\bnounwind\\b|\
|
||||
\\bnosanitize_coverage\\b|\
|
||||
\\bnull_pointer_is_valid\\b|\
|
||||
\\boptforfuzzing\\b|\
|
||||
\\boptnone\\b|\
|
||||
|
|
Loading…
Reference in New Issue