| // RUN: %clang_analyze_cc1 \ | |
| // RUN: -analyzer-checker=unix.BlockInCriticalSection \ | |
| // RUN: -std=c++11 \ | |
| // RUN: -analyzer-output text \ | |
| // RUN: -verify %s | |
| unsigned int sleep(unsigned int seconds) {return 0;} | |
| namespace std { | |
| // There are some standard library implementations where some mutex methods | |
| // come from an implementation detail base class. We need to ensure that these | |
| // are matched correctly. | |
| class __mutex_base { | |
| public: | |
| void lock(); | |
| }; | |
| class mutex : public __mutex_base{ | |
| public: | |
| void unlock(); | |
| bool try_lock(); | |
| }; | |
| } // namespace std | |
| void gh_99628() { | |
| std::mutex m; | |
| m.lock(); | |
| // expected-note@-1 {{Entering critical section here}} | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| m.unlock(); | |
| } | |
| void no_false_positive_gh_104241() { | |
| std::mutex m; | |
| m.lock(); | |
| // If inheritance not handled properly, this unlock might not match the lock | |
| // above because technically they act on different memory regions: | |
| // __mutex_base and mutex. | |
| m.unlock(); | |
| sleep(10); // no-warning | |
| } | |
| struct TwoMutexes { | |
| std::mutex m1; | |
| std::mutex m2; | |
| }; | |
| void two_mutexes_no_false_negative(TwoMutexes &tm) { | |
| tm.m1.lock(); | |
| // expected-note@-1 {{Entering critical section here}} | |
| tm.m2.unlock(); | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| tm.m1.unlock(); | |
| } | |
| struct MyMutexBase1 : std::mutex { | |
| void lock1() { lock(); } | |
| // expected-note@-1 {{Entering critical section here}} | |
| void unlock1() { unlock(); } | |
| }; | |
| struct MyMutexBase2 : std::mutex { | |
| void lock2() { lock(); } | |
| void unlock2() { unlock(); } | |
| }; | |
| struct MyMutex : MyMutexBase1, MyMutexBase2 {}; | |
| // MyMutex has two distinct std::mutex as base classes | |
| void custom_mutex_tp(MyMutexBase1 &mb) { | |
| mb.lock(); | |
| // expected-note@-1 {{Entering critical section here}} | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| mb.unlock(); | |
| } | |
| void custom_mutex_tn(MyMutexBase1 &mb) { | |
| mb.lock(); | |
| mb.unlock(); | |
| sleep(10); | |
| } | |
| void custom_mutex_cast_tp(MyMutexBase1 &mb) { | |
| static_cast<std::mutex&>(mb).lock(); | |
| // expected-note@-1 {{Entering critical section here}} | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| static_cast<std::mutex&>(mb).unlock(); | |
| } | |
| void custom_mutex_cast_tn(MyMutexBase1 &mb) { | |
| static_cast<std::mutex&>(mb).lock(); | |
| static_cast<std::mutex&>(mb).unlock(); | |
| sleep(10); | |
| } | |
| void two_custom_mutex_bases_tp(MyMutex &m) { | |
| m.lock1(); | |
| // expected-note@-1 {{Calling 'MyMutexBase1::lock1'}} | |
| // expected-note@-2 {{Returning from 'MyMutexBase1::lock1'}} | |
| m.unlock2(); | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| m.unlock1(); | |
| } | |
| void two_custom_mutex_bases_tn(MyMutex &m) { | |
| m.lock1(); | |
| m.unlock1(); | |
| sleep(10); | |
| } | |
| void two_custom_mutex_bases_casts_tp(MyMutex &m) { | |
| static_cast<MyMutexBase1&>(m).lock(); | |
| // expected-note@-1 {{Entering critical section here}} | |
| static_cast<MyMutexBase2&>(m).unlock(); | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| static_cast<MyMutexBase1&>(m).unlock(); | |
| } | |
| void two_custom_mutex_bases_casts_tn(MyMutex &m) { | |
| static_cast<MyMutexBase1&>(m).lock(); | |
| static_cast<MyMutexBase1&>(m).unlock(); | |
| sleep(10); | |
| } | |
| struct MutexVirtBase1 : virtual std::mutex { | |
| void lock1() { lock(); } | |
| // expected-note@-1 {{Entering critical section here}} | |
| void unlock1() { unlock(); } | |
| }; | |
| struct MutexVirtBase2 : virtual std::mutex { | |
| void lock2() { lock(); } | |
| void unlock2() { unlock(); } | |
| }; | |
| struct CombinedVirtMutex : MutexVirtBase1, MutexVirtBase2 {}; | |
| void virt_inherited_mutexes_same_base_tn1(CombinedVirtMutex &cvt) { | |
| cvt.lock1(); | |
| cvt.unlock1(); | |
| sleep(10); | |
| } | |
| void virt_inherited_mutexes_different_bases_tn(CombinedVirtMutex &cvt) { | |
| cvt.lock1(); | |
| cvt.unlock2(); // Despite a different name, unlock2 acts on the same mutex as lock1 | |
| sleep(10); | |
| } | |
| void virt_inherited_mutexes_different_bases_tp(CombinedVirtMutex &cvt) { | |
| cvt.lock1(); | |
| // expected-note@-1 {{Calling 'MutexVirtBase1::lock1'}} | |
| // expected-note@-2 {{Returning from 'MutexVirtBase1::lock1'}} | |
| sleep(10); | |
| // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}} | |
| // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}} | |
| cvt.unlock1(); | |
| } | |
| namespace std { | |
| template <class... MutexTypes> struct scoped_lock { | |
| explicit scoped_lock(MutexTypes&... m); | |
| ~scoped_lock(); | |
| }; | |
| template <class MutexType> class scoped_lock<MutexType> { | |
| public: | |
| explicit scoped_lock(MutexType& m) : m(m) { m.lock(); } | |
| ~scoped_lock() { m.unlock(); } | |
| private: | |
| MutexType& m; | |
| }; | |
| } // namespace std | |
| namespace gh_104241 { | |
| int magic_number; | |
| std::mutex m; | |
| void fixed() { | |
| int current; | |
| for (int items_processed = 0; items_processed < 100; ++items_processed) { | |
| { | |
| std::scoped_lock<std::mutex> guard(m); | |
| current = magic_number; | |
| } | |
| sleep(current); // expected no warning | |
| } | |
| } | |
| } // namespace gh_104241 |