[PATCH] Implement no_sanitize attribute.
Aaron Ballman
aaron at aaronballman.com
Fri May 15 07:06:22 PDT 2015
On Thu, May 14, 2015 at 9:10 PM, Peter Collingbourne <peter at pcc.me.uk> wrote:
> On Thu, May 14, 2015 at 07:19:40PM -0400, Aaron Ballman wrote:
>> On Thu, May 14, 2015 at 6:34 PM, Peter Collingbourne <peter at pcc.me.uk> wrote:
>> > On Thu, May 14, 2015 at 04:44:45PM -0400, Aaron Ballman wrote:
>> >> I was unclear, I apologize. I was thinking along the lines of:
>> >>
>> >>
>> >> def NoSanitizeAttrs : InheritableAttr {
>> >> let Spellings = [GNU<"no_sanitize_thread">, GNU<"no_sanitize_memory">, etc];
>> >> let ASTNode = 0; // No AST representations; creates a NoSanitizeAttr
>> >> object instead
>> >> let Subjects = SubjectList<[Function], ErrorDiag>;
>> >> let Documentation = [Undocumented];
>> >> }
>> >>
>> >> Then in SemaDeclAttr.cpp, the handler for all these attributes can be
>> >> implemented as instead attaching a no_sanitize attribute with the
>> >> proper mask. The downside to this is that it could lose source
>> >> fidelity when pretty printing (it would be equivalent, but not
>> >> identical attributes in that case). The upside is that we're not
>> >> carrying around AST nodes for these things, and we can remove some of
>> >> the clutter in Attr.td.
>> >
>> > But we can't document the attributes properly that way, right? We probably want
>> > to keep the attribute documentation as these attributes are GCC-compatible.
>>
>> We can keep the attributes documented. I put Undocumented because I
>> forgot that we had a better way to handle it. :-) The Documentation
>> tablegen type has a Deprecated field. So the NoSanitizeFooDocs for
>> each of the deprecated ones would have its Deprecated field filled
>> out, pointing to the NoSanitizeDocs replacement. Then the
>> Documentation for the conglomerate definition would be: let
>> Documentation = [NoSanitizeAddressDocs, NoSanitizeThreadDocs,
>> NoSanitizeMemoryDocs]; which should cover the documentation needs.
>
> Done.
LGTM, with some incredibly minor nits below. Thank you for all the
work you've done on this!
> Index: docs/UsersManual.rst
> ===================================================================
> --- docs/UsersManual.rst
> +++ docs/UsersManual.rst
> @@ -930,6 +930,8 @@
> and the precompiled header cannot be generated after headers have been
> installed.
>
> +.. _controlling-code-generation:
> +
> Controlling Code Generation
> ---------------------------
>
> Index: include/clang/AST/Attr.h
> ===================================================================
> --- include/clang/AST/Attr.h
> +++ include/clang/AST/Attr.h
> @@ -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"
> Index: include/clang/Basic/Attr.td
> ===================================================================
> --- include/clang/Basic/Attr.td
> +++ include/clang/Basic/Attr.td
> @@ -144,6 +144,7 @@
> 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 @@
> 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">];
Does GCC support no_sanitize_memory? If so, we should switch that to
be a GCC spelling for compatibility (despite not being the preferred
spelling now).
> 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)
> Index: include/clang/Basic/AttrDocs.td
> ===================================================================
> --- include/clang/Basic/AttrDocs.td
> +++ include/clang/Basic/AttrDocs.td
> @@ -920,6 +920,22 @@
> }];
> }
>
> +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 @@
>
> def NoSanitizeThreadDocs : Documentation {
> let Category = DocCatFunction;
> + let Heading = "no_sanitize_thread";
> let Content = [{
> .. _langext-thread_sanitizer:
>
> @@ -948,6 +965,7 @@
>
> def NoSanitizeMemoryDocs : Documentation {
> let Category = DocCatFunction;
> + let Heading = "no_sanitize_memory";
> let Content = [{
> .. _langext-memory_sanitizer:
>
> Index: include/clang/Basic/DiagnosticGroups.td
> ===================================================================
> --- include/clang/Basic/DiagnosticGroups.td
> +++ include/clang/Basic/DiagnosticGroups.td
> @@ -401,6 +401,7 @@
> 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">;
> Index: include/clang/Basic/DiagnosticSemaKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticSemaKinds.td
> +++ include/clang/Basic/DiagnosticSemaKinds.td
> @@ -2519,6 +2519,10 @@
> "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;
> Index: lib/CodeGen/CodeGenFunction.cpp
> ===================================================================
> --- lib/CodeGen/CodeGenFunction.cpp
> +++ lib/CodeGen/CodeGenFunction.cpp
> @@ -608,6 +608,20 @@
> 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.
> Index: lib/CodeGen/CodeGenModule.cpp
> ===================================================================
> --- lib/CodeGen/CodeGenModule.cpp
> +++ lib/CodeGen/CodeGenModule.cpp
> @@ -752,23 +752,6 @@
> 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));
> Index: lib/Sema/SemaDeclAttr.cpp
> ===================================================================
> --- lib/Sema/SemaDeclAttr.cpp
> +++ lib/Sema/SemaDeclAttr.cpp
> @@ -4354,6 +4354,44 @@
> 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("");
I would remove the .Default case. We want to assert if we fall off the switch.
> + 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 +4860,15 @@
> 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;
> Index: test/CodeGen/address-safety-attr.cpp
> ===================================================================
> --- test/CodeGen/address-safety-attr.cpp
> +++ test/CodeGen/address-safety-attr.cpp
> @@ -3,14 +3,14 @@
> // 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 @@
> 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 @@
> 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;
> Index: test/CodeGen/sanitize-thread-attr.cpp
> ===================================================================
> --- test/CodeGen/sanitize-thread-attr.cpp
> +++ test/CodeGen/sanitize-thread-attr.cpp
> @@ -22,6 +22,12 @@
> 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]+]]
> Index: test/CodeGenCXX/cfi-vcall.cpp
> ===================================================================
> --- test/CodeGenCXX/cfi-vcall.cpp
> +++ test/CodeGenCXX/cfi-vcall.cpp
> @@ -47,16 +47,32 @@
> 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}
> Index: test/CodeGenObjC/no-sanitize.m
> ===================================================================
> --- /dev/null
> +++ test/CodeGenObjC/no-sanitize.m
> @@ -0,0 +1,8 @@
> +// RUN: %clang_cc1 %s -emit-llvm -fsanitize=address -o - | FileCheck %s
> +
> + at interface I0 @end
> + at implementation I0
> +// CHECK-NOT: sanitize_address
> +- (void) im0: (int) a0 __attribute__((no_sanitize("address"))) {
> +}
> + at end
> Index: test/SemaCXX/attr-no-sanitize.cpp
> ===================================================================
> --- /dev/null
> +++ test/SemaCXX/attr-no-sanitize.cpp
> @@ -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}}
> Index: utils/TableGen/ClangAttrEmitter.cpp
> ===================================================================
> --- utils/TableGen/ClangAttrEmitter.cpp
> +++ utils/TableGen/ClangAttrEmitter.cpp
> @@ -82,6 +82,7 @@
> .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 @@
> .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 @@
> }
> };
>
> + 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 @@
> 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")
>
~Aaron
More information about the cfe-commits
mailing list