ASDenysPetrov updated this revision to Diff 308781.
ASDenysPetrov added a comment.
@NoQ , considered your suggestions about the case of recursive mutexes.
Implemented this feature. Now this works like below:
recursive_mutex rm;
rm.lock();
rm.lock(); // no-warning
recursive_timed_mutex rtm;
rtm.lock();
rtm.lock(); // no-warning
Please look.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D85984/new/
https://reviews.llvm.org/D85984
Files:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp
Index: clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp
@@ -0,0 +1,1111 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.CPlusPlus11Lock -verify %s
+
+namespace std {
+
+namespace chrono {
+using duration = int;
+using time_point = int;
+} // namespace chrono
+
+struct mutex {
+ void lock();
+ bool try_lock();
+ void unlock();
+};
+
+struct timed_mutex {
+ void lock();
+ bool try_lock();
+ bool try_lock_for(std::chrono::duration);
+ bool try_lock_until(std::chrono::time_point);
+ void unlock();
+};
+
+struct recursive_mutex {
+ void lock();
+ bool try_lock();
+ void unlock();
+};
+
+struct recursive_timed_mutex {
+ void lock();
+ bool try_lock();
+ bool try_lock_for(std::chrono::duration);
+ bool try_lock_until(std::chrono::time_point);
+ void unlock();
+};
+
+struct shared_mutex {
+ void lock();
+ bool try_lock();
+ void unlock();
+ void lock_shared();
+ bool try_lock_shared();
+ void unlock_shared();
+};
+
+struct shared_timed_mutex {
+ void lock();
+ bool try_lock();
+ bool try_lock_for(std::chrono::duration);
+ bool try_lock_until(std::chrono::time_point);
+ void unlock();
+ void lock_shared();
+ bool try_lock_shared();
+ bool try_lock_shared_for(std::chrono::duration);
+ bool try_lock_shared_until(std::chrono::time_point);
+ void unlock_shared();
+};
+
+template <typename T>
+struct lock_guard {
+ T &t;
+ lock_guard(T &m) : t(m) {
+ t.lock();
+ }
+ ~lock_guard() {
+ t.unlock();
+ }
+};
+
+template <typename T>
+struct shared_lock {
+ T &t;
+ shared_lock(T &m) : t(m) {
+ t.lock_shared();
+ }
+ ~shared_lock() {
+ t.unlock_shared();
+ }
+};
+} // namespace std
+
+std::mutex m1;
+std::mutex m2;
+
+// mutex ok
+
+void m_ok1() {
+ m1.lock(); // no-warning
+}
+
+void m_ok2() {
+ m1.unlock(); // no-warning
+}
+
+void m_ok3() {
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok4() {
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok5() {
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+ m2.lock(); // no-warning
+ m2.unlock(); // no-warning
+}
+
+void m_ok6(void) {
+ m1.lock(); // no-warning
+ m2.lock(); // no-warning
+ m2.unlock(); // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok7(void) {
+ if (m1.try_lock()) // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok8(void) {
+ m1.unlock(); // no-warning
+ if (m1.try_lock()) // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok9(void) {
+ if (!m1.try_lock()) // no-warning
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+}
+
+void m_ok10() {
+ std::lock_guard<std::mutex> gl(m1); // no-warning
+}
+
+// mutex bad
+
+void m_bad1() {
+ m1.lock(); // no-warning
+ m1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void m_bad2() {
+ m1.lock(); // no-warning
+ m1.unlock(); // no-warning
+ m1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void m_bad3() {
+ m1.lock(); // no-warning
+ m2.lock(); // no-warning
+ m1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ m2.unlock(); // no-warning
+}
+
+void m_bad5() {
+ while (true)
+ m1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void m_bad6() {
+ while (true)
+ m1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void m_bad7() {
+ if (m1.try_lock()) // no-warning
+ m1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+std::timed_mutex tm1;
+std::timed_mutex tm2;
+
+// timed_mutex ok
+
+void tm_ok1() {
+ tm1.lock(); // no-warning
+}
+
+void tm_ok2() {
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok3() {
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok4() {
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok5() {
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+ tm2.lock(); // no-warning
+ tm2.unlock(); // no-warning
+}
+
+void tm_ok6(void) {
+ tm1.lock(); // no-warning
+ tm2.lock(); // no-warning
+ tm2.unlock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok7(void) {
+ if (tm1.try_lock()) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok8(void) {
+ tm1.unlock(); // no-warning
+ if (tm1.try_lock()) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok9(void) {
+ if (!tm1.try_lock()) // no-warning
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok10(void) {
+ if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok11(void) {
+ tm1.unlock(); // no-warning
+ if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok12(void) {
+ if (!tm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok13(void) {
+ if (tm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok14(void) {
+ tm1.unlock(); // no-warning
+ if (tm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok15(void) {
+ if (!tm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+}
+
+void tm_ok16() {
+ std::lock_guard<std::timed_mutex> gl(tm1); // no-warning
+}
+
+// timed_mutex bad
+
+void tm_bad1() {
+ tm1.lock(); // no-warning
+ tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad2() {
+ tm1.lock(); // no-warning
+ tm1.unlock(); // no-warning
+ tm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void tm_bad3() {
+ tm1.lock(); // no-warning
+ tm2.lock(); // no-warning
+ tm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ tm2.unlock(); // no-warning
+}
+
+void tm_bad5() {
+ while (true)
+ tm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void tm_bad6() {
+ while (true)
+ tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad7() {
+ if (tm1.try_lock()) // no-warning
+ tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad8() {
+ if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void tm_bad9() {
+ if (tm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ tm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+std::recursive_mutex rm1;
+std::recursive_mutex rm2;
+
+// recursive_mutex ok
+
+void rm_ok1() {
+ rm1.lock(); // no-warning
+}
+
+void rm_ok2() {
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok3() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok4() {
+ rm1.lock(); // no-warning
+ rm1.lock(); // no-warning
+}
+
+void rm_ok5() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok6() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+ rm2.lock(); // no-warning
+ rm2.unlock(); // no-warning
+}
+
+void rm_ok7(void) {
+ rm1.lock(); // no-warning
+ rm2.lock(); // no-warning
+ rm2.unlock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok8(void) {
+ if (rm1.try_lock()) // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok9(void) {
+ rm1.unlock(); // no-warning
+ if (rm1.try_lock()) // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok10(void) {
+ if (!rm1.try_lock()) // no-warning
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok11() {
+ std::lock_guard<std::recursive_mutex> gl(rm1); // no-warning
+}
+
+void rm_ok12() {
+ while (true)
+ rm1.lock(); // no-warning
+}
+
+void rm_ok13() {
+ if (rm1.try_lock()) // no-warning
+ rm1.lock(); // no-warning
+}
+
+// recursive_mutex bad
+
+void rm_bad1() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+ rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rm_bad2() {
+ rm1.lock(); // no-warning
+ rm2.lock(); // no-warning
+ rm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ rm2.unlock(); // no-warning
+}
+
+void rm_bad3() {
+ while (true)
+ rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+std::recursive_timed_mutex rtm1;
+std::recursive_timed_mutex rtm2;
+
+// recursive_timed_mutex ok
+
+void rtm_ok1() {
+ rtm1.lock(); // no-warning
+}
+
+void rtm_ok2() {
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok3() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok4() {
+ rtm1.lock(); // no-warning
+ rtm1.lock(); // no-warning
+}
+
+void rtm_ok5() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok6() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+ rtm2.lock(); // no-warning
+ rtm2.unlock(); // no-warning
+}
+
+void rtm_ok7(void) {
+ rtm1.lock(); // no-warning
+ rtm2.lock(); // no-warning
+ rtm2.unlock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok8(void) {
+ if (rtm1.try_lock()) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok9(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock()) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok10(void) {
+ if (!rtm1.try_lock()) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok11(void) {
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok12(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok13(void) {
+ if (!rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok14(void) {
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok15(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok16(void) {
+ if (!rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok17() {
+ std::lock_guard<std::recursive_timed_mutex> gl(rtm1); // no-warning
+}
+
+void rtm_ok18() {
+ while (true)
+ rtm1.lock(); // no-warning
+}
+
+void rtm_ok19() {
+ if (rtm1.try_lock()) // no-warning
+ rtm1.lock(); // no-warning
+}
+
+void rtm_ok20() {
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.lock(); // no-warning
+}
+
+void rtm_ok21() {
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.lock(); // no-warning
+}
+
+// recursive_timed_mutex bad
+
+void rtm_bad1() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+ rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rtm_bad2() {
+ rtm1.lock(); // no-warning
+ rtm2.lock(); // no-warning
+ rtm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ rtm2.unlock(); // no-warning
+}
+
+void rtm_bad3() {
+ while (true)
+ rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+std::shared_mutex sm1;
+std::shared_mutex sm2;
+
+// shared_mutex ok
+
+void sm_ok1() {
+ sm1.lock(); // no-warning
+}
+
+void sm_ok2() {
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok3() {
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok4() {
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok5() {
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+ sm2.lock(); // no-warning
+ sm2.unlock(); // no-warning
+}
+
+void sm_ok6(void) {
+ sm1.lock(); // no-warning
+ sm2.lock(); // no-warning
+ sm2.unlock(); // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok7(void) {
+ if (sm1.try_lock()) // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok8(void) {
+ sm1.unlock(); // no-warning
+ if (sm1.try_lock()) // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok9(void) {
+ if (!sm1.try_lock()) // no-warning
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+}
+
+void sm_ok10() {
+ std::lock_guard<std::shared_mutex> gl(sm1); // no-warning
+}
+
+// shared_mutex bad
+
+void sm_bad1() {
+ sm1.lock(); // no-warning
+ sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sm_bad2() {
+ sm1.lock(); // no-warning
+ sm1.unlock(); // no-warning
+ sm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sm_bad3() {
+ sm1.lock(); // no-warning
+ sm2.lock(); // no-warning
+ sm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ sm2.unlock(); // no-warning
+}
+
+void sm_bad5() {
+ while (true)
+ sm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sm_bad6() {
+ while (true)
+ sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sm_bad7() {
+ if (sm1.try_lock()) // no-warning
+ sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+// shared_mutex ok (shared semantics)
+
+void sms_ok1() {
+ sm1.lock_shared(); // no-warning
+}
+
+void sms_ok2() {
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok3() {
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok4() {
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok5() {
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+ sm2.lock_shared(); // no-warning
+ sm2.unlock_shared(); // no-warning
+}
+
+void sms_ok6(void) {
+ sm1.lock_shared(); // no-warning
+ sm2.lock_shared(); // no-warning
+ sm2.unlock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok7(void) {
+ if (sm1.try_lock_shared()) // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok8(void) {
+ sm1.unlock_shared(); // no-warning
+ if (sm1.try_lock_shared()) // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok9(void) {
+ if (!sm1.try_lock_shared()) // no-warning
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+}
+
+void sms_ok10() {
+ std::shared_lock<std::shared_mutex> gl(sm1); // no-warning
+}
+
+// shared_mutex bad (shared semantics)
+
+void sms_bad1() {
+ sm1.lock_shared(); // no-warning
+ sm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sms_bad2() {
+ sm1.lock(); // no-warning
+ sm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sms_bad3() {
+ sm1.lock_shared(); // no-warning
+ sm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sms_bad4() {
+ sm1.lock_shared(); // no-warning
+ sm1.unlock_shared(); // no-warning
+ sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sms_bad5() {
+ sm1.unlock_shared(); // no-warning
+ sm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sms_bad6() {
+ sm1.unlock(); // no-warning
+ sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sms_bad7() {
+ sm1.lock_shared(); // no-warning
+ sm2.lock_shared(); // no-warning
+ sm1.unlock_shared(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ sm2.unlock_shared(); // no-warning
+}
+
+void sms_bad8() {
+ while (true)
+ sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void sms_bad9() {
+ while (true)
+ sm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sms_bad10() {
+ if (sm1.try_lock_shared()) // no-warning
+ sm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void sms_bad11() {
+ sm1.lock_shared(); // no-warning
+ sm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void sms_bad12() {
+ sm1.lock(); // no-warning
+ sm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
+
+void sms_bad13() {
+ if (sm1.try_lock_shared()) // no-warning
+ sm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void sms_bad14() {
+ if (sm1.try_lock()) // no-warning
+ sm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
+
+std::shared_timed_mutex stm1;
+std::shared_timed_mutex stm2;
+
+// shared_timed_mutex ok
+
+void stm_ok1() {
+ stm1.lock(); // no-warning
+}
+
+void stm_ok2() {
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok3() {
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok4() {
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok5() {
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+ stm2.lock(); // no-warning
+ stm2.unlock(); // no-warning
+}
+
+void stm_ok6(void) {
+ stm1.lock(); // no-warning
+ stm2.lock(); // no-warning
+ stm2.unlock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok7(void) {
+ if (stm1.try_lock()) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok8(void) {
+ stm1.unlock(); // no-warning
+ if (stm1.try_lock()) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok9(void) {
+ if (!stm1.try_lock()) // no-warning
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok10(void) {
+ if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok11(void) {
+ stm1.unlock(); // no-warning
+ if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok12(void) {
+ if (!stm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok13(void) {
+ if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok14(void) {
+ stm1.unlock(); // no-warning
+ if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok15(void) {
+ if (!stm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+}
+
+void stm_ok16() {
+ std::lock_guard<std::shared_timed_mutex> gl(stm1); // no-warning
+}
+
+// shared_timed_mutex bad
+
+void stm_bad1() {
+ stm1.lock(); // no-warning
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad2() {
+ stm1.lock(); // no-warning
+ stm1.unlock(); // no-warning
+ stm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stm_bad3() {
+ stm1.lock(); // no-warning
+ stm2.lock(); // no-warning
+ stm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ stm2.unlock(); // no-warning
+}
+
+void stm_bad5() {
+ while (true)
+ stm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stm_bad6() {
+ while (true)
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad7() {
+ if (stm1.try_lock()) // no-warning
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad8() {
+ if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stm_bad9() {
+ if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+// shared_timed_mutex ok (shared semantics)
+
+void stms_ok1() {
+ stm1.lock_shared(); // no-warning
+}
+
+void stms_ok2() {
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok3() {
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok4() {
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok5() {
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+ stm2.lock_shared(); // no-warning
+ stm2.unlock_shared(); // no-warning
+}
+
+void stms_ok6(void) {
+ stm1.lock_shared(); // no-warning
+ stm2.lock_shared(); // no-warning
+ stm2.unlock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok7(void) {
+ if (stm1.try_lock_shared()) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok8(void) {
+ stm1.unlock_shared(); // no-warning
+ if (stm1.try_lock_shared()) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok9(void) {
+ if (!stm1.try_lock_shared()) // no-warning
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok10(void) {
+ if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok11(void) {
+ stm1.unlock_shared(); // no-warning
+ if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok12(void) {
+ if (!stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok13(void) {
+ if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok14(void) {
+ stm1.unlock_shared(); // no-warning
+ if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok15(void) {
+ if (!stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+}
+
+void stms_ok16() {
+ std::shared_lock<std::shared_timed_mutex> gl(stm1); // no-warning
+}
+
+// shared_timed_mutex bad (shared semantics)
+
+void stms_bad1() {
+ stm1.lock_shared(); // no-warning
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad2() {
+ stm1.lock(); // no-warning
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad3() {
+ stm1.lock_shared(); // no-warning
+ stm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad4() {
+ stm1.lock_shared(); // no-warning
+ stm1.unlock_shared(); // no-warning
+ stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stms_bad5() {
+ stm1.unlock_shared(); // no-warning
+ stm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stms_bad6() {
+ stm1.unlock(); // no-warning
+ stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stms_bad7() {
+ stm1.lock_shared(); // no-warning
+ stm2.lock_shared(); // no-warning
+ stm1.unlock_shared(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}}
+ stm2.unlock_shared(); // no-warning
+}
+
+void stms_bad8() {
+ while (true)
+ stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void stms_bad9() {
+ while (true)
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad10() {
+ if (stm1.try_lock_shared()) // no-warning
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad11() {
+ if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad12() {
+ if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning
+ stm1.lock_shared(); // expected-warning{{This lock has already been acquired}}
+}
+
+void stms_bad13() {
+ stm1.lock_shared(); // no-warning
+ stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void stms_bad14() {
+ stm1.lock(); // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
+
+void stms_bad15() {
+ if (stm1.try_lock_shared()) // no-warning
+ stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void stms_bad16() {
+ if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void stms_bad17() {
+ if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}}
+}
+
+void stms_bad18() {
+ if (stm1.try_lock()) // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
+
+void stms_bad19() {
+ if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
+
+void stms_bad20() {
+ if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -83,8 +83,16 @@
CK_PthreadLockChecker,
CK_FuchsiaLockChecker,
CK_C11LockChecker,
+ CK_CPlusPlus11LockChecker,
CK_NumCheckKinds
};
+ using ModeFlags = int8_t;
+ enum : ModeFlags {
+ MF_None = 0b0000,
+ MF_Try = 0b0001,
+ MF_Shared = 0b0010,
+ MF_Recursive = 0b0100,
+ };
DefaultBool ChecksEnabled[CK_NumCheckKinds];
CheckerNameRef CheckNames[CK_NumCheckKinds];
@@ -172,12 +180,88 @@
{{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
};
+ CallDescriptionMap<FnCheck> CPlusPlus11Callbacks = {
+
+ // Acquire.
+ {{{"std", "mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11Lock},
+ {{{"std", "timed_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11Lock},
+ {{{"std", "recursive_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11LockRecursive},
+ {{{"std", "recursive_timed_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11LockRecursive},
+ {{{"std", "shared_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11Lock},
+ {{{"std", "shared_mutex", "lock_shared"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11LockShared},
+ {{{"std", "shared_timed_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11Lock},
+ {{{"std", "shared_timed_mutex", "lock_shared"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11LockShared},
+
+ // Try.
+ {{{"std", "mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "timed_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "timed_mutex", "try_lock_for"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "timed_mutex", "try_lock_until"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "recursive_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11LockRecursive},
+ {{{"std", "recursive_timed_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11LockRecursive},
+ {{{"std", "recursive_timed_mutex", "try_lock_for"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11LockRecursive},
+ {{{"std", "recursive_timed_mutex", "try_lock_until"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11LockRecursive},
+ {{{"std", "shared_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "shared_mutex", "try_lock_shared"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11LockShared},
+ {{{"std", "shared_timed_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "shared_timed_mutex", "try_lock_for"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "shared_timed_mutex", "try_lock_until"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "shared_timed_mutex", "try_lock_shared"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11LockShared},
+ {{{"std", "shared_timed_mutex", "try_lock_shared_for"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11LockShared},
+ {{{"std", "shared_timed_mutex", "try_lock_shared_until"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11LockShared},
+
+ // Release.
+ {{{"std", "mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "timed_mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "recursive_mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "recursive_timed_mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "shared_mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "shared_mutex", "unlock_shared"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11LockShared},
+ {{{"std", "shared_timed_mutex", "unlock"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11Lock},
+ {{{"std", "shared_timed_mutex", "unlock_shared"}, 0},
+ &PthreadLockChecker::ReleaseCPlusPlus11LockShared},
+ };
+
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
const MemRegion *lockR,
const SymbolRef *sym) const;
void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
const Expr *MtxExpr, CheckerKind CheckKind,
StringRef Desc) const;
+ bool hasModeFlag(const ModeFlags flags, const ModeFlags flag) const {
+ return (flags & flag) == flag;
+ }
// Init.
void InitAnyLock(const CallEvent &Call, CheckerContext &C,
@@ -193,6 +277,12 @@
CheckerKind CheckKind) const;
void AcquireXNULockShared(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const;
+ void AcquireCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
+ void AcquireCPlusPlus11LockRecursive(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
+ void AcquireCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const;
void TryXNULock(const CallEvent &Call, CheckerContext &C,
@@ -203,18 +293,27 @@
CheckerKind CheckKind) const;
void TryC11Lock(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const;
+ void TryCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
+ void TryCPlusPlus11LockRecursive(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
+ void TryCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
- const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
- bool IsShared, LockingSemantics Semantics,
- CheckerKind CheckKind) const;
+ const Expr *MtxExpr, SVal MtxVal, ModeFlags Flags,
+ LockingSemantics Semantics, CheckerKind CheckKind) const;
// Release.
void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const;
void ReleaseAnyLockShared(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const;
+ void ReleaseCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
+ void ReleaseCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C,
+ CheckerKind CheckKind) const;
void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
- const Expr *MtxExpr, SVal MtxVal, bool IsShared,
+ const Expr *MtxExpr, SVal MtxVal, ModeFlags Flags,
enum LockingSemantics Semantics,
CheckerKind CheckKind) const;
@@ -282,7 +381,7 @@
// with exactly one identifier?
// FIXME: Try to handle cases when the implementation was inlined rather
// than just giving up.
- if (!Call.isGlobalCFunction() || C.wasInlined)
+ if (C.wasInlined)
return;
if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
@@ -291,6 +390,8 @@
(this->**Callback)(Call, C, CK_FuchsiaLockChecker);
else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
(this->**Callback)(Call, C, CK_C11LockChecker);
+ else if (const FnCheck *Callback = CPlusPlus11Callbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_CPlusPlus11LockChecker);
}
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
@@ -368,61 +469,115 @@
void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None,
PthreadSemantics, CheckKind);
}
void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None,
XNUSemantics, CheckKind);
}
void PthreadLockChecker::AcquireXNULockShared(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, true,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Shared,
XNUSemantics, CheckKind);
}
+void PthreadLockChecker::AcquireCPlusPlus11Lock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_None, XNUSemantics, CheckKind);
+}
+
+void PthreadLockChecker::AcquireCPlusPlus11LockRecursive(
+ const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_Recursive, XNUSemantics,
+ CheckKind);
+}
+
+void PthreadLockChecker::AcquireCPlusPlus11LockShared(
+ const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_Shared, XNUSemantics,
+ CheckKind);
+}
+
void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try,
PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try,
XNUSemantics, CheckKind);
}
void PthreadLockChecker::TryXNULockShared(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, true,
- XNUSemantics, CheckKind);
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
+ MF_Try | MF_Shared, XNUSemantics, CheckKind);
}
void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try,
PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
CheckerKind CheckKind) const {
- AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false,
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try,
PthreadSemantics, CheckKind);
}
+void PthreadLockChecker::TryCPlusPlus11Lock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_Try, XNUSemantics, CheckKind);
+}
+
+void PthreadLockChecker::TryCPlusPlus11LockRecursive(
+ const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_Try | MF_Recursive,
+ XNUSemantics, CheckKind);
+}
+
+void PthreadLockChecker::TryCPlusPlus11LockShared(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ AcquireLockAux(Call, C, ThisExpr, ThisVal, MF_Try | MF_Shared, XNUSemantics,
+ CheckKind);
+}
+
void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
CheckerContext &C, const Expr *MtxExpr,
- SVal MtxVal, bool IsTryLock,
- bool IsShared,
+ SVal MtxVal, ModeFlags Flags,
enum LockingSemantics Semantics,
CheckerKind CheckKind) const {
if (!ChecksEnabled[CheckKind])
@@ -438,7 +593,7 @@
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
if (const LockState *LState = state->get<LockMap>(lockR)) {
- if (LState->isAnyLocked()) {
+ if (LState->isAnyLocked() && !hasModeFlag(Flags, MF_Recursive)) {
reportBug(C, BT_doublelock, MtxExpr, CheckKind,
"This lock has already been acquired");
return;
@@ -450,7 +605,7 @@
}
ProgramStateRef lockSucc = state;
- if (IsTryLock) {
+ if (hasModeFlag(Flags, MF_Try)) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
SVal RetVal = Call.getReturnValue();
if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
@@ -489,7 +644,8 @@
// Record that the lock was acquired.
lockSucc = lockSucc->add<LockSet>(lockR);
- auto State = IsShared ? LockState::getSharedLocked() : LockState::getLocked();
+ auto State = hasModeFlag(Flags, MF_Shared) ? LockState::getSharedLocked()
+ : LockState::getLocked();
lockSucc = lockSucc->set<LockMap>(lockR, State);
C.addTransition(lockSucc);
}
@@ -497,20 +653,38 @@
void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+ ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None,
NotApplicable, CheckKind);
}
void PthreadLockChecker::ReleaseAnyLockShared(const CallEvent &Call,
CheckerContext &C,
CheckerKind CheckKind) const {
- ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Shared,
NotApplicable, CheckKind);
}
+void PthreadLockChecker::ReleaseCPlusPlus11Lock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ ReleaseLockAux(Call, C, ThisExpr, ThisVal, MF_None, XNUSemantics, CheckKind);
+}
+
+void PthreadLockChecker::ReleaseCPlusPlus11LockShared(
+ const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const {
+ auto MemberCall = cast<CXXMemberCall>(&Call);
+ auto ThisExpr = MemberCall->getCXXThisExpr();
+ auto ThisVal = MemberCall->getCXXThisVal();
+ ReleaseLockAux(Call, C, ThisExpr, ThisVal, MF_Shared, XNUSemantics,
+ CheckKind);
+}
+
void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
CheckerContext &C, const Expr *MtxExpr,
- SVal MtxVal, bool IsShared,
+ SVal MtxVal, ModeFlags Flags,
enum LockingSemantics Semantics,
CheckerKind CheckKind) const {
if (!ChecksEnabled[CheckKind])
@@ -525,29 +699,30 @@
if (sym)
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
+ const bool IsShared = hasModeFlag(Flags, MF_Shared);
+
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isAnyUnlocked()) {
reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
"This lock has already been unlocked");
return;
- } else if (LState->isDestroyed()) {
+ }
+ if (LState->isDestroyed()) {
reportBug(C, BT_destroylock, MtxExpr, CheckKind,
"This lock has already been destroyed");
return;
- } else if (LState->isLocked()) {
- if (IsShared) {
- reportBug(
- C, BT_shared, MtxExpr, CheckKind,
- "This lock has been acquired exclusively, but shared unlock used");
- return;
- }
- } else if (LState->isSharedLocked()) {
- if (!IsShared) {
- reportBug(
- C, BT_shared, MtxExpr, CheckKind,
- "This lock has been acquired as shared, but exclusive unlock used");
- return;
- }
+ }
+ if (LState->isLocked() && IsShared) {
+ reportBug(C, BT_shared, MtxExpr, CheckKind,
+ "This lock has been acquired exclusively, but shared "
+ "unlock used");
+ return;
+ }
+ if (LState->isSharedLocked() && !IsShared) {
+ reportBug(C, BT_shared, MtxExpr, CheckKind,
+ "This lock has been acquired exclusively, but shared "
+ "unlock used");
+ return;
}
}
@@ -720,10 +895,10 @@
const CallEvent *Call) const {
bool IsLibraryFunction = false;
- if (Call && Call->isGlobalCFunction()) {
+ if (Call) {
// Avoid invalidating mutex state when a known supported function is called.
if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
- C11Callbacks.lookup(*Call))
+ C11Callbacks.lookup(*Call) || CPlusPlus11Callbacks.lookup(*Call))
return State;
if (Call->isInSystemHeader())
@@ -769,3 +944,4 @@
REGISTER_CHECKER(PthreadLockChecker)
REGISTER_CHECKER(FuchsiaLockChecker)
REGISTER_CHECKER(C11LockChecker)
+REGISTER_CHECKER(CPlusPlus11LockChecker)
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -751,6 +751,11 @@
Dependencies<[SmartPtrModeling]>,
Documentation<HasAlphaDocumentation>;
+def CPlusPlus11LockChecker : Checker<"CPlusPlus11Lock">,
+ HelpText<"Simple C++11 lock -> unlock checker">,
+ Dependencies<[PthreadLockBase]>,
+ Documentation<HasAlphaDocumentation>;
+
} // end: "alpha.cplusplus"
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits