ASDenysPetrov updated this revision to Diff 289190.
ASDenysPetrov added a comment.
Added //timed// functions support and tests for them.
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.unlock(); // no-warning
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok5() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+ rm2.lock(); // no-warning
+ rm2.unlock(); // no-warning
+}
+
+void rm_ok6(void) {
+ rm1.lock(); // no-warning
+ rm2.lock(); // no-warning
+ rm2.unlock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok7(void) {
+ if (rm1.try_lock()) // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok8(void) {
+ rm1.unlock(); // no-warning
+ if (rm1.try_lock()) // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok9(void) {
+ if (!rm1.try_lock()) // no-warning
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+}
+
+void rm_ok10() {
+ std::lock_guard<std::recursive_mutex> gl(rm1); // no-warning
+}
+
+// recursive_mutex bad
+
+void rm_bad1() {
+ rm1.lock(); // no-warning
+ rm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rm_bad2() {
+ rm1.lock(); // no-warning
+ rm1.unlock(); // no-warning
+ rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rm_bad3() {
+ 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_bad5() {
+ while (true)
+ rm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rm_bad6() {
+ while (true)
+ rm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rm_bad7() {
+ if (rm1.try_lock()) // no-warning
+ rm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+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.unlock(); // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok5() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+ rtm2.lock(); // no-warning
+ rtm2.unlock(); // no-warning
+}
+
+void rtm_ok6(void) {
+ rtm1.lock(); // no-warning
+ rtm2.lock(); // no-warning
+ rtm2.unlock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok7(void) {
+ if (rtm1.try_lock()) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok8(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock()) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok9(void) {
+ if (!rtm1.try_lock()) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok10(void) {
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok11(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok12(void) {
+ if (!rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok13(void) {
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok14(void) {
+ rtm1.unlock(); // no-warning
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok15(void) {
+ if (!rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+}
+
+void rtm_ok16() {
+ std::lock_guard<std::recursive_timed_mutex> gl(rtm1); // no-warning
+}
+
+// recursive_timed_mutex bad
+
+void rtm_bad1() {
+ rtm1.lock(); // no-warning
+ rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad2() {
+ rtm1.lock(); // no-warning
+ rtm1.unlock(); // no-warning
+ rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rtm_bad3() {
+ 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_bad5() {
+ while (true)
+ rtm1.unlock(); // expected-warning {{This lock has already been unlocked}}
+}
+
+void rtm_bad6() {
+ while (true)
+ rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad7() {
+ if (rtm1.try_lock()) // no-warning
+ rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad8() {
+ if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning
+ rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+void rtm_bad9() {
+ if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning
+ rtm1.lock(); // expected-warning{{This lock has already been acquired}}
+}
+
+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 locked as shared, but exclusive unlock used}}
+}
+
+void sms_bad12() {
+ sm1.lock(); // no-warning
+ sm1.unlock_shared(); // expected-warning{{This lock has been locked exclusively, but shared unlock used}}
+}
+
+void sms_bad13() {
+ if (sm1.try_lock_shared()) // no-warning
+ sm1.unlock(); // expected-warning{{This lock has been locked as shared, but exclusive unlock used}}
+}
+
+void sms_bad14() {
+ if (sm1.try_lock()) // no-warning
+ sm1.unlock_shared(); // expected-warning{{This lock has been locked 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 locked as shared, but exclusive unlock used}}
+}
+
+void stms_bad14() {
+ stm1.lock(); // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been locked exclusively, but shared unlock used}}
+}
+
+void stms_bad15() {
+ if (stm1.try_lock_shared()) // no-warning
+ stm1.unlock(); // expected-warning{{This lock has been locked 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 locked 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 locked as shared, but exclusive unlock used}}
+}
+
+void stms_bad18() {
+ if (stm1.try_lock()) // no-warning
+ stm1.unlock_shared(); // expected-warning{{This lock has been locked 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 locked 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 locked exclusively, but shared unlock used}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -33,7 +33,9 @@
enum Kind {
Destroyed,
Locked,
+ SharedLocked,
Unlocked,
+ SharedUnlocked,
UntouchedAndPossiblyDestroyed,
UnlockedAndPossiblyDestroyed
} K;
@@ -43,7 +45,9 @@
public:
static LockState getLocked() { return LockState(Locked); }
+ static LockState getSharedLocked() { return LockState(SharedLocked); }
static LockState getUnlocked() { return LockState(Unlocked); }
+ static LockState getSharedUnlocked() { return LockState(SharedUnlocked); }
static LockState getDestroyed() { return LockState(Destroyed); }
static LockState getUntouchedAndPossiblyDestroyed() {
return LockState(UntouchedAndPossiblyDestroyed);
@@ -55,7 +59,9 @@
bool operator==(const LockState &X) const { return K == X.K; }
bool isLocked() const { return K == Locked; }
+ bool isSharedLocked() const { return K == SharedLocked; }
bool isUnlocked() const { return K == Unlocked; }
+ bool isSharedUnlocked() const { return K == SharedUnlocked; }
bool isDestroyed() const { return K == Destroyed; }
bool isUntouchedAndPossiblyDestroyed() const {
return K == UntouchedAndPossiblyDestroyed;
@@ -70,11 +76,18 @@
class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
check::RegionChanges> {
public:
- enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
+ enum LockingSemantics {
+ NotApplicable = 0,
+ PthreadSemantics,
+ XNUSemantics,
+ CPlusPlusSemantics,
+ CPlusPlusSharedSemantics,
+ };
enum CheckerKind {
CK_PthreadLockChecker,
CK_FuchsiaLockChecker,
CK_C11LockChecker,
+ CK_CPlusPlus11LockChecker,
CK_NumCheckKinds
};
DefaultBool ChecksEnabled[CK_NumCheckKinds];
@@ -83,7 +96,7 @@
private:
typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
CallDescriptionMap<FnCheck> PThreadCallbacks = {
// Init.
{{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
@@ -164,49 +177,136 @@
{{"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::AcquireCPlusPlus11Lock},
+ {{{"std", "recursive_timed_mutex", "lock"}, 0},
+ &PthreadLockChecker::AcquireCPlusPlus11Lock},
+ {{{"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::TryCPlusPlus11Lock},
+ {{{"std", "recursive_timed_mutex", "try_lock"}, 0},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "recursive_timed_mutex", "try_lock_for"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"std", "recursive_timed_mutex", "try_lock_until"}, 1},
+ &PthreadLockChecker::TryCPlusPlus11Lock},
+ {{{"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 reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
- unsigned ArgNo, CheckerKind checkKind) const;
+ void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
+ const Expr *MtxExpr, CheckerKind CheckKind,
+ StringRef TextReport) const;
// Init.
void InitAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal Lock, CheckerKind checkkind) const;
+ SVal Lock, CheckerKind CheckKind) const;
// Lock, Try-lock.
void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void AcquireCPlusPlus11Lock(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;
+ CheckerKind CheckKind) const;
void TryXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryC11Lock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock, bool isTryLock, LockingSemantics semantics,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void TryCPlusPlus11Lock(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,
+ LockingSemantics semantics, CheckerKind CheckKind) const;
// Release.
void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock, CheckerKind checkkind) const;
+ 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,
+ LockingSemantics semantics, CheckerKind CheckKind) const;
// Destroy.
void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
SVal Lock, LockingSemantics semantics,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -225,20 +325,23 @@
mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_shared[CK_NumCheckKinds];
- void initBugType(CheckerKind checkKind) const {
- if (BT_doublelock[checkKind])
+ void initBugType(CheckerKind CheckKind) const {
+ if (BT_doublelock[CheckKind])
return;
- BT_doublelock[checkKind].reset(
- new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
- BT_doubleunlock[checkKind].reset(
- new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"});
- BT_destroylock[checkKind].reset(new BugType{
- CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
- BT_initlock[checkKind].reset(new BugType{
- CheckNames[checkKind], "Init invalid lock", "Lock checker"});
- BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
+ BT_doublelock[CheckKind].reset(
+ new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"});
+ BT_doubleunlock[CheckKind].reset(
+ new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"});
+ BT_destroylock[CheckKind].reset(new BugType{
+ CheckNames[CheckKind], "Use destroyed lock", "Lock checker"});
+ BT_initlock[CheckKind].reset(new BugType{
+ CheckNames[CheckKind], "Init invalid lock", "Lock checker"});
+ BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind],
"Lock order reversal", "Lock checker"});
+ BT_shared[CheckKind].reset(new BugType{
+ CheckNames[CheckKind], "Shared semantics misuse", "Lock checker"});
}
};
} // end anonymous namespace
@@ -260,7 +363,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))
@@ -269,6 +372,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
@@ -315,6 +420,8 @@
I.first->dumpToStream(Out);
if (I.second.isLocked())
Out << ": locked";
+ else if (I.second.isSharedLocked())
+ Out << ": shared locked";
else if (I.second.isUnlocked())
Out << ": unlocked";
else if (I.second.isDestroyed())
@@ -341,53 +448,92 @@
void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+ 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, false, CPlusPlusSemantics,
+ 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, false, CPlusPlusSharedSemantics,
+ CheckKind);
}
void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ 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, true, CPlusPlusSemantics,
+ 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, true, CPlusPlusSharedSemantics,
+ CheckKind);
}
void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
- CheckerContext &C, unsigned ArgNo,
- SVal lock, bool isTryLock,
- enum LockingSemantics semantics,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerContext &C, const Expr *MtxExpr,
+ SVal MtxVal, bool IsTryLock,
+ LockingSemantics semantics,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *lockR = lock.getAsRegion();
+ const MemRegion *lockR = MtxVal.getAsRegion();
if (!lockR)
return;
@@ -397,39 +543,36 @@
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
if (const LockState *LState = state->get<LockMap>(lockR)) {
- if (LState->isLocked()) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_doublelock[checkKind], "This lock has already been acquired", N);
- report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(report));
+ if (LState->isLocked() || LState->isSharedLocked()) {
+ reportBug(C, BT_doublelock, MtxExpr, CheckKind,
+ "This lock has already been acquired");
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+ reportBug(C, BT_destroylock, MtxExpr, CheckKind,
+ "This lock has already been destroyed");
return;
}
}
- ProgramStateRef lockSucc = state;
- if (isTryLock) {
+ ProgramStateRef LockSucc = state;
+ if (IsTryLock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
SVal RetVal = Call.getReturnValue();
if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
ProgramStateRef lockFail;
switch (semantics) {
case PthreadSemantics:
- std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
+ std::tie(lockFail, LockSucc) = state->assume(*DefinedRetVal);
break;
+ case CPlusPlusSemantics:
+ case CPlusPlusSharedSemantics:
case XNUSemantics:
- std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
+ std::tie(LockSucc, lockFail) = state->assume(*DefinedRetVal);
break;
default:
llvm_unreachable("Unknown tryLock locking semantics");
}
- assert(lockFail && lockSucc);
+ assert(lockFail && LockSucc);
C.addTransition(lockFail);
}
// We might want to handle the case when the mutex lock function was inlined
@@ -440,37 +583,61 @@
if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
// FIXME: If the lock function was inlined and returned true,
// we need to behave sanely - at least generate sink.
- lockSucc = state->assume(*DefinedRetVal, false);
- assert(lockSucc);
+ LockSucc = state->assume(*DefinedRetVal, false);
+ assert(LockSucc);
}
// We might want to handle the case when the mutex lock function was inlined
// and returned an Unknown or Undefined value.
} else {
- // XNU locking semantics return void on non-try locks
- assert((semantics == XNUSemantics) && "Unknown locking semantics");
- lockSucc = state;
+ // XNU and C++ locking semantics return void on non-try locks
+ assert((semantics == CPlusPlusSharedSemantics ||
+ semantics == CPlusPlusSemantics || semantics == XNUSemantics) &&
+ "Unknown locking semantics");
+ LockSucc = state;
}
// Record that the lock was acquired.
- lockSucc = lockSucc->add<LockSet>(lockR);
- lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
- C.addTransition(lockSucc);
+ LockSucc = LockSucc->add<LockSet>(lockR);
+ auto State = (semantics == CPlusPlusSharedSemantics)
+ ? LockState::getSharedLocked()
+ : LockState::getLocked();
+ LockSucc = LockSucc->set<LockMap>(lockR, State);
+ C.addTransition(LockSucc);
}
void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+ CheckerKind CheckKind) const {
+ ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), 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, CPlusPlusSemantics, 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, CPlusPlusSharedSemantics,
+ CheckKind);
}
void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
- CheckerContext &C, unsigned ArgNo,
- SVal lock,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerContext &C, const Expr *MtxExpr,
+ SVal MtxVal, LockingSemantics semantics,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *lockR = lock.getAsRegion();
+ const MemRegion *lockR = MtxVal.getAsRegion();
if (!lockR)
return;
@@ -480,20 +647,28 @@
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
if (const LockState *LState = state->get<LockMap>(lockR)) {
- if (LState->isUnlocked()) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_doubleunlock[checkKind], "This lock has already been unlocked",
- N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ if (LState->isUnlocked() || LState->isSharedUnlocked()) {
+ reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
+ "This lock has already been unlocked");
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+ reportBug(C, BT_destroylock, MtxExpr, CheckKind,
+ "This lock has already been destroyed");
return;
+ } else if (LState->isLocked()) {
+ if (semantics == CPlusPlusSharedSemantics) {
+ reportBug(
+ C, BT_shared, MtxExpr, CheckKind,
+ "This lock has been locked exclusively, but shared unlock used");
+ return;
+ }
+ } else if (LState->isSharedLocked()) {
+ if (semantics == CPlusPlusSemantics) {
+ reportBug(
+ C, BT_shared, MtxExpr, CheckKind,
+ "This lock has been locked as shared, but exclusive unlock used");
+ return;
+ }
}
}
@@ -502,45 +677,39 @@
if (!LS.isEmpty()) {
const MemRegion *firstLockR = LS.getHead();
if (firstLockR != lockR) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_lor[checkKind],
- "This was not the most recently acquired lock. Possible "
- "lock order reversal",
- N);
- report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(report));
+ reportBug(C, BT_lor, MtxExpr, CheckKind,
+ "This was not the most recently acquired lock. Possible lock "
+ "order reversal");
return;
}
// Record that the lock was released.
state = state->set<LockSet>(LS.getTail());
}
- state = state->set<LockMap>(lockR, LockState::getUnlocked());
+ auto State = (semantics == CPlusPlusSharedSemantics)
+ ? LockState::getSharedUnlocked()
+ : LockState::getUnlocked();
+ state = state->set<LockMap>(lockR, State);
C.addTransition(state);
}
void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
+ CheckerKind CheckKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, CheckKind);
}
void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
+ CheckerKind CheckKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, CheckKind);
}
void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
CheckerContext &C, unsigned ArgNo,
- SVal Lock,
- enum LockingSemantics semantics,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ SVal Lock, LockingSemantics semantics,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
const MemRegion *LockR = Lock.getAsRegion();
@@ -581,33 +750,23 @@
return;
}
}
- StringRef Message;
- if (LState->isLocked()) {
- Message = "This lock is still locked";
- } else {
- Message = "This lock has already been destroyed";
- }
+ StringRef Message = LState->isLocked()
+ ? "This lock is still locked"
+ : "This lock has already been destroyed";
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_destroylock[checkKind], Message, N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ reportBug(C, BT_destroylock, Call.getArgExpr(ArgNo), CheckKind, Message);
}
void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+ CheckerKind CheckKind) const {
+ InitLockAux(Call, C, 0, Call.getArgSVal(0), CheckKind);
}
void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
unsigned ArgNo, SVal Lock,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
const MemRegion *LockR = Lock.getAsRegion();
@@ -627,35 +786,24 @@
return;
}
- StringRef Message;
+ StringRef Message = LState->isLocked()
+ ? "This lock is still being held"
+ : "This lock has already been initialized";
- if (LState->isLocked()) {
- Message = "This lock is still being held";
- } else {
- Message = "This lock has already been initialized";
- }
-
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_initlock[checkKind], Message, N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ reportBug(C, BT_initlock, Call.getArgExpr(ArgNo), CheckKind, Message);
}
-void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
- CheckerContext &C,
- unsigned ArgNo,
- CheckerKind checkKind) const {
+void PthreadLockChecker::reportBug(CheckerContext &C,
+ std::unique_ptr<BugType> BT[],
+ const Expr *MtxExpr, CheckerKind CheckKind,
+ StringRef Desc) const {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_destroylock[checkKind], "This lock has already been destroyed", N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+ initBugType(CheckKind);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
+ Report->addRange(MtxExpr->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -691,10 +839,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())
@@ -740,3 +888,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
@@ -749,6 +749,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