xref: /llvm-project/clang/test/Analysis/block-in-critical-section-inheritance.cpp (revision 82e314e3664d2c8768212e74f751187d51950b87)
1 // RUN: %clang_analyze_cc1 \
2 // RUN:   -analyzer-checker=unix.BlockInCriticalSection \
3 // RUN:   -std=c++11 \
4 // RUN:   -analyzer-output text \
5 // RUN:   -verify %s
6 
7 unsigned int sleep(unsigned int seconds) {return 0;}
8 namespace std {
9 // There are some standard library implementations where some mutex methods
10 // come from an implementation detail base class. We need to ensure that these
11 // are matched correctly.
12 class __mutex_base {
13 public:
14   void lock();
15 };
16 class mutex : public __mutex_base{
17 public:
18   void unlock();
19   bool try_lock();
20 };
21 } // namespace std
22 
23 void gh_99628() {
24   std::mutex m;
25   m.lock();
26   // expected-note@-1 {{Entering critical section here}}
27   sleep(10);
28   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
29   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
30   m.unlock();
31 }
32 
33 void no_false_positive_gh_104241() {
34   std::mutex m;
35   m.lock();
36   // If inheritance not handled properly, this unlock might not match the lock
37   // above because technically they act on different memory regions:
38   // __mutex_base and mutex.
39   m.unlock();
40   sleep(10); // no-warning
41 }
42 
43 struct TwoMutexes {
44   std::mutex m1;
45   std::mutex m2;
46 };
47 
48 void two_mutexes_no_false_negative(TwoMutexes &tm) {
49   tm.m1.lock();
50   // expected-note@-1 {{Entering critical section here}}
51   tm.m2.unlock();
52   sleep(10);
53   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
54   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
55   tm.m1.unlock();
56 }
57 
58 struct MyMutexBase1 : std::mutex {
59   void lock1() { lock(); }
60   // expected-note@-1 {{Entering critical section here}}
61   void unlock1() { unlock(); }
62 };
63 struct MyMutexBase2 : std::mutex {
64   void lock2() { lock(); }
65   void unlock2() { unlock(); }
66 };
67 struct MyMutex : MyMutexBase1, MyMutexBase2 {};
68 // MyMutex has two distinct std::mutex as base classes
69 
70 void custom_mutex_tp(MyMutexBase1 &mb) {
71   mb.lock();
72   // expected-note@-1 {{Entering critical section here}}
73   sleep(10);
74   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
75   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
76   mb.unlock();
77 }
78 
79 void custom_mutex_tn(MyMutexBase1 &mb) {
80   mb.lock();
81   mb.unlock();
82   sleep(10);
83 }
84 
85 void custom_mutex_cast_tp(MyMutexBase1 &mb) {
86   static_cast<std::mutex&>(mb).lock();
87   // expected-note@-1 {{Entering critical section here}}
88   sleep(10);
89   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
90   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
91   static_cast<std::mutex&>(mb).unlock();
92 }
93 
94 void custom_mutex_cast_tn(MyMutexBase1 &mb) {
95   static_cast<std::mutex&>(mb).lock();
96   static_cast<std::mutex&>(mb).unlock();
97   sleep(10);
98 }
99 
100 void two_custom_mutex_bases_tp(MyMutex &m) {
101   m.lock1();
102   // expected-note@-1 {{Calling 'MyMutexBase1::lock1'}}
103   // expected-note@-2 {{Returning from 'MyMutexBase1::lock1'}}
104   m.unlock2();
105   sleep(10);
106   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
107   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
108   m.unlock1();
109 }
110 
111 void two_custom_mutex_bases_tn(MyMutex &m) {
112   m.lock1();
113   m.unlock1();
114   sleep(10);
115 }
116 
117 void two_custom_mutex_bases_casts_tp(MyMutex &m) {
118   static_cast<MyMutexBase1&>(m).lock();
119   // expected-note@-1 {{Entering critical section here}}
120   static_cast<MyMutexBase2&>(m).unlock();
121   sleep(10);
122   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
123   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
124   static_cast<MyMutexBase1&>(m).unlock();
125 }
126 
127 void two_custom_mutex_bases_casts_tn(MyMutex &m) {
128   static_cast<MyMutexBase1&>(m).lock();
129   static_cast<MyMutexBase1&>(m).unlock();
130   sleep(10);
131 }
132 
133 struct MutexVirtBase1 : virtual std::mutex {
134   void lock1() { lock(); }
135   // expected-note@-1 {{Entering critical section here}}
136   void unlock1() { unlock(); }
137 };
138 
139 struct MutexVirtBase2 : virtual std::mutex {
140   void lock2() { lock(); }
141   void unlock2() { unlock(); }
142 };
143 
144 struct CombinedVirtMutex : MutexVirtBase1, MutexVirtBase2 {};
145 
146 void virt_inherited_mutexes_same_base_tn1(CombinedVirtMutex &cvt) {
147   cvt.lock1();
148   cvt.unlock1();
149   sleep(10);
150 }
151 
152 void virt_inherited_mutexes_different_bases_tn(CombinedVirtMutex &cvt) {
153   cvt.lock1();
154   cvt.unlock2(); // Despite a different name, unlock2 acts on the same mutex as lock1
155   sleep(10);
156 }
157 
158 void virt_inherited_mutexes_different_bases_tp(CombinedVirtMutex &cvt) {
159   cvt.lock1();
160   // expected-note@-1 {{Calling 'MutexVirtBase1::lock1'}}
161   // expected-note@-2 {{Returning from 'MutexVirtBase1::lock1'}}
162   sleep(10);
163   // expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
164   // expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
165   cvt.unlock1();
166 }
167 
168 namespace std {
169 template <class... MutexTypes> struct scoped_lock {
170   explicit scoped_lock(MutexTypes&... m);
171   ~scoped_lock();
172 };
173 template <class MutexType> class scoped_lock<MutexType> {
174 public:
175   explicit scoped_lock(MutexType& m) : m(m) { m.lock(); }
176   ~scoped_lock() { m.unlock(); }
177 private:
178   MutexType& m;
179 };
180 } // namespace std
181 
182 namespace gh_104241 {
183 int magic_number;
184 std::mutex m;
185 
186 void fixed() {
187   int current;
188   for (int items_processed = 0; items_processed < 100; ++items_processed) {
189     {
190       std::scoped_lock<std::mutex> guard(m);
191       current = magic_number;
192     }
193     sleep(current); // expected no warning
194   }
195 }
196 } // namespace gh_104241
197