Skip to content

Commit

Permalink
Implement no_sanitize attribute.
Browse files Browse the repository at this point in the history
Differential Revision: http://reviews.llvm.org/D9631

llvm-svn: 237463
  • Loading branch information
pcc committed May 15, 2015
1 parent 25e2500 commit 915df99
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 55 deletions.
2 changes: 2 additions & 0 deletions clang/docs/UsersManual.rst
Expand Up @@ -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
---------------------------

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/Attr.h
Expand Up @@ -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"
Expand Down
44 changes: 27 additions & 17 deletions clang/include/clang/Basic/Attr.td
Expand Up @@ -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>;
Expand Down Expand Up @@ -1386,26 +1387,35 @@ def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr<TargetX86> {
let Documentation = [Undocumented];
}

// Attribute to disable AddressSanitizer (or equivalent) checks.
def NoSanitizeAddress : InheritableAttr {
let Spellings = [GCC<"no_address_safety_analysis">,
GCC<"no_sanitize_address">];
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];
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;
}
}];
}

// Attribute to disable MemorySanitizer checks.
def NoSanitizeMemory : InheritableAttr {
let Spellings = [GNU<"no_sanitize_memory">];
// 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_thread">,
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)
Expand Down
18 changes: 18 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Expand Up @@ -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
Expand All @@ -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:

Expand All @@ -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:

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Expand Up @@ -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">;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -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.
Expand Down
17 changes: 0 additions & 17 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -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));
Expand Down
52 changes: 44 additions & 8 deletions clang/lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
59 changes: 49 additions & 10 deletions clang/test/CodeGen/address-safety-attr.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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]]
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions clang/test/CodeGen/sanitize-thread-attr.cpp
Expand Up @@ -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]+]]
Expand Down
22 changes: 19 additions & 3 deletions clang/test/CodeGenCXX/cfi-vcall.cpp
Expand Up @@ -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}
Expand Down
8 changes: 8 additions & 0 deletions clang/test/CodeGenObjC/no-sanitize.m
@@ -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

0 comments on commit 915df99

Please sign in to comment.