https://github.com/melver updated https://github.com/llvm/llvm-project/pull/127396
>From a70f021becb2888d6c2a63b2d1e619393a996058 Mon Sep 17 00:00:00 2001 From: Marco Elver <el...@google.com> Date: Sun, 16 Feb 2025 14:53:41 +0100 Subject: [PATCH 1/2] Thread Safety Analysis: Handle address-of followed by dereference Correctly analyze expressions where the address of a guarded variable is taken and immediately dereferenced, such as (*(type-specifier *)&x). Previously, such patterns would result in false negatives. --- clang/lib/Analysis/ThreadSafety.cpp | 10 ++++++++++ clang/test/Sema/warn-thread-safety-analysis.c | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index bfaf1a0e7c7ff..260505b71c17c 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -1765,6 +1765,8 @@ void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp, void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK, ProtectedOperationKind POK) { + // Strips off paren- and cast-expressions, checking if we encounter any other + // operator that should be delegated to checkAccess() instead. while (true) { if (const auto *PE = dyn_cast<ParenExpr>(Exp)) { Exp = PE->getSubExpr(); @@ -1780,6 +1782,14 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp, Exp = CE->getSubExpr(); continue; } + if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) { + if (UO->getOpcode() == UO_AddrOf) { + // Pointer access via pointer taken of variable, so the dereferenced + // variable is not actually a pointer. + checkAccess(FSet, UO->getSubExpr(), AK, POK); + return; + } + } break; } diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c index 73b4e4798f644..8a0d44cd4bea9 100644 --- a/clang/test/Sema/warn-thread-safety-analysis.c +++ b/clang/test/Sema/warn-thread-safety-analysis.c @@ -24,6 +24,9 @@ __attribute__ ((shared_locks_required(__VA_ARGS__))) #define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis)) +#define __READ_ONCE(x) (*(const volatile __typeof__(x) *)&(x)) +#define __WRITE_ONCE(x, val) do { *(volatile __typeof__(x) *)&(x) = (val); } while (0) + // Define the mutex struct. // Simplified only for test purpose. struct LOCKABLE Mutex {}; @@ -142,9 +145,19 @@ int main(void) { (void)(get_value(b_) == 1); mutex_unlock(foo_.mu_); + a_ = 0; // expected-warning{{writing variable 'a_' requires holding mutex 'foo_.mu_'}} + __WRITE_ONCE(a_, 0); // expected-warning{{writing variable 'a_' requires holding mutex 'foo_.mu_'}} + (void)(a_ == 0); // expected-warning{{reading variable 'a_' requires holding mutex 'foo_.mu_'}} + (void)(__READ_ONCE(a_) == 0); // expected-warning{{reading variable 'a_' requires holding mutex 'foo_.mu_'}} + (void)(*b_ == 0); // expected-warning{{reading the value pointed to by 'b_' requires holding mutex 'foo_.mu_'}} c_ = 0; // expected-warning{{writing variable 'c_' requires holding any mutex exclusively}} (void)(*d_ == 0); // expected-warning{{reading the value pointed to by 'd_' requires holding any mutex}} mutex_exclusive_lock(foo_.mu_); + a_ = 0; + __WRITE_ONCE(a_, 0); + (void)(a_ == 0); + (void)(__READ_ONCE(a_) == 0); + (void)(*b_ == 0); c_ = 1; (void)(*d_ == 1); mutex_unlock(foo_.mu_); >From 7a84fdae119fe45f800a9913e5cbd8230bd1c588 Mon Sep 17 00:00:00 2001 From: Marco Elver <el...@google.com> Date: Sun, 16 Feb 2025 12:42:06 +0100 Subject: [PATCH 2/2] Thread Safety Analysis: Support warning on passing/returning pointers to guarded variables Introduce `-Wthread-safety-pointer` (under `-Wthread-safety-beta`) to warn when passing or returning pointers to guarded variables or guarded data. This is is analogous to `-Wthread-safety-reference`, which performs similar checks for C++ references. Adding checks for pointer passing is required to avoid false negatives in large C codebases, where data structures are typically implemented through helpers that take pointers to instances of a data structure. --- clang/docs/ReleaseNotes.rst | 6 + clang/docs/ThreadSafetyAnalysis.rst | 6 +- .../clang/Analysis/Analyses/ThreadSafety.h | 12 ++ clang/include/clang/Basic/DiagnosticGroups.td | 4 +- .../clang/Basic/DiagnosticSemaKinds.td | 18 ++ clang/lib/Analysis/ThreadSafety.cpp | 24 ++- clang/lib/Sema/AnalysisBasedWarnings.cpp | 24 +++ clang/test/Sema/warn-thread-safety-analysis.c | 4 + .../SemaCXX/warn-thread-safety-analysis.cpp | 179 +++++++++++++++++- 9 files changed, 272 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index efaacdf18d50a..0060bece2fb08 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -143,6 +143,12 @@ Improvements to Clang's diagnostics - A statement attribute applied to a ``case`` label no longer suppresses 'bypassing variable initialization' diagnostics (#84072). +- The :doc:`ThreadSafetyAnalysis` now supports ``-Wthread-safety-pointer`` + (with ``-Wthread-safety-beta``), which enables warning on passing or + returning pointers to guarded variables as function arguments or return value + respectively. Note that :doc:`ThreadSafetyAnalysis` still does not perform + alias analysis. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst index 9c1c32e46989b..1e7950067f608 100644 --- a/clang/docs/ThreadSafetyAnalysis.rst +++ b/clang/docs/ThreadSafetyAnalysis.rst @@ -515,7 +515,8 @@ Warning flags + ``-Wthread-safety-analysis``: The core analysis. + ``-Wthread-safety-precise``: Requires that mutex expressions match precisely. This warning can be disabled for code which has a lot of aliases. - + ``-Wthread-safety-reference``: Checks when guarded members are passed by reference. + + ``-Wthread-safety-reference``: Checks when guarded members are passed or + returned by reference. :ref:`negative` are an experimental feature, which are enabled with: @@ -528,6 +529,9 @@ for a period of time, after which they are migrated into the standard analysis. * ``-Wthread-safety-beta``: New features. Off by default. + + ``-Wthread-safety-pointer``: Checks when passing or returning pointers to + guarded variables, or pointers to guarded data, as function argument or + return value respectively. .. _negative: diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafety.h b/clang/include/clang/Analysis/Analyses/ThreadSafety.h index 0fcf5bed1285a..20b75c46593e0 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafety.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafety.h @@ -54,6 +54,18 @@ enum ProtectedOperationKind { /// Returning a pt-guarded variable by reference. POK_PtReturnByRef, + + /// Passing pointer to a guarded variable. + POK_PassPointer, + + /// Passing a pt-guarded pointer. + POK_PtPassPointer, + + /// Returning pointer to a guarded variable. + POK_ReturnPointer, + + /// Returning a pt-guarded pointer. + POK_PtReturnPointer, }; /// This enum distinguishes between different kinds of lock actions. For diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 05e39899e6f25..94c44cd199ed9 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1156,6 +1156,7 @@ def ThreadSafetyPrecise : DiagGroup<"thread-safety-precise">; def ThreadSafetyReferenceReturn : DiagGroup<"thread-safety-reference-return">; def ThreadSafetyReference : DiagGroup<"thread-safety-reference", [ThreadSafetyReferenceReturn]>; +def ThreadSafetyPointer : DiagGroup<"thread-safety-pointer">; def ThreadSafetyNegative : DiagGroup<"thread-safety-negative">; def ThreadSafety : DiagGroup<"thread-safety", [ThreadSafetyAttributes, @@ -1163,7 +1164,8 @@ def ThreadSafety : DiagGroup<"thread-safety", ThreadSafetyPrecise, ThreadSafetyReference]>; def ThreadSafetyVerbose : DiagGroup<"thread-safety-verbose">; -def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">; +def ThreadSafetyBeta : DiagGroup<"thread-safety-beta", + [ThreadSafetyPointer]>; // Warnings and notes related to the function effects system which underlies // the nonblocking and nonallocating attributes. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f10af8f5bd6b2..ad5310145c841 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4135,6 +4135,24 @@ def warn_pt_guarded_return_by_reference : Warning< "%select{'%2'|'%2' exclusively}3">, InGroup<ThreadSafetyReferenceReturn>, DefaultIgnore; +// Thread safety warnings on pointer passing +def warn_guarded_pass_pointer : Warning< + "passing pointer to variable %1 requires holding %0 " + "%select{'%2'|'%2' exclusively}3">, + InGroup<ThreadSafetyPointer>, DefaultIgnore; +def warn_pt_guarded_pass_pointer : Warning< + "passing pointer %1 requires holding %0 " + "%select{'%2'|'%2' exclusively}3">, + InGroup<ThreadSafetyPointer>, DefaultIgnore; +def warn_guarded_return_pointer : Warning< + "returning pointer to variable %1 requires holding %0 " + "%select{'%2'|'%2' exclusively}3">, + InGroup<ThreadSafetyPointer>, DefaultIgnore; +def warn_pt_guarded_return_pointer : Warning< + "returning pointer %1 requires holding %0 " + "%select{'%2'|'%2' exclusively}3">, + InGroup<ThreadSafetyPointer>, DefaultIgnore; + // Imprecise thread safety warnings def warn_variable_requires_lock : Warning< "%select{reading|writing}3 variable %1 requires holding %0 " diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index 260505b71c17c..29c46dd24115c 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -1795,9 +1795,22 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp, // Pass by reference warnings are under a different flag. ProtectedOperationKind PtPOK = POK_VarDereference; - if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef; - if (POK == POK_ReturnByRef) + switch (POK) { + case POK_PassByRef: + PtPOK = POK_PtPassByRef; + break; + case POK_ReturnByRef: PtPOK = POK_PtReturnByRef; + break; + case POK_PassPointer: + PtPOK = POK_PtPassPointer; + break; + case POK_ReturnPointer: + PtPOK = POK_PtReturnPointer; + break; + default: + break; + } const ValueDecl *D = getValueDecl(Exp); if (!D || !D->hasAttrs()) @@ -2144,6 +2157,8 @@ void BuildLockset::examineArguments(const FunctionDecl *FD, QualType Qt = (*Param)->getType(); if (Qt->isReferenceType()) checkAccess(*Arg, AK_Read, POK_PassByRef); + else if (Qt->isPointerType()) + checkPtAccess(*Arg, AK_Read, POK_PassPointer); } } @@ -2294,6 +2309,11 @@ void BuildLockset::VisitReturnStmt(const ReturnStmt *S) { FunctionExitFSet, RetVal, ReturnType->getPointeeType().isConstQualified() ? AK_Read : AK_Written, POK_ReturnByRef); + } else if (ReturnType->isPointerType()) { + Analyzer->checkPtAccess( + FunctionExitFSet, RetVal, + ReturnType->getPointeeType().isConstQualified() ? AK_Read : AK_Written, + POK_ReturnPointer); } } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 589869d018657..f0ea6cfbf33f9 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2021,6 +2021,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { case POK_PtReturnByRef: DiagID = diag::warn_pt_guarded_return_by_reference; break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind << D @@ -2057,6 +2069,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { case POK_PtReturnByRef: DiagID = diag::warn_pt_guarded_return_by_reference; break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind << D diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c index 8a0d44cd4bea9..ad072296100de 100644 --- a/clang/test/Sema/warn-thread-safety-analysis.c +++ b/clang/test/Sema/warn-thread-safety-analysis.c @@ -137,7 +137,9 @@ int main(void) { Foo_func3(5); set_value(&a_, 0); // expected-warning{{calling function 'set_value' requires holding mutex 'foo_.mu_' exclusively}} + // expected-warning@-1{{passing pointer to variable 'a_' requires holding mutex 'foo_.mu_'}} get_value(b_); // expected-warning{{calling function 'get_value' requires holding mutex 'foo_.mu_'}} + // expected-warning@-1{{passing pointer 'b_' requires holding mutex 'foo_.mu_'}} mutex_exclusive_lock(foo_.mu_); set_value(&a_, 1); mutex_unlock(foo_.mu_); @@ -193,6 +195,8 @@ int main(void) { #ifdef LATE_PARSING late_parsing.a_value_defined_before = 1; // expected-warning{{writing variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late' exclusively}} late_parsing.a_ptr_defined_before = 0; + set_value(&late_parsing.a_value_defined_before, 0); // expected-warning{{calling function 'set_value' requires holding mutex 'foo_.mu_' exclusively}} + // expected-warning@-1{{passing pointer to variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late'}} mutex_exclusive_lock(late_parsing.a_mutex_defined_late); mutex_exclusive_lock(late_parsing.a_mutex_defined_early); // expected-warning{{mutex 'a_mutex_defined_early' must be acquired before 'a_mutex_defined_late'}} mutex_exclusive_unlock(late_parsing.a_mutex_defined_early); diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 018d6d1bb258b..ae65d36c11d99 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -4863,6 +4863,10 @@ class Data { int dat; }; +class DataWithAddrOf : public Data { +public: + const Data* operator&() const; +}; class DataCell { public: @@ -4873,6 +4877,7 @@ class DataCell { }; +void showData(const Data* d); void showDataCell(const DataCell& dc); @@ -4944,6 +4949,14 @@ class Foo { (*datap2_)[0] = 0; // expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}} data_(); // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}} + + // Calls operator&, and does not take the address. + (void)&data_ao_; // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}} + (void)__builtin_addressof(data_ao_); // expected-warning {{passing variable 'data_ao_' by reference requires holding mutex 'mu_'}} + showData(&data_ao_); // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}} + (void)&data_; // no warning + (void)datap2_; // no warning + showData(&data_); // expected-warning {{passing pointer to variable 'data_' requires holding mutex 'mu_'}} } // const operator tests @@ -4955,6 +4968,10 @@ class Foo { //showDataCell(*datap2_); // xpected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}} int a = data_[0]; // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}} + + (void)&data_ao_; // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}} + showData(&data_ao_); // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}} + showData(&data_); // expected-warning {{passing pointer to variable 'data_' requires holding mutex 'mu_'}} } private: @@ -4962,6 +4979,7 @@ class Foo { Data data_ GUARDED_BY(mu_); Data* datap1_ GUARDED_BY(mu_); Data* datap2_ PT_GUARDED_BY(mu_); + DataWithAddrOf data_ao_ GUARDED_BY(mu_); }; } // end namespace GuardedNonPrimitiveTypeTest @@ -5911,8 +5929,12 @@ T&& mymove(T& f); void copy(Foo f); void write1(Foo& f); void write2(int a, Foo& f); +void write3(Foo* f); +void write4(Foo** f); void read1(const Foo& f); void read2(int a, const Foo& f); +void read3(const Foo* f); +void read4(Foo* const* f); void destroy(Foo&& f); void operator/(const Foo& f, const Foo& g); @@ -5942,19 +5964,24 @@ class Bar { Foo foo GUARDED_BY(mu); Foo foo2 GUARDED_BY(mu); Foo* foop PT_GUARDED_BY(mu); + Foo* foop2 GUARDED_BY(mu); SmartPtr<Foo> foosp PT_GUARDED_BY(mu); // test methods. void mwrite1(Foo& f); void mwrite2(int a, Foo& f); + void mwrite3(Foo* f); void mread1(const Foo& f); void mread2(int a, const Foo& f); + void mread3(const Foo* f); // static methods static void smwrite1(Foo& f); static void smwrite2(int a, Foo& f); + static void smwrite3(Foo* f); static void smread1(const Foo& f); static void smread2(int a, const Foo& f); + static void smread3(const Foo* f); void operator<<(const Foo& f); @@ -6017,6 +6044,107 @@ class Bar { read2(10, *foosp.get()); destroy(mymove(*foosp.get())); } + + void test_pass_pointer() { + (void)&foo; // no warning + (void)foop; // no warning + + write3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + write3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + write3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + write4((Foo **)&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + write4(&foop); // no warning + read3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + read3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + read3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + read4((Foo **)&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + read4(&foop); // no warning + mwrite3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + mwrite3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + mwrite3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + mread3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + mread3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + mread3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + smwrite3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + smwrite3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + smwrite3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + smread3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}} + smread3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + smread3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}} + + write3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + write3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + write4(&foop2); // expected-warning {{passing pointer to variable 'foop2' requires holding mutex 'mu'}} + read3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + read3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + read4(&foop2); // expected-warning {{passing pointer to variable 'foop2' requires holding mutex 'mu'}} + mwrite3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + mwrite3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + mread3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + mread3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + smwrite3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + smwrite3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + smread3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + smread3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}} + + mu.Lock(); + write3(&foo); + write3(foop); + write3(&*foop); + write3(foop2); + write4(&foop2); + read3(&foo); + read3(foop); + read3(&*foop); + read3(foop2); + read4(&foop2); + mwrite3(&foo); + mwrite3(foop); + mwrite3(&*foop); + mwrite3(foop2); + mread3(&foo); + mread3(foop); + mread3(&*foop); + mread3(foop2); + smwrite3(&foo); + smwrite3(foop); + smwrite3(&*foop); + smwrite3(foop2); + smread3(&foo); + smread3(foop); + smread3(&*foop); + smread3(foop2); + mu.Unlock(); + + mu.ReaderLock(); + write3(&foo); + write3(foop); + write3(&*foop); + write3(foop2); + write4(&foop2); + read3(&foo); + read3(foop); + read3(&*foop); + read3(foop2); + read4(&foop2); + mwrite3(&foo); + mwrite3(foop); + mwrite3(&*foop); + mwrite3(foop2); + mread3(&foo); + mread3(foop); + mread3(&*foop); + mread3(foop2); + smwrite3(&foo); + smwrite3(foop); + smwrite3(&*foop); + smwrite3(foop2); + smread3(&foo); + smread3(foop); + smread3(&*foop); + smread3(foop2); + mu.ReaderUnlock(); + } }; class Return { @@ -6087,15 +6215,64 @@ class Return { const Foo &returns_constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) { return foo; } + + Foo *returns_ptr_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) { + return &foo; + } + + Foo *returns_pt_ptr_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) { + return foo_ptr; + } + + Foo *returns_ptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) { + return &foo; // expected-warning {{returning pointer to variable 'foo' requires holding mutex 'mu' exclusively}} + } + + Foo *returns_pt_ptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) { + return foo_ptr; // expected-warning {{returning pointer 'foo_ptr' requires holding mutex 'mu' exclusively}} + } + + const Foo *returns_constptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) { + return &foo; + } + + const Foo *returns_pt_constptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) { + return foo_ptr; + } Foo *returns_ptr() { - return &foo; // FIXME -- Do we want to warn on this ? + return &foo; // expected-warning {{returning pointer to variable 'foo' requires holding mutex 'mu' exclusively}} + } + + Foo *returns_pt_ptr() { + return foo_ptr; // expected-warning {{returning pointer 'foo_ptr' requires holding mutex 'mu' exclusively}} } Foo &returns_ref2() { return *foo_ptr; // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}} } + // FIXME: Basic alias analysis would help catch cases like below. + Foo *returns_ptr_alias() { + mu.Lock(); + Foo *ret = &foo; + mu.Unlock(); + return ret; // no warning (false negative) + } + + Foo *returns_pt_ptr_alias() { + mu.Lock(); + Foo *ret = foo_ptr; + mu.Unlock(); + return ret; // no warning (false negative) + } + + Foo &returns_ref2_alias() { + mu.Lock(); + Foo *ret = foo_ptr; + mu.Unlock(); + return *ret; // no warning (false negative) + } }; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits