diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst index 357ebd9d93e0..ad30d0ae6a14 100644 --- a/clang/docs/AddressSanitizer.rst +++ b/clang/docs/AddressSanitizer.rst @@ -105,12 +105,13 @@ this purpose. # endif #endif -``__attribute__((no_address_safety_analysis))`` +``__attribute__((no_sanitize_address))`` ----------------------------------------------- Some code should not be instrumented by AddressSanitizer. One may use the function attribute -:ref:`no_address_safety_analysis ` +:ref:`no_sanitize_address ` +(or a deprecated synonym `no_address_safety_analysis`) to disable instrumentation of a particular function. This attribute may not be supported by other compilers, so we suggest to use it together with ``__has_feature(address_sanitizer)``. Note: currently, this attribute will be diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 014e2645d86b..c870d20b8763 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1616,10 +1616,38 @@ AddressSanitizer Use ``__has_feature(address_sanitizer)`` to check if the code is being built with :doc:`AddressSanitizer`. -Use ``__attribute__((no_address_safety_analysis))`` on a function declaration +Use ``__attribute__((no_sanitize_address))`` +on a function declaration to specify that address safety instrumentation (e.g. AddressSanitizer) should not be applied to that function. +.. _langext-thread_sanitizer: + +ThreadSanitizer +---------------- + +Use ``__has_feature(thread_sanitizer)`` to check if the code is being built +with :doc:`ThreadSanitizer`. + +Use ``__attribute__((no_sanitize_thread))`` on a function declaration +to specify that checks for data races on plain (non-atomic) memory accesses +should not be inserted by ThreadSanitizer. +The function may still be instrumented by the tool +to avoid false positives in other places. + +.. _langext-memory_sanitizer: + +MemorySanitizer +---------------- +Use ``__has_feature(memory_sanitizer)`` to check if the code is being built +with :doc:`MemorySanitizer`. + +Use ``__attribute__((no_sanitize_memory))`` on a function declaration +to specify that checks for uninitialized memory should not be inserted +(e.g. by MemorySanitizer). The function may still be instrumented by the tool +to avoid false positives in other places. + + Thread-Safety Annotation Checking ================================= diff --git a/clang/docs/MemorySanitizer.rst b/clang/docs/MemorySanitizer.rst index 6ede1c36e026..fdb8a810232d 100644 --- a/clang/docs/MemorySanitizer.rst +++ b/clang/docs/MemorySanitizer.rst @@ -80,6 +80,19 @@ whether MemorySanitizer is enabled. :ref:`\_\_has\_feature # endif #endif +``__attribute__((no_sanitize_memory))`` +----------------------------------------------- + +Some code should not be checked by MemorySanitizer. +One may use the function attribute +:ref:`no_sanitize_memory ` +to disable uninitialized checks in a particular function. +MemorySanitizer may still instrument such functions to avoid false positives. +This attribute may not be +supported by other compilers, so we suggest to use it together with +``__has_feature(memory_sanitizer)``. Note: currently, this attribute will be +lost if the function is inlined. + Origin Tracking =============== diff --git a/clang/docs/ThreadSanitizer.rst b/clang/docs/ThreadSanitizer.rst index a7f6eb782b5f..c0c576b44a5b 100644 --- a/clang/docs/ThreadSanitizer.rst +++ b/clang/docs/ThreadSanitizer.rst @@ -78,10 +78,25 @@ this purpose. .. code-block:: c - #if defined(__has_feature) && __has_feature(thread_sanitizer) + #if defined(__has_feature) + # if __has_feature(thread_sanitizer) // code that builds only under ThreadSanitizer + # endif #endif +``__attribute__((no_sanitize_thread))`` +----------------------------------------------- + +Some code should not be instrumented by ThreadSanitizer. +One may use the function attribute +:ref:`no_sanitize_thread ` +to disable instrumentation of plain (non-atomic) loads/stores in a particular function. +ThreadSanitizer may still instrument such functions to avoid false positives. +This attribute may not be +supported by other compilers, so we suggest to use it together with +``__has_feature(thread_sanitizer)``. Note: currently, this attribute will be +lost if the function is inlined. + Limitations ----------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 70217d2beb0e..3d4c3e026e2d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -767,9 +767,20 @@ def X86ForceAlignArgPointer : InheritableAttr { let Spellings = []; } -// AddressSafety attribute (e.g. for AddressSanitizer) -def NoAddressSafetyAnalysis : InheritableAttr { - let Spellings = [GNU<"no_address_safety_analysis">]; +// Attribute to disable AddressSanitizer (or equivalent) checks. +def NoSanitizeAddress : InheritableAttr { + let Spellings = [GNU<"no_address_safety_analysis">, + GNU<"no_sanitize_address">]; +} + +// Attribute to disable ThreadSanitizer checks. +def NoSanitizeThread : InheritableAttr { + let Spellings = [GNU<"no_sanitize_thread">]; +} + +// Attribute to disable MemorySanitizer checks. +def NoSanitizeMemory : InheritableAttr { + let Spellings = [GNU<"no_sanitize_memory">]; } // C/C++ Thread safety attributes (e.g. for deadlock, data race checking) diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index f9d8199bb846..a5884e35db60 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -233,7 +233,11 @@ CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, Fn->setDoesNotThrow(); if (CGM.getSanOpts().Address) - Fn->addFnAttr(llvm::Attribute::AddressSafety); + Fn->addFnAttr(llvm::Attribute::SanitizeAddress); + if (CGM.getSanOpts().Thread) + Fn->addFnAttr(llvm::Attribute::SanitizeThread); + if (CGM.getSanOpts().Memory) + Fn->addFnAttr(llvm::Attribute::SanitizeMemory); return Fn; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 6d1ea22aee5b..22aeee90875c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -617,12 +617,16 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, else if (LangOpts.getStackProtector() == LangOptions::SSPReq) F->addFnAttr(llvm::Attribute::StackProtectReq); - if (SanOpts.Address) { - // When AddressSanitizer is enabled, set AddressSafety attribute - // unless __attribute__((no_address_safety_analysis)) is used. - if (!D->hasAttr()) - F->addFnAttr(llvm::Attribute::AddressSafety); - } + // When AddressSanitizer is enabled, set SanitizeAddress attribute + // unless __attribute__((no_sanitize_address)) is used. + if (SanOpts.Address && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::SanitizeAddress); + // Same for ThreadSanitizer and __attribute__((no_sanitize_thread)) + if (SanOpts.Thread && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::SanitizeThread); + // Same for MemorySanitizer and __attribute__((no_sanitize_memory)) + if (SanOpts.Memory && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::SanitizeMemory); unsigned alignment = D->getMaxAlignment() / Context.getCharWidth(); if (alignment) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 5f6f81de148f..d7f423fd32d9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -610,8 +610,8 @@ static void handleScopedLockableAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } -static void handleNoThreadSafetyAttr(Sema &S, Decl *D, - const AttributeList &Attr) { +static void handleNoThreadSafetyAnalysis(Sema &S, Decl *D, + const AttributeList &Attr) { assert(!Attr.isInvalid()); if (!checkAttributeNumArgs(S, Attr, 0)) @@ -627,7 +627,7 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D, S.Context)); } -static void handleNoAddressSafetyAttr(Sema &S, Decl *D, +static void handleNoSanitizeAddressAttr(Sema &S, Decl *D, const AttributeList &Attr) { assert(!Attr.isInvalid()); @@ -635,14 +635,48 @@ static void handleNoAddressSafetyAttr(Sema &S, Decl *D, return; if (!isa(D) && !isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; } D->addAttr(::new (S.Context) - NoAddressSafetyAnalysisAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + NoSanitizeAddressAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); +} + +static void handleNoSanitizeMemory(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isa(D) && !isa(D)) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) NoSanitizeMemoryAttr(Attr.getRange(), + S.Context)); +} + +static void handleNoSanitizeThread(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isa(D) && !isa(D)) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) NoSanitizeThreadAttr(Attr.getRange(), + S.Context)); } static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, @@ -4817,11 +4851,17 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ScopedLockable: handleScopedLockableAttr(S, D, Attr); break; - case AttributeList::AT_NoAddressSafetyAnalysis: - handleNoAddressSafetyAttr(S, D, Attr); + case AttributeList::AT_NoSanitizeAddress: + handleNoSanitizeAddressAttr(S, D, Attr); break; case AttributeList::AT_NoThreadSafetyAnalysis: - handleNoThreadSafetyAttr(S, D, Attr); + handleNoThreadSafetyAnalysis(S, D, Attr); + break; + case AttributeList::AT_NoSanitizeThread: + handleNoSanitizeThread(S, D, Attr); + break; + case AttributeList::AT_NoSanitizeMemory: + handleNoSanitizeMemory(S, D, Attr); break; case AttributeList::AT_Lockable: handleLockableAttr(S, D, Attr); diff --git a/clang/test/CodeGen/address-safety-attr.cpp b/clang/test/CodeGen/address-safety-attr.cpp index a830e23da1b6..c9f6ddcab445 100644 --- a/clang/test/CodeGen/address-safety-attr.cpp +++ b/clang/test/CodeGen/address-safety-attr.cpp @@ -6,20 +6,20 @@ // FIXME: %t is like "src:x:\path\to\clang\test\CodeGen\address-safety-attr.cpp" // REQUIRES: shell -// The address_safety attribute should be attached to functions -// when AddressSanitizer is enabled, unless no_address_safety_analysis attribute +// The sanitize_address attribute should be attached to functions +// when AddressSanitizer is enabled, unless no_sanitize_address attribute // is present. // WITHOUT: NoAddressSafety1{{.*}}) #[[NOATTR:[0-9]+]] // BL: NoAddressSafety1{{.*}}) #[[NOATTR:[0-9]+]] // ASAN: NoAddressSafety1{{.*}}) #[[NOATTR:[0-9]+]] -__attribute__((no_address_safety_analysis)) +__attribute__((no_sanitize_address)) int NoAddressSafety1(int *a) { return *a; } // WITHOUT: NoAddressSafety2{{.*}}) #[[NOATTR]] // BL: NoAddressSafety2{{.*}}) #[[NOATTR]] // ASAN: NoAddressSafety2{{.*}}) #[[NOATTR]] -__attribute__((no_address_safety_analysis)) +__attribute__((no_sanitize_address)) int NoAddressSafety2(int *a); int NoAddressSafety2(int *a) { return *a; } @@ -38,13 +38,13 @@ int TemplateAddressSafetyOk() { return i; } // BL: TemplateNoAddressSafety{{.*}}) #[[NOATTR]] // ASAN: TemplateNoAddressSafety{{.*}}) #[[NOATTR]] template -__attribute__((no_address_safety_analysis)) +__attribute__((no_sanitize_address)) int TemplateNoAddressSafety() { return i; } int force_instance = TemplateAddressSafetyOk<42>() + TemplateNoAddressSafety<42>(); -// Check that __cxx_global_var_init* get the address_safety attribute. +// Check that __cxx_global_var_init* get the sanitize_address attribute. int global1 = 0; int global2 = *(int*)((char*)&global1+1); // WITHOUT: @__cxx_global_var_init{{.*}}#[[GVI:[0-9]+]] @@ -57,5 +57,5 @@ int global2 = *(int*)((char*)&global1+1); // BL: attributes #[[GVI]] = { nounwind{{.*}} } // ASAN: attributes #[[NOATTR]] = { nounwind{{.*}} } -// ASAN: attributes #[[WITH]] = { address_safety nounwind{{.*}} } -// ASAN: attributes #[[GVI]] = { address_safety nounwind{{.*}} } +// ASAN: attributes #[[WITH]] = {{.*}}sanitize_address +// ASAN: attributes #[[GVI]] = {{.*}}sanitize_address diff --git a/clang/test/CodeGen/sanitize-thread-attr.cpp b/clang/test/CodeGen/sanitize-thread-attr.cpp new file mode 100644 index 000000000000..09f4db575031 --- /dev/null +++ b/clang/test/CodeGen/sanitize-thread-attr.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck -check-prefix=WITHOUT %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefix=TSAN %s +// RUN: echo "src:%s" > %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -fsanitize=thread -fsanitize-blacklist=%t | FileCheck -check-prefix=BL %s + +// REQUIRES: shell + +// The sanitize_thread attribute should be attached to functions +// when ThreadSanitizer is enabled, unless no_sanitize_thread attribute +// is present. + +// WITHOUT: NoTSAN1{{.*}}) #[[NOATTR:[0-9]+]] +// BL: NoTSAN1{{.*}}) #[[NOATTR:[0-9]+]] +// TSAN: NoTSAN1{{.*}}) #[[NOATTR:[0-9]+]] +__attribute__((no_sanitize_thread)) +int NoTSAN1(int *a) { return *a; } + +// WITHOUT: NoTSAN2{{.*}}) #[[NOATTR]] +// BL: NoTSAN2{{.*}}) #[[NOATTR]] +// TSAN: NoTSAN2{{.*}}) #[[NOATTR]] +__attribute__((no_sanitize_thread)) +int NoTSAN2(int *a); +int NoTSAN2(int *a) { return *a; } + +// WITHOUT: TSANOk{{.*}}) #[[NOATTR]] +// BL: TSANOk{{.*}}) #[[NOATTR]] +// TSAN: TSANOk{{.*}}) #[[WITH:[0-9]+]] +int TSANOk(int *a) { return *a; } + +// WITHOUT: TemplateTSANOk{{.*}}) #[[NOATTR]] +// BL: TemplateTSANOk{{.*}}) #[[NOATTR]] +// TSAN: TemplateTSANOk{{.*}}) #[[WITH]] +template +int TemplateTSANOk() { return i; } + +// WITHOUT: TemplateNoTSAN{{.*}}) #[[NOATTR]] +// BL: TemplateNoTSAN{{.*}}) #[[NOATTR]] +// TSAN: TemplateNoTSAN{{.*}}) #[[NOATTR]] +template +__attribute__((no_sanitize_thread)) +int TemplateNoTSAN() { return i; } + +int force_instance = TemplateTSANOk<42>() + + TemplateNoTSAN<42>(); + +// Check that __cxx_global_var_init* get the sanitize_thread attribute. +int global1 = 0; +int global2 = *(int*)((char*)&global1+1); +// WITHOUT: @__cxx_global_var_init{{.*}}#[[GVI:[0-9]+]] +// BL: @__cxx_global_var_init{{.*}}#[[GVI:[0-9]+]] +// TSAN: @__cxx_global_var_init{{.*}}#[[GVI:[0-9]+]] + +// WITHOUT: attributes #[[NOATTR]] = { nounwind{{.*}} } +// WITHOUT: attributes #[[GVI]] = { nounwind{{.*}} } +// BL: attributes #[[NOATTR]] = { nounwind{{.*}} } +// BL: attributes #[[GVI]] = { nounwind{{.*}} } + +// TSAN: attributes #[[NOATTR]] = { nounwind{{.*}} } +// TSAN: attributes #[[WITH]] = { nounwind{{.*}} sanitize_thread +// TSAN: attributes #[[GVI]] = { nounwind{{.*}} sanitize_thread diff --git a/clang/test/CodeGenObjCXX/address-safety-attr.mm b/clang/test/CodeGenObjCXX/address-safety-attr.mm index cd9abaf1106c..1b6f0e807afd 100644 --- a/clang/test/CodeGenObjCXX/address-safety-attr.mm +++ b/clang/test/CodeGenObjCXX/address-safety-attr.mm @@ -17,5 +17,5 @@ @end -// WITHOUT: attributes #0 = { nounwind{{.*}} } -// ASAN: attributes #0 = { address_safety nounwind{{.*}} } +// ASAN: attributes #0 = {{.*}}sanitize_address +// WITHOUT-NOT: attributes #0 = {{.*}}sanitize_address diff --git a/clang/test/SemaCXX/attr-no-sanitize-address.cpp b/clang/test/SemaCXX/attr-no-sanitize-address.cpp new file mode 100644 index 000000000000..dc4d79758c16 --- /dev/null +++ b/clang/test/SemaCXX/attr-no-sanitize-address.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) + +#if !__has_attribute(no_sanitize_address) +#error "Should support no_sanitize_address" +#endif + +void noanal_fun() NO_SANITIZE_ADDRESS; + +void noanal_fun_args() __attribute__((no_sanitize_address(1))); // \ + // expected-error {{attribute takes no arguments}} + +int noanal_testfn(int y) NO_SANITIZE_ADDRESS; + +int noanal_testfn(int y) { + int x NO_SANITIZE_ADDRESS = y; // \ + // expected-error {{'no_sanitize_address' attribute only applies to functions and methods}} + return x; +} + +int noanal_test_var NO_SANITIZE_ADDRESS; // \ + // expected-error {{'no_sanitize_address' attribute only applies to functions and methods}} + +class NoanalFoo { + private: + int test_field NO_SANITIZE_ADDRESS; // \ + // expected-error {{'no_sanitize_address' attribute only applies to functions and methods}} + void test_method() NO_SANITIZE_ADDRESS; +}; + +class NO_SANITIZE_ADDRESS NoanalTestClass { // \ + // expected-error {{'no_sanitize_address' attribute only applies to functions and methods}} +}; + +void noanal_fun_params(int lvar NO_SANITIZE_ADDRESS); // \ + // expected-error {{'no_sanitize_address' attribute only applies to functions and methods}} diff --git a/clang/test/SemaCXX/attr-no-sanitize-memory.cpp b/clang/test/SemaCXX/attr-no-sanitize-memory.cpp new file mode 100644 index 000000000000..84acdaca838f --- /dev/null +++ b/clang/test/SemaCXX/attr-no-sanitize-memory.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) + +#if !__has_attribute(no_sanitize_memory) +#error "Should support no_sanitize_memory" +#endif + +void noanal_fun() NO_SANITIZE_MEMORY; + +void noanal_fun_args() __attribute__((no_sanitize_memory(1))); // \ + // expected-error {{attribute takes no arguments}} + +int noanal_testfn(int y) NO_SANITIZE_MEMORY; + +int noanal_testfn(int y) { + int x NO_SANITIZE_MEMORY = y; // \ + // expected-error {{'no_sanitize_memory' attribute only applies to functions and methods}} + return x; +} + +int noanal_test_var NO_SANITIZE_MEMORY; // \ + // expected-error {{'no_sanitize_memory' attribute only applies to functions and methods}} + +class NoanalFoo { + private: + int test_field NO_SANITIZE_MEMORY; // \ + // expected-error {{'no_sanitize_memory' attribute only applies to functions and methods}} + void test_method() NO_SANITIZE_MEMORY; +}; + +class NO_SANITIZE_MEMORY NoanalTestClass { // \ + // expected-error {{'no_sanitize_memory' attribute only applies to functions and methods}} +}; + +void noanal_fun_params(int lvar NO_SANITIZE_MEMORY); // \ + // expected-error {{'no_sanitize_memory' attribute only applies to functions and methods}} diff --git a/clang/test/SemaCXX/attr-no-sanitize-thread.cpp b/clang/test/SemaCXX/attr-no-sanitize-thread.cpp new file mode 100644 index 000000000000..50960c42b931 --- /dev/null +++ b/clang/test/SemaCXX/attr-no-sanitize-thread.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) + +#if !__has_attribute(no_sanitize_thread) +#error "Should support no_sanitize_thread" +#endif + +void noanal_fun() NO_SANITIZE_THREAD; + +void noanal_fun_args() __attribute__((no_sanitize_thread(1))); // \ + // expected-error {{attribute takes no arguments}} + +int noanal_testfn(int y) NO_SANITIZE_THREAD; + +int noanal_testfn(int y) { + int x NO_SANITIZE_THREAD = y; // \ + // expected-error {{'no_sanitize_thread' attribute only applies to functions and methods}} + return x; +} + +int noanal_test_var NO_SANITIZE_THREAD; // \ + // expected-error {{'no_sanitize_thread' attribute only applies to functions and methods}} + +class NoanalFoo { + private: + int test_field NO_SANITIZE_THREAD; // \ + // expected-error {{'no_sanitize_thread' attribute only applies to functions and methods}} + void test_method() NO_SANITIZE_THREAD; +}; + +class NO_SANITIZE_THREAD NoanalTestClass { // \ + // expected-error {{'no_sanitize_thread' attribute only applies to functions and methods}} +}; + +void noanal_fun_params(int lvar NO_SANITIZE_THREAD); // \ + // expected-error {{'no_sanitize_thread' attribute only applies to functions and methods}}