[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