[clang][Codegen] Introduce the disable_sanitizer_instrumentation attribute
The purpose of __attribute__((disable_sanitizer_instrumentation)) is to prevent all kinds of sanitizer instrumentation applied to a certain function, Objective-C method, or global variable. The no_sanitize(...) attribute drops instrumentation checks, but may still insert code preventing false positive reports. In some cases though (e.g. when building Linux kernel with -fsanitize=kernel-memory or -fsanitize=thread) the users may want to avoid any kind of instrumentation. Differential Revision: https://reviews.llvm.org/D108029
This commit is contained in:
parent
b2aa470fae
commit
b0391dfc73
|
@ -2948,6 +2948,13 @@ def NoSanitizeSpecific : InheritableAttr {
|
|||
let ASTNode = 0;
|
||||
}
|
||||
|
||||
def DisableSanitizerInstrumentation : InheritableAttr {
|
||||
let Spellings = [Clang<"disable_sanitizer_instrumentation">];
|
||||
let Subjects = SubjectList<[Function, ObjCMethod, GlobalVar]>;
|
||||
let Documentation = [DisableSanitizerInstrumentationDocs];
|
||||
let SimpleHandler = 1;
|
||||
}
|
||||
|
||||
def CFICanonicalJumpTable : InheritableAttr {
|
||||
let Spellings = [Clang<"cfi_canonical_jump_table">];
|
||||
let Subjects = SubjectList<[Function], ErrorDiag>;
|
||||
|
|
|
@ -2602,6 +2602,18 @@ full list of supported sanitizer flags.
|
|||
}];
|
||||
}
|
||||
|
||||
def DisableSanitizerInstrumentationDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
Use the ``disable_sanitizer_instrumentation`` attribute on a function,
|
||||
Objective-C method, or global variable, to specify that no sanitizer
|
||||
instrumentation should be applied.
|
||||
|
||||
This is not the same as ``__attribute__((no_sanitize(...)))``, which depending
|
||||
on the tool may still insert instrumentation to prevent false positive reports.
|
||||
}];
|
||||
}
|
||||
|
||||
def NoSanitizeAddressDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
// This function has multiple distinct spellings, and so it requires a custom
|
||||
|
|
|
@ -381,6 +381,9 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
|
|||
"__cyg_profile_func_exit");
|
||||
}
|
||||
|
||||
if (ShouldSkipSanitizerInstrumentation())
|
||||
CurFn->addFnAttr(llvm::Attribute::DisableSanitizerInstrumentation);
|
||||
|
||||
// Emit debug descriptor for function end.
|
||||
if (CGDebugInfo *DI = getDebugInfo())
|
||||
DI->EmitFunctionEnd(Builder, CurFn);
|
||||
|
@ -519,6 +522,12 @@ bool CodeGenFunction::ShouldInstrumentFunction() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CodeGenFunction::ShouldSkipSanitizerInstrumentation() {
|
||||
if (!CurFuncDecl)
|
||||
return false;
|
||||
return CurFuncDecl->hasAttr<DisableSanitizerInstrumentationAttr>();
|
||||
}
|
||||
|
||||
/// ShouldXRayInstrument - Return true if the current function should be
|
||||
/// instrumented with XRay nop sleds.
|
||||
bool CodeGenFunction::ShouldXRayInstrumentFunction() const {
|
||||
|
|
|
@ -2286,6 +2286,10 @@ public:
|
|||
/// instrumented with __cyg_profile_func_* calls
|
||||
bool ShouldInstrumentFunction();
|
||||
|
||||
/// ShouldSkipSanitizerInstrumentation - Return true if the current function
|
||||
/// should not be instrumented with sanitizers.
|
||||
bool ShouldSkipSanitizerInstrumentation();
|
||||
|
||||
/// ShouldXRayInstrument - Return true if the current function should be
|
||||
/// instrumented with XRay nop sleds.
|
||||
bool ShouldXRayInstrumentFunction() const;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// RUN: %clang_cc1 -debug-info-kind=limited %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
void t1() __attribute__((disable_sanitizer_instrumentation)) {
|
||||
}
|
||||
// CHECK: disable_sanitizer_instrumentation
|
||||
// CHECK-NEXT: void @t1
|
||||
|
||||
// CHECK-NOT: disable_sanitizer_instrumentation
|
||||
// CHECK: void @t2
|
||||
void t2() {
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
|
||||
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
|
||||
// CHECK-NEXT: Destructor (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: DisableSanitizerInstrumentation (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global)
|
||||
// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method)
|
||||
// CHECK-NEXT: EnableIf (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: EnforceTCB (SubjectMatchRule_function)
|
||||
|
|
|
@ -1075,6 +1075,8 @@ The integer codes are mapped to well-known attributes as follows.
|
|||
* code 74: ``vscale_range(<Min>[, <Max>])``
|
||||
* code 75: ``swiftasync``
|
||||
* code 76: ``nosanitize_coverage``
|
||||
* code 77: ``elementtype``
|
||||
* code 78: ``disable_sanitizer_instrumentation``
|
||||
|
||||
.. note::
|
||||
The ``allocsize`` attribute has a special encoding for its arguments. Its two
|
||||
|
|
|
@ -1582,6 +1582,19 @@ example:
|
|||
can prove that the function does not execute any convergent operations.
|
||||
Similarly, the optimizer may remove ``convergent`` on calls/invokes when it
|
||||
can prove that the call/invoke cannot call a convergent function.
|
||||
``disable_sanitizer_instrumentation``
|
||||
When instrumenting code with sanitizers, it can be important to skip certain
|
||||
functions to ensure no instrumentation is applied to them.
|
||||
|
||||
This attribute is not always similar to absent ``sanitize_<name>``
|
||||
attributes: depending on the specific sanitizer, code can be inserted into
|
||||
functions regardless of the ``sanitize_<name>`` attribute to prevent false
|
||||
positive reports.
|
||||
|
||||
``disable_sanitizer_instrumentation`` disables all kinds of instrumentation,
|
||||
taking precedence over the ``sanitize_<name>`` attributes and other compiler
|
||||
flags.
|
||||
|
||||
``"frame-pointer"``
|
||||
This attribute tells the code generator whether the function
|
||||
should keep the frame pointer. The code generator may emit the frame pointer
|
||||
|
|
|
@ -190,6 +190,7 @@ enum Kind {
|
|||
kw_convergent,
|
||||
kw_dereferenceable,
|
||||
kw_dereferenceable_or_null,
|
||||
kw_disable_sanitizer_instrumentation,
|
||||
kw_elementtype,
|
||||
kw_inaccessiblememonly,
|
||||
kw_inaccessiblemem_or_argmemonly,
|
||||
|
|
|
@ -671,6 +671,7 @@ enum AttributeKindCodes {
|
|||
ATTR_KIND_SWIFT_ASYNC = 75,
|
||||
ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
|
||||
ATTR_KIND_ELEMENTTYPE = 77,
|
||||
ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78,
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
|
|
@ -86,6 +86,9 @@ def Dereferenceable : IntAttr<"dereferenceable", [ParamAttr, RetAttr]>;
|
|||
def DereferenceableOrNull : IntAttr<"dereferenceable_or_null",
|
||||
[ParamAttr, RetAttr]>;
|
||||
|
||||
/// Do not instrument function with sanitizers.
|
||||
def DisableSanitizerInstrumentation: EnumAttr<"disable_sanitizer_instrumentation", [FnAttr]>;
|
||||
|
||||
/// Provide pointer element type to intrinsic.
|
||||
def ElementType : TypeAttr<"elementtype", [ParamAttr]>;
|
||||
|
||||
|
|
|
@ -643,6 +643,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
|||
KEYWORD(convergent);
|
||||
KEYWORD(dereferenceable);
|
||||
KEYWORD(dereferenceable_or_null);
|
||||
KEYWORD(disable_sanitizer_instrumentation);
|
||||
KEYWORD(elementtype);
|
||||
KEYWORD(inaccessiblememonly);
|
||||
KEYWORD(inaccessiblemem_or_argmemonly);
|
||||
|
|
|
@ -1388,6 +1388,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
|||
return Attribute::Cold;
|
||||
case bitc::ATTR_KIND_CONVERGENT:
|
||||
return Attribute::Convergent;
|
||||
case bitc::ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION:
|
||||
return Attribute::DisableSanitizerInstrumentation;
|
||||
case bitc::ATTR_KIND_ELEMENTTYPE:
|
||||
return Attribute::ElementType;
|
||||
case bitc::ATTR_KIND_INACCESSIBLEMEM_ONLY:
|
||||
|
|
|
@ -626,6 +626,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
|||
return bitc::ATTR_KIND_IN_ALLOCA;
|
||||
case Attribute::Cold:
|
||||
return bitc::ATTR_KIND_COLD;
|
||||
case Attribute::DisableSanitizerInstrumentation:
|
||||
return bitc::ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION;
|
||||
case Attribute::Hot:
|
||||
return bitc::ATTR_KIND_HOT;
|
||||
case Attribute::ElementType:
|
||||
|
|
|
@ -943,6 +943,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
|||
// Those attributes should be safe to propagate to the extracted function.
|
||||
case Attribute::AlwaysInline:
|
||||
case Attribute::Cold:
|
||||
case Attribute::DisableSanitizerInstrumentation:
|
||||
case Attribute::Hot:
|
||||
case Attribute::NoRecurse:
|
||||
case Attribute::InlineHint:
|
||||
|
|
|
@ -472,6 +472,12 @@ define void @f79() {
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @f80() #50
|
||||
define void @f80() disable_sanitizer_instrumentation
|
||||
{
|
||||
ret void;
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { noreturn }
|
||||
; CHECK: attributes #1 = { nounwind }
|
||||
; CHECK: attributes #2 = { readnone }
|
||||
|
@ -522,4 +528,5 @@ define void @f79() {
|
|||
; CHECK: attributes #47 = { vscale_range(1,0) }
|
||||
; CHECK: attributes #48 = { nosanitize_coverage }
|
||||
; CHECK: attributes #49 = { noprofile }
|
||||
; CHECK: attributes #50 = { disable_sanitizer_instrumentation }
|
||||
; 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() #45
|
||||
; CHECK: call void @f.nobuiltin() #46
|
||||
|
||||
call fastcc noalias i32* @f.noalias() noinline
|
||||
; CHECK: call fastcc noalias i32* @f.noalias() #12
|
||||
|
@ -1907,6 +1907,9 @@ define void @instructions.strictfp() strictfp {
|
|||
declare void @f.nosanitize_coverage() nosanitize_coverage
|
||||
; CHECK: declare void @f.nosanitize_coverage() #44
|
||||
|
||||
declare void @f.disable_sanitizer_instrumentation() disable_sanitizer_instrumentation
|
||||
; CHECK: declare void @f.disable_sanitizer_instrumentation() #45
|
||||
|
||||
; immarg attribute
|
||||
declare void @llvm.test.immarg.intrinsic(i32 immarg)
|
||||
; CHECK: declare void @llvm.test.immarg.intrinsic(i32 immarg)
|
||||
|
@ -1965,7 +1968,8 @@ declare void @byval_named_type(%named_type* byval(%named_type))
|
|||
; CHECK: attributes #42 = { speculatable }
|
||||
; CHECK: attributes #43 = { strictfp }
|
||||
; CHECK: attributes #44 = { nosanitize_coverage }
|
||||
; CHECK: attributes #45 = { builtin }
|
||||
; CHECK: attributes #45 = { disable_sanitizer_instrumentation }
|
||||
; CHECK: attributes #46 = { builtin }
|
||||
|
||||
;; Metadata
|
||||
|
||||
|
|
Loading…
Reference in New Issue