Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -1598,6 +1598,33 @@ 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_thread_safety_analysis))`` 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_uninitialized_checks))`` 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 ================================= @@ -1614,6 +1641,7 @@ specify that the thread safety analysis should not be run on that function. This attribute provides an escape hatch (e.g. for situations when it is difficult to annotate the locking policy). +This attribute is also used by :ref:`ThreadSanitizer `. ``lockable`` ------------ Index: docs/MemorySanitizer.rst =================================================================== --- docs/MemorySanitizer.rst +++ docs/MemorySanitizer.rst @@ -80,6 +80,19 @@ # endif #endif +``__attribute__((no_uninitialized_checks))`` +----------------------------------------------- + +Some code should not be checked by MemorySanitizer. +One may use the function attribute +:ref:`no_uninitialized_checks ` +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 =============== Index: docs/ThreadSanitizer.rst =================================================================== --- docs/ThreadSanitizer.rst +++ docs/ThreadSanitizer.rst @@ -78,10 +78,25 @@ .. 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_thread_safety_analysis))`` +----------------------------------------------- + +Some code should not be instrumented by ThreadSanitizer. +One may use the function attribute +:ref:`no_thread_safety_analysis ` +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 ----------- Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -764,6 +764,11 @@ let Spellings = [GNU<"no_address_safety_analysis">]; } +// UninitializedChecks attribute (e.g. for MemorySanitizer) +def NoUninitializedChecks : InheritableAttr { + let Spellings = [GNU<"no_uninitialized_checks">]; +} + // C/C++ Thread safety attributes (e.g. for deadlock, data race checking) def GuardedVar : InheritableAttr { @@ -782,6 +787,7 @@ let Spellings = [GNU<"scoped_lockable">]; } +// ThreadSafety attribute (e.g. for ThreadSanitizer or static analysis) def NoThreadSafetyAnalysis : InheritableAttr { let Spellings = [GNU<"no_thread_safety_analysis">]; } Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -234,6 +234,10 @@ if (CGM.getSanOpts().Address) Fn->addFnAttr(llvm::Attribute::AddressSafety); + if (CGM.getSanOpts().Thread) + Fn->addFnAttr(llvm::Attribute::ThreadSafety); + if (CGM.getSanOpts().Memory) + Fn->addFnAttr(llvm::Attribute::UninitializedChecks); return Fn; } Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -615,12 +615,16 @@ 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 AddressSafety attribute + // unless __attribute__((no_address_safety_analysis)) is used. + if (SanOpts.Address && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::AddressSafety); + // Same for ThreadSanitizer and __attribute__((no_thread_safety_analysis)) + if (SanOpts.Thread && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::ThreadSafety); + // Same for MemorySanitizer and __attribute__((no_uninitialized_checks)) + if (SanOpts.Memory && !D->hasAttr()) + F->addFnAttr(llvm::Attribute::UninitializedChecks); unsigned alignment = D->getMaxAlignment() / Context.getCharWidth(); if (alignment) Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -634,7 +634,7 @@ 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; } @@ -644,6 +644,23 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNoUninitializedChecksAttr(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) NoUninitializedChecksAttr(Attr.getRange(), + S.Context)); +} + static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const AttributeList &Attr, SmallVector &Args) { @@ -4774,6 +4791,9 @@ case AttributeList::AT_NoThreadSafetyAnalysis: handleNoThreadSafetyAttr(S, D, Attr); break; + case AttributeList::AT_NoUninitializedChecks: + handleNoUninitializedChecksAttr(S, D, Attr); + break; case AttributeList::AT_Lockable: handleLockableAttr(S, D, Attr); break; Index: test/CodeGen/thread-safety-attr.cpp =================================================================== --- test/CodeGen/thread-safety-attr.cpp +++ test/CodeGen/thread-safety-attr.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefix TSAN %s +// RUN: echo "src:%s" > %t +// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=thread -fsanitize-blacklist=%t | FileCheck %s + +// REQUIRES: shell + +// The thread_safety attribute should be attached to functions +// when ThreadSanitizer is enabled, unless no_thread_safety_analysis attribute +// is present. + +// CHECK-NOT: NoThreadSafety1{{.*}} thread_safety +// TSAN-NOT: NoThreadSafety1{{.*}} thread_safety +__attribute__((no_thread_safety_analysis)) +int NoThreadSafety1(int *a) { return *a; } + +// CHECK-NOT: NoThreadSafety2{{.*}} thread_safety +// TSAN-NOT: NoThreadSafety2{{.*}} thread_safety +__attribute__((no_thread_safety_analysis)) +int NoThreadSafety2(int *a); +int NoThreadSafety2(int *a) { return *a; } + +// CHECK-NOT: ThreadSafetyOk{{.*}} thread_safety +// TSAN: ThreadSafetyOk{{.*}} thread_safety +int ThreadSafetyOk(int *a) { return *a; } + +// CHECK-NOT: TemplateNoThreadSafety{{.*}} thread_safety +// TSAN-NOT: TemplateNoThreadSafety{{.*}} thread_safety +template +__attribute__((no_thread_safety_analysis)) +int TemplateNoThreadSafety() { return i; } + +// CHECK-NOT: TemplateThreadSafetyOk{{.*}} thread_safety +// TSAN: TemplateThreadSafetyOk{{.*}} thread_safety +template +int TemplateThreadSafetyOk() { return i; } + +int force_instance = TemplateThreadSafetyOk<42>() + + TemplateNoThreadSafety<42>(); + +// Check that __cxx_global_var_init* get the thread_safety attribute. +int global1 = 0; +int global2 = *(int*)((char*)&global1+1); +// CHECK-NOT: @__cxx_global_var_init{{.*}}thread_safety +// TSAN: @__cxx_global_var_init{{.*}}thread_safety Index: test/CodeGen/uninitialized-checks-attr.cpp =================================================================== --- test/CodeGen/uninitialized-checks-attr.cpp +++ test/CodeGen/uninitialized-checks-attr.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=memory | FileCheck -check-prefix MSAN %s +// RUN: echo "src:%s" > %t +// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=memory -fsanitize-blacklist=%t | FileCheck %s + +// REQUIRES: shell + +// The uninitialized_checks attribute should be attached to functions +// when MemorySanitizer is enabled, unless no_uninitialized_checks attribute +// is present. + +// CHECK-NOT: NoUninitializedChecks1{{.*}} uninitialized_checks +// MSAN-NOT: NoUninitializedChecks1{{.*}} uninitialized_checks +__attribute__((no_uninitialized_checks)) +int NoUninitializedChecks1(int *a) { return *a; } + +// CHECK-NOT: NoUninitializedChecks2{{.*}} uninitialized_checks +// MSAN-NOT: NoUninitializedChecks2{{.*}} uninitialized_checks +__attribute__((no_uninitialized_checks)) +int NoUninitializedChecks2(int *a); +int NoUninitializedChecks2(int *a) { return *a; } + +// CHECK-NOT: UninitializedChecksOk{{.*}} uninitialized_checks +// MSAN: UninitializedChecksOk{{.*}} uninitialized_checks +int UninitializedChecksOk(int *a) { return *a; } + +// CHECK-NOT: TemplateNoUninitializedChecks{{.*}} uninitialized_checks +// MSAN-NOT: TemplateNoUninitializedChecks{{.*}} uninitialized_checks +template +__attribute__((no_uninitialized_checks)) +int TemplateNoUninitializedChecks() { return i; } + +// CHECK-NOT: TemplateUninitializedChecksOk{{.*}} uninitialized_checks +// MSAN: TemplateUninitializedChecksOk{{.*}} uninitialized_checks +template +int TemplateUninitializedChecksOk() { return i; } + +int force_instance = TemplateUninitializedChecksOk<42>() + + TemplateNoUninitializedChecks<42>(); + +// Check that __cxx_global_var_init* get the uninitialized_checks attribute. +int global1 = 0; +int global2 = *(int*)((char*)&global1+1); +// CHECK-NOT: @__cxx_global_var_init{{.*}}uninitialized_checks +// MSAN: @__cxx_global_var_init{{.*}}uninitialized_checks Index: test/SemaCXX/attr-no-address-safety-analysis.cpp =================================================================== --- test/SemaCXX/attr-no-address-safety-analysis.cpp +++ test/SemaCXX/attr-no-address-safety-analysis.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NO_ADDRESS_SAFETY_ANALYSIS __attribute__((no_address_safety_analysis)) +#if !__has_attribute(no_address_safety_analysis) +#error "Should support no_address_safety_analysis" +#endif + +void noanal_fun() NO_ADDRESS_SAFETY_ANALYSIS; + +void noanal_fun_args() __attribute__((no_address_safety_analysis(1))); // \ + // expected-error {{attribute takes no arguments}} + +int noanal_testfn(int y) NO_ADDRESS_SAFETY_ANALYSIS; + +int noanal_testfn(int y) { + int x NO_ADDRESS_SAFETY_ANALYSIS = y; // \ + // expected-error {{'no_address_safety_analysis' attribute only applies to functions and methods}} + return x; +} + +int noanal_test_var NO_ADDRESS_SAFETY_ANALYSIS; // \ + // expected-error {{'no_address_safety_analysis' attribute only applies to functions and methods}} + +class NoanalFoo { + private: + int test_field NO_ADDRESS_SAFETY_ANALYSIS; // \ + // expected-error {{'no_address_safety_analysis' attribute only applies to functions and methods}} + void test_method() NO_ADDRESS_SAFETY_ANALYSIS; +}; + +class NO_ADDRESS_SAFETY_ANALYSIS NoanalTestClass { // \ + // expected-error {{'no_address_safety_analysis' attribute only applies to functions and methods}} +}; + +void noanal_fun_params(int lvar NO_ADDRESS_SAFETY_ANALYSIS); // \ + // expected-error {{'no_address_safety_analysis' attribute only applies to functions and methods}} Index: test/SemaCXX/attr-no-uninitialized-checks.cpp =================================================================== --- test/SemaCXX/attr-no-uninitialized-checks.cpp +++ test/SemaCXX/attr-no-uninitialized-checks.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NO_UNINITIALIZED_CHECKS __attribute__((no_uninitialized_checks)) +#if !__has_attribute(no_uninitialized_checks) +#error "Should support no_uninitialized_checks" +#endif + +void noanal_fun() NO_UNINITIALIZED_CHECKS; + +void noanal_fun_args() __attribute__((no_uninitialized_checks(1))); // \ + // expected-error {{attribute takes no arguments}} + +int noanal_testfn(int y) NO_UNINITIALIZED_CHECKS; + +int noanal_testfn(int y) { + int x NO_UNINITIALIZED_CHECKS = y; // \ + // expected-error {{'no_uninitialized_checks' attribute only applies to functions and methods}} + return x; +} + +int noanal_test_var NO_UNINITIALIZED_CHECKS; // \ + // expected-error {{'no_uninitialized_checks' attribute only applies to functions and methods}} + +class NoanalFoo { + private: + int test_field NO_UNINITIALIZED_CHECKS; // \ + // expected-error {{'no_uninitialized_checks' attribute only applies to functions and methods}} + void test_method() NO_UNINITIALIZED_CHECKS; +}; + +class NO_UNINITIALIZED_CHECKS NoanalTestClass { // \ + // expected-error {{'no_uninitialized_checks' attribute only applies to functions and methods}} +}; + +void noanal_fun_params(int lvar NO_UNINITIALIZED_CHECKS); // \ + // expected-error {{'no_uninitialized_checks' attribute only applies to functions and methods}}