Implement no_sanitize attribute.

Differential Revision: http://reviews.llvm.org/D9631

llvm-svn: 237463
This commit is contained in:
Peter Collingbourne 2015-05-15 18:33:32 +00:00
parent 25e2500ac8
commit 915df9968b
15 changed files with 237 additions and 56 deletions

View File

@ -930,6 +930,8 @@ number of cases where the compilation environment is tightly controlled
and the precompiled header cannot be generated after headers have been
installed.
.. _controlling-code-generation:
Controlling Code Generation
---------------------------

View File

@ -20,6 +20,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/VersionTuple.h"
#include "llvm/ADT/SmallVector.h"

View File

@ -144,6 +144,7 @@ class TypeArgument<string name, bit opt = 0> : Argument<name, opt>;
class UnsignedArgument<string name, bit opt = 0> : Argument<name, opt>;
class VariadicUnsignedArgument<string name> : Argument<name, 1>;
class VariadicExprArgument<string name> : Argument<name, 1>;
class VariadicStringArgument<string name> : Argument<name, 1>;
// A version of the form major.minor[.subminor].
class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
@ -1386,26 +1387,35 @@ def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr<TargetX86> {
let Documentation = [Undocumented];
}
// Attribute to disable AddressSanitizer (or equivalent) checks.
def NoSanitizeAddress : InheritableAttr {
def NoSanitize : InheritableAttr {
let Spellings = [GNU<"no_sanitize">, CXX11<"clang", "no_sanitize">];
let Args = [VariadicStringArgument<"Sanitizers">];
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
let Documentation = [NoSanitizeDocs];
let AdditionalMembers = [{
SanitizerMask getMask() const {
SanitizerMask Mask = 0;
for (auto SanitizerName : sanitizers()) {
SanitizerMask ParsedMask =
parseSanitizerValue(SanitizerName, /*AllowGroups=*/true);
Mask |= expandSanitizerGroups(ParsedMask);
}
return Mask;
}
}];
}
// Attributes to disable a specific sanitizer. No new sanitizers should be added
// to this list; the no_sanitize attribute should be extended instead.
def NoSanitizeSpecific : InheritableAttr {
let Spellings = [GCC<"no_address_safety_analysis">,
GCC<"no_sanitize_address">];
GCC<"no_sanitize_address">,
GCC<"no_sanitize_thread">,
GNU<"no_sanitize_memory">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [NoSanitizeAddressDocs];
}
// Attribute to disable ThreadSanitizer checks.
def NoSanitizeThread : InheritableAttr {
let Spellings = [GNU<"no_sanitize_thread">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [NoSanitizeThreadDocs];
}
// Attribute to disable MemorySanitizer checks.
def NoSanitizeMemory : InheritableAttr {
let Spellings = [GNU<"no_sanitize_memory">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [NoSanitizeMemoryDocs];
let Documentation = [NoSanitizeAddressDocs, NoSanitizeThreadDocs,
NoSanitizeMemoryDocs];
let ASTNode = 0;
}
// C/C++ Thread safety attributes (e.g. for deadlock, data race checking)

View File

@ -920,6 +920,22 @@ This attribute accepts a single parameter that must be one of the following:
}];
}
def NoSanitizeDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Use the ``no_sanitize`` attribute on a function declaration to specify
that a particular instrumentation or set of instrumentations should not be
applied to that function. 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.
See :ref:`Controlling Code Generation <controlling-code-generation>` for a
full list of supported sanitizer flags.
}];
}
def NoSanitizeAddressDocs : Documentation {
let Category = DocCatFunction;
// This function has multiple distinct spellings, and so it requires a custom
@ -936,6 +952,7 @@ not be applied to that function.
def NoSanitizeThreadDocs : Documentation {
let Category = DocCatFunction;
let Heading = "no_sanitize_thread";
let Content = [{
.. _langext-thread_sanitizer:
@ -948,6 +965,7 @@ tool to avoid false positives and provide meaningful stack traces.
def NoSanitizeMemoryDocs : Documentation {
let Category = DocCatFunction;
let Heading = "no_sanitize_memory";
let Content = [{
.. _langext-memory_sanitizer:

View File

@ -401,6 +401,7 @@ def UnknownAttributes : DiagGroup<"unknown-attributes">;
def IgnoredAttributes : DiagGroup<"ignored-attributes">;
def Attributes : DiagGroup<"attributes", [UnknownAttributes,
IgnoredAttributes]>;
def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
def UnnamedTypeTemplateArgs : DiagGroup<"unnamed-type-template-args",
[CXX98CompatUnnamedTypeTemplateArgs]>;
def UnsupportedFriend : DiagGroup<"unsupported-friend">;

View File

@ -2519,6 +2519,10 @@ def warn_param_typestate_mismatch : Warning<
"argument not in expected state; expected '%0', observed '%1'">,
InGroup<Consumed>, DefaultIgnore;
// no_sanitize attribute
def warn_unknown_sanitizer_ignored : Warning<
"unknown sanitizer '%0' ignored">, InGroup<UnknownSanitizers>;
def warn_impcast_vector_scalar : Warning<
"implicit conversion turns vector to scalar: %0 to %1">,
InGroup<Conversion>, DefaultIgnore;

View File

@ -608,6 +608,20 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
if (CGM.isInSanitizerBlacklist(Fn, Loc))
SanOpts.clear();
if (D) {
// Apply the no_sanitize* attributes to SanOpts.
for (auto Attr : D->specific_attrs<NoSanitizeAttr>())
SanOpts.Mask &= ~Attr->getMask();
}
// Apply sanitizer attributes to the function.
if (SanOpts.has(SanitizerKind::Address))
Fn->addFnAttr(llvm::Attribute::SanitizeAddress);
if (SanOpts.has(SanitizerKind::Thread))
Fn->addFnAttr(llvm::Attribute::SanitizeThread);
if (SanOpts.has(SanitizerKind::Memory))
Fn->addFnAttr(llvm::Attribute::SanitizeMemory);
// Pass inline keyword to optimizer if it appears explicitly on any
// declaration. Also, in the case of -fno-inline attach NoInline
// attribute to all function that are not marked AlwaysInline.

View File

@ -752,23 +752,6 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
else if (LangOpts.getStackProtector() == LangOptions::SSPReq)
B.addAttribute(llvm::Attribute::StackProtectReq);
// Add sanitizer attributes if function is not blacklisted.
if (!isInSanitizerBlacklist(F, D->getLocation())) {
// When AddressSanitizer is enabled, set SanitizeAddress attribute
// unless __attribute__((no_sanitize_address)) is used.
if (LangOpts.Sanitize.has(SanitizerKind::Address) &&
!D->hasAttr<NoSanitizeAddressAttr>())
B.addAttribute(llvm::Attribute::SanitizeAddress);
// Same for ThreadSanitizer and __attribute__((no_sanitize_thread))
if (LangOpts.Sanitize.has(SanitizerKind::Thread) &&
!D->hasAttr<NoSanitizeThreadAttr>())
B.addAttribute(llvm::Attribute::SanitizeThread);
// Same for MemorySanitizer and __attribute__((no_sanitize_memory))
if (LangOpts.Sanitize.has(SanitizerKind::Memory) &&
!D->hasAttr<NoSanitizeMemoryAttr>())
B.addAttribute(llvm::Attribute::SanitizeMemory);
}
F->addAttributes(llvm::AttributeSet::FunctionIndex,
llvm::AttributeSet::get(
F->getContext(), llvm::AttributeSet::FunctionIndex, B));

View File

@ -4354,6 +4354,45 @@ static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
handleAttrWithMessage<DeprecatedAttr>(S, D, Attr);
}
static void handleNoSanitizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
return;
std::vector<std::string> Sanitizers;
for (unsigned I = 0, E = Attr.getNumArgs(); I != E; ++I) {
StringRef SanitizerName;
SourceLocation LiteralLoc;
if (!S.checkStringLiteralArgumentAttr(Attr, I, SanitizerName, &LiteralLoc))
return;
if (parseSanitizerValue(SanitizerName, /*AllowGroups=*/true) == 0)
S.Diag(LiteralLoc, diag::warn_unknown_sanitizer_ignored) << SanitizerName;
Sanitizers.push_back(SanitizerName);
}
D->addAttr(::new (S.Context) NoSanitizeAttr(
Attr.getRange(), S.Context, Sanitizers.data(), Sanitizers.size(),
Attr.getAttributeSpellingListIndex()));
}
static void handleNoSanitizeSpecificAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
std::string SanitizerName =
llvm::StringSwitch<const char *>(Attr.getName()->getName())
.Case("no_address_safety_analysis", "address")
.Case("no_sanitize_address", "address")
.Case("no_sanitize_thread", "thread")
.Case("no_sanitize_memory", "memory")
.Default("");
assert(!SanitizerName.empty());
D->addAttr(::new (S.Context)
NoSanitizeAttr(Attr.getRange(), S.Context, &SanitizerName, 1,
Attr.getAttributeSpellingListIndex()));
}
/// Handles semantic checking for features that are common to all attributes,
/// such as checking whether a parameter was properly specified, or the correct
/// number of arguments were passed, etc.
@ -4822,18 +4861,15 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_ScopedLockable:
handleSimpleAttribute<ScopedLockableAttr>(S, D, Attr);
break;
case AttributeList::AT_NoSanitizeAddress:
handleSimpleAttribute<NoSanitizeAddressAttr>(S, D, Attr);
case AttributeList::AT_NoSanitize:
handleNoSanitizeAttr(S, D, Attr);
break;
case AttributeList::AT_NoSanitizeSpecific:
handleNoSanitizeSpecificAttr(S, D, Attr);
break;
case AttributeList::AT_NoThreadSafetyAnalysis:
handleSimpleAttribute<NoThreadSafetyAnalysisAttr>(S, D, Attr);
break;
case AttributeList::AT_NoSanitizeThread:
handleSimpleAttribute<NoSanitizeThreadAttr>(S, D, Attr);
break;
case AttributeList::AT_NoSanitizeMemory:
handleSimpleAttribute<NoSanitizeMemoryAttr>(S, D, Attr);
break;
case AttributeList::AT_GuardedBy:
handleGuardedByAttr(S, D, Attr);
break;

View File

@ -3,14 +3,14 @@ int DefinedInDifferentFile(int *a);
// RUN: echo "struct S { S(){} ~S(){} };" >> %t.extra-source.cpp
// RUN: echo "S glob_array[5];" >> %t.extra-source.cpp
// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp | FileCheck -check-prefix=WITHOUT %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address | FileCheck -check-prefix=ASAN %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp | FileCheck -check-prefix=WITHOUT %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address | FileCheck -check-prefix=ASAN %s
// RUN: echo "fun:*BlacklistedFunction*" > %t.func.blacklist
// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.func.blacklist | FileCheck -check-prefix=BLFUNC %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.func.blacklist | FileCheck -check-prefix=BLFUNC %s
// RUN: echo "src:%s" > %t.file.blacklist
// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.file.blacklist | FileCheck -check-prefix=BLFILE %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin -emit-llvm -o - %s -include %t.extra-source.cpp -fsanitize=address -fsanitize-blacklist=%t.file.blacklist | FileCheck -check-prefix=BLFILE %s
// FIXME: %t.file.blacklist is like "src:x:\path\to\clang\test\CodeGen\address-safety-attr.cpp"
// REQUIRES: shell
@ -52,6 +52,36 @@ __attribute__((no_sanitize_address))
int NoAddressSafety2(int *a);
int NoAddressSafety2(int *a) { return *a; }
// WITHOUT: NoAddressSafety3{{.*}}) [[NOATTR]]
// BLFILE: NoAddressSafety3{{.*}}) [[NOATTR]]
// BLFUNC: NoAddressSafety3{{.*}}) [[NOATTR]]
// ASAN: NoAddressSafety3{{.*}}) [[NOATTR]]
[[gnu::no_sanitize_address]]
int NoAddressSafety3(int *a) { return *a; }
// WITHOUT: NoAddressSafety4{{.*}}) [[NOATTR]]
// BLFILE: NoAddressSafety4{{.*}}) [[NOATTR]]
// BLFUNC: NoAddressSafety4{{.*}}) [[NOATTR]]
// ASAN: NoAddressSafety4{{.*}}) [[NOATTR]]
[[gnu::no_sanitize_address]]
int NoAddressSafety4(int *a);
int NoAddressSafety4(int *a) { return *a; }
// WITHOUT: NoAddressSafety5{{.*}}) [[NOATTR]]
// BLFILE: NoAddressSafety5{{.*}}) [[NOATTR]]
// BLFUNC: NoAddressSafety5{{.*}}) [[NOATTR]]
// ASAN: NoAddressSafety5{{.*}}) [[NOATTR]]
__attribute__((no_sanitize("address")))
int NoAddressSafety5(int *a) { return *a; }
// WITHOUT: NoAddressSafety6{{.*}}) [[NOATTR]]
// BLFILE: NoAddressSafety6{{.*}}) [[NOATTR]]
// BLFUNC: NoAddressSafety6{{.*}}) [[NOATTR]]
// ASAN: NoAddressSafety6{{.*}}) [[NOATTR]]
__attribute__((no_sanitize("address")))
int NoAddressSafety6(int *a);
int NoAddressSafety6(int *a) { return *a; }
// WITHOUT: AddressSafetyOk{{.*}}) [[NOATTR]]
// BLFILE: AddressSafetyOk{{.*}}) [[NOATTR]]
// BLFUNC: AddressSafetyOk{{.*}}) [[WITH]]
@ -86,16 +116,25 @@ int GENERATE_NAME(Function)(int *a) { return *a; }
template<int i>
int TemplateAddressSafetyOk() { return i; }
// WITHOUT: TemplateNoAddressSafety{{.*}}) [[NOATTR]]
// BLFILE: TemplateNoAddressSafety{{.*}}) [[NOATTR]]
// BLFUNC: TemplateNoAddressSafety{{.*}}) [[NOATTR]]
// ASAN: TemplateNoAddressSafety{{.*}}) [[NOATTR]]
// WITHOUT: TemplateNoAddressSafety1{{.*}}) [[NOATTR]]
// BLFILE: TemplateNoAddressSafety1{{.*}}) [[NOATTR]]
// BLFUNC: TemplateNoAddressSafety1{{.*}}) [[NOATTR]]
// ASAN: TemplateNoAddressSafety1{{.*}}) [[NOATTR]]
template<int i>
__attribute__((no_sanitize_address))
int TemplateNoAddressSafety() { return i; }
int TemplateNoAddressSafety1() { return i; }
// WITHOUT: TemplateNoAddressSafety2{{.*}}) [[NOATTR]]
// BLFILE: TemplateNoAddressSafety2{{.*}}) [[NOATTR]]
// BLFUNC: TemplateNoAddressSafety2{{.*}}) [[NOATTR]]
// ASAN: TemplateNoAddressSafety2{{.*}}) [[NOATTR]]
template<int i>
__attribute__((no_sanitize("address")))
int TemplateNoAddressSafety2() { return i; }
int force_instance = TemplateAddressSafetyOk<42>()
+ TemplateNoAddressSafety<42>();
+ TemplateNoAddressSafety1<42>()
+ TemplateNoAddressSafety2<42>();
// Check that __cxx_global_var_init* get the sanitize_address attribute.
int global1 = 0;

View File

@ -22,6 +22,12 @@ __attribute__((no_sanitize_thread))
int NoTSAN2(int *a);
int NoTSAN2(int *a) { return *a; }
// WITHOUT: NoTSAN3{{.*}}) [[NOATTR:#[0-9]+]]
// BL: NoTSAN3{{.*}}) [[NOATTR:#[0-9]+]]
// TSAN: NoTSAN3{{.*}}) [[NOATTR:#[0-9]+]]
__attribute__((no_sanitize("thread")))
int NoTSAN3(int *a) { return *a; }
// WITHOUT: TSANOk{{.*}}) [[NOATTR]]
// BL: TSANOk{{.*}}) [[NOATTR]]
// TSAN: TSANOk{{.*}}) [[WITH:#[0-9]+]]

View File

@ -47,16 +47,32 @@ void af(A *a) {
a->f();
}
// CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE
void df(D *d) {
// CHECK: define internal void @_Z3df1PN12_GLOBAL__N_11DE
void df1(D *d) {
// CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE")
d->f();
}
// CHECK: define internal void @_Z3df2PN12_GLOBAL__N_11DE
__attribute__((no_sanitize("cfi")))
void df2(D *d) {
// CHECK-NOT: call i1 @llvm.bitset.test
d->f();
}
// CHECK: define internal void @_Z3df3PN12_GLOBAL__N_11DE
__attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall")))
void df3(D *d) {
// CHECK-NOT: call i1 @llvm.bitset.test
d->f();
}
D d;
void foo() {
df(&d);
df1(&d);
df2(&d);
df3(&d);
}
// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16}

View File

@ -0,0 +1,8 @@
// RUN: %clang_cc1 %s -emit-llvm -fsanitize=address -o - | FileCheck %s
@interface I0 @end
@implementation I0
// CHECK-NOT: sanitize_address
- (void) im0: (int) a0 __attribute__((no_sanitize("address"))) {
}
@end

View File

@ -0,0 +1,29 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
// RUN: not %clang_cc1 -std=c++11 -ast-dump %s 2>&1 | FileCheck --check-prefix=DUMP %s
// RUN: not %clang_cc1 -std=c++11 -ast-print %s 2>&1 | FileCheck --check-prefix=PRINT %s
int v1 __attribute__((no_sanitize("address"))); // expected-error{{'no_sanitize' attribute only applies to functions and methods}}
int f1() __attribute__((no_sanitize)); // expected-error{{'no_sanitize' attribute takes at least 1 argument}}
int f2() __attribute__((no_sanitize(1))); // expected-error{{'no_sanitize' attribute requires a string}}
// DUMP-LABEL: FunctionDecl {{.*}} f3
// DUMP: NoSanitizeAttr {{.*}} address
// PRINT: int f3() __attribute__((no_sanitize("address")))
int f3() __attribute__((no_sanitize("address")));
// DUMP-LABEL: FunctionDecl {{.*}} f4
// DUMP: NoSanitizeAttr {{.*}} thread
// PRINT: int f4() {{\[\[}}clang::no_sanitize("thread")]]
[[clang::no_sanitize("thread")]] int f4();
// DUMP-LABEL: FunctionDecl {{.*}} f5
// DUMP: NoSanitizeAttr {{.*}} address thread
// PRINT: int f5() __attribute__((no_sanitize("address", "thread")))
int f5() __attribute__((no_sanitize("address", "thread")));
// DUMP-LABEL: FunctionDecl {{.*}} f6
// DUMP: NoSanitizeAttr {{.*}} unknown
// PRINT: int f6() __attribute__((no_sanitize("unknown")))
int f6() __attribute__((no_sanitize("unknown"))); // expected-warning{{unknown sanitizer 'unknown' ignored}}

View File

@ -82,6 +82,7 @@ static std::string ReadPCHRecord(StringRef type) {
.Case("TypeSourceInfo *", "GetTypeSourceInfo(F, Record, Idx)")
.Case("Expr *", "ReadExpr(F)")
.Case("IdentifierInfo *", "GetIdentifierInfo(F, Record, Idx)")
.Case("std::string", "ReadString(Record, Idx)")
.Default("Record[Idx++]");
}
@ -95,6 +96,7 @@ static std::string WritePCHRecord(StringRef type, StringRef name) {
.Case("Expr *", "AddStmt(" + std::string(name) + ");\n")
.Case("IdentifierInfo *",
"AddIdentifierRef(" + std::string(name) + ", Record);\n")
.Case("std::string", "AddString(" + std::string(name) + ", Record);\n")
.Default("Record.push_back(" + std::string(name) + ");\n");
}
@ -983,6 +985,16 @@ namespace {
}
};
class VariadicStringArgument : public VariadicArgument {
public:
VariadicStringArgument(const Record &Arg, StringRef Attr)
: VariadicArgument(Arg, Attr, "std::string")
{}
void writeValueImpl(raw_ostream &OS) const override {
OS << " OS << \"\\\"\" << Val << \"\\\"\";\n";
}
};
class TypeArgument : public SimpleArgument {
public:
TypeArgument(const Record &Arg, StringRef Attr)
@ -1044,6 +1056,8 @@ createArgument(const Record &Arg, StringRef Attr,
Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "unsigned");
else if (ArgName == "VariadicUnsignedArgument")
Ptr = llvm::make_unique<VariadicArgument>(Arg, Attr, "unsigned");
else if (ArgName == "VariadicStringArgument")
Ptr = llvm::make_unique<VariadicStringArgument>(Arg, Attr);
else if (ArgName == "VariadicEnumArgument")
Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr);
else if (ArgName == "VariadicExprArgument")