kernel.return_fixes: add mipi_dsi_device_transfer(), timer_delete() and get_device()
[smatch.git] / check_locking.c
blobe009bdc41713b6d9ac05df8e9c18e384c980c0bb
1 /*
2 * Copyright (C) 2009 Dan Carpenter.
3 * Copyright (C) 2019 Oracle.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 #include <ctype.h>
20 #include "parse.h"
21 #include "smatch.h"
22 #include "smatch_extra.h"
23 #include "smatch_slist.h"
25 static int my_id;
27 STATE(locked);
28 STATE(half_locked);
29 STATE(start_state);
30 STATE(unlocked);
31 STATE(impossible);
32 STATE(restore);
33 STATE(ignore);
35 enum lock_type {
36 spin_lock,
37 read_lock,
38 write_lock,
39 mutex,
40 bottom_half,
41 irq,
42 sem,
43 prepare_lock,
44 enable_lock,
45 rcu,
46 rcu_read,
49 const char *get_lock_name(enum lock_type type)
51 static const char *names[] = {
52 [spin_lock] = "spin_lock",
53 [read_lock] = "read_lock",
54 [write_lock] = "write_lock",
55 [mutex] = "mutex",
56 [bottom_half] = "bottom_half",
57 [irq] = "irq",
58 [sem] = "sem",
59 [prepare_lock] = "prepare_lock",
60 [enable_lock] = "enable_lock",
61 [rcu] = "rcu",
62 [rcu_read] = "rcu_read",
65 return names[type];
68 #define RETURN_VAL -1
69 #define NO_ARG -2
71 struct lock_info {
72 const char *function;
73 int action;
74 enum lock_type type;
75 int arg;
76 const char *key;
77 const sval_t *implies_start, *implies_end;
78 func_hook *call_back;
81 void match_class_mutex_destructor(const char *fn, struct expression *expr, void *data);
83 static struct lock_info lock_table[] = {
84 {"spin_lock", LOCK, spin_lock, 0, "$"},
85 {"spin_unlock", UNLOCK, spin_lock, 0, "$"},
86 {"spin_lock_nested", LOCK, spin_lock, 0, "$"},
87 {"_spin_lock", LOCK, spin_lock, 0, "$"},
88 {"_spin_unlock", UNLOCK, spin_lock, 0, "$"},
89 {"_spin_lock_nested", LOCK, spin_lock, 0, "$"},
90 {"__spin_lock", LOCK, spin_lock, 0, "$"},
91 {"__spin_unlock", UNLOCK, spin_lock, 0, "$"},
92 {"__spin_lock_nested", LOCK, spin_lock, 0, "$"},
93 {"raw_spin_lock", LOCK, spin_lock, 0, "$"},
94 {"raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
95 {"_raw_spin_lock", LOCK, spin_lock, 0, "$"},
96 {"_raw_spin_lock_nested", LOCK, spin_lock, 0, "$"},
97 {"_raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
98 {"__raw_spin_lock", LOCK, spin_lock, 0, "$"},
99 {"__raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
101 {"spin_lock_irq", LOCK, spin_lock, 0, "$"},
102 {"spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
103 {"_spin_lock_irq", LOCK, spin_lock, 0, "$"},
104 {"_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
105 {"__spin_lock_irq", LOCK, spin_lock, 0, "$"},
106 {"__spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
107 {"_raw_spin_lock_irq", LOCK, spin_lock, 0, "$"},
108 {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
109 {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
110 {"spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
111 {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
112 {"_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
113 {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
114 {"__spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
115 {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
116 {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
117 {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
118 {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
119 {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
120 {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
121 {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
122 {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
123 {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
124 {"spin_lock_bh", LOCK, spin_lock, 0, "$"},
125 {"spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
126 {"_spin_lock_bh", LOCK, spin_lock, 0, "$"},
127 {"_spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
128 {"__spin_lock_bh", LOCK, spin_lock, 0, "$"},
129 {"__spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
131 {"spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
132 {"_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
133 {"__spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
134 {"raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
135 {"_raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
136 {"spin_trylock_irq", LOCK, spin_lock, 0, "$", &int_one, &int_one},
137 {"spin_trylock_irqsave", LOCK, spin_lock, 0, "$", &int_one, &int_one},
138 {"spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
139 {"_spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
140 {"__spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
141 {"__raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
142 {"_atomic_dec_and_lock", LOCK, spin_lock, 1, "$", &int_one, &int_one},
144 {"read_lock", LOCK, read_lock, 0, "$"},
145 {"down_read", LOCK, read_lock, 0, "$"},
146 {"down_read_nested", LOCK, read_lock, 0, "$"},
147 {"down_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
148 {"down_read_killable", LOCK, read_lock, 0, "$", &int_zero, &int_zero},
149 {"up_read", UNLOCK, read_lock, 0, "$"},
150 {"read_unlock", UNLOCK, read_lock, 0, "$"},
151 {"_read_lock", LOCK, read_lock, 0, "$"},
152 {"_read_unlock", UNLOCK, read_lock, 0, "$"},
153 {"__read_lock", LOCK, read_lock, 0, "$"},
154 {"__read_unlock", UNLOCK, read_lock, 0, "$"},
155 {"_raw_read_lock", LOCK, read_lock, 0, "$"},
156 {"_raw_read_unlock", UNLOCK, read_lock, 0, "$"},
157 {"__raw_read_lock", LOCK, read_lock, 0, "$"},
158 {"__raw_read_unlock", UNLOCK, read_lock, 0, "$"},
159 {"read_lock_irq", LOCK, read_lock, 0, "$"},
160 {"read_unlock_irq" , UNLOCK, read_lock, 0, "$"},
161 {"_read_lock_irq", LOCK, read_lock, 0, "$"},
162 {"_read_unlock_irq", UNLOCK, read_lock, 0, "$"},
163 {"__read_lock_irq", LOCK, read_lock, 0, "$"},
164 {"__read_unlock_irq", UNLOCK, read_lock, 0, "$"},
165 {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, "$"},
166 {"_raw_read_lock_irq", LOCK, read_lock, 0, "$"},
167 {"_raw_read_lock_bh", LOCK, read_lock, 0, "$"},
168 {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
169 {"read_lock_irqsave", LOCK, read_lock, 0, "$"},
170 {"read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
171 {"_read_lock_irqsave", LOCK, read_lock, 0, "$"},
172 {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
173 {"__read_lock_irqsave", LOCK, read_lock, 0, "$"},
174 {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
175 {"read_lock_bh", LOCK, read_lock, 0, "$"},
176 {"read_unlock_bh", UNLOCK, read_lock, 0, "$"},
177 {"_read_lock_bh", LOCK, read_lock, 0, "$"},
178 {"_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
179 {"__read_lock_bh", LOCK, read_lock, 0, "$"},
180 {"__read_unlock_bh", UNLOCK, read_lock, 0, "$"},
181 {"__raw_read_lock_bh", LOCK, read_lock, 0, "$"},
182 {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
184 {"_raw_read_lock_irqsave", LOCK, read_lock, 0, "$"},
185 {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
186 {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
187 {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
188 {"_raw_spin_lock_bh", LOCK, read_lock, 0, "$"},
189 {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
190 {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, "$"},
191 {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, "$"},
192 {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
193 {"_raw_write_lock_irqsave", LOCK, write_lock, 0, "$"},
194 {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
195 {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
196 {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
197 {"__raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
198 {"__raw_write_unlock_irq", UNLOCK, irq, 0, "irq"},
199 {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
200 {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
202 {"generic__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
203 {"read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
204 {"_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
205 {"raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
206 {"_raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
207 {"__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
208 {"__read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
210 {"write_lock", LOCK, write_lock, 0, "$"},
211 {"down_write", LOCK, write_lock, 0, "$"},
212 {"down_write_nested", LOCK, write_lock, 0, "$"},
213 {"up_write", UNLOCK, write_lock, 0, "$"},
214 {"write_unlock", UNLOCK, write_lock, 0, "$"},
215 {"_write_lock", LOCK, write_lock, 0, "$"},
216 {"_write_unlock", UNLOCK, write_lock, 0, "$"},
217 {"__write_lock", LOCK, write_lock, 0, "$"},
218 {"__write_unlock", UNLOCK, write_lock, 0, "$"},
219 {"write_lock_irq", LOCK, write_lock, 0, "$"},
220 {"write_unlock_irq", UNLOCK, write_lock, 0, "$"},
221 {"_write_lock_irq", LOCK, write_lock, 0, "$"},
222 {"_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
223 {"__write_lock_irq", LOCK, write_lock, 0, "$"},
224 {"__write_unlock_irq", UNLOCK, write_lock, 0, "$"},
225 {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
226 {"write_lock_irqsave", LOCK, write_lock, 0, "$"},
227 {"write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
228 {"_write_lock_irqsave", LOCK, write_lock, 0, "$"},
229 {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
230 {"__write_lock_irqsave", LOCK, write_lock, 0, "$"},
231 {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
232 {"write_lock_bh", LOCK, write_lock, 0, "$"},
233 {"write_unlock_bh", UNLOCK, write_lock, 0, "$"},
234 {"_write_lock_bh", LOCK, write_lock, 0, "$"},
235 {"_write_unlock_bh", UNLOCK, write_lock, 0, "$"},
236 {"__write_lock_bh", LOCK, write_lock, 0, "$"},
237 {"__write_unlock_bh", UNLOCK, write_lock, 0, "$"},
238 {"_raw_write_lock", LOCK, write_lock, 0, "$"},
239 {"__raw_write_lock", LOCK, write_lock, 0, "$"},
240 {"_raw_write_unlock", UNLOCK, write_lock, 0, "$"},
241 {"__raw_write_unlock", UNLOCK, write_lock, 0, "$"},
242 {"_raw_write_lock_bh", LOCK, write_lock, 0, "$"},
243 {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, "$"},
244 {"_raw_write_lock_irq", LOCK, write_lock, 0, "$"},
246 {"write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
247 {"_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
248 {"raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
249 {"_raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
250 {"__write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
251 {"__raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
252 {"down_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
253 {"down_write_killable", LOCK, write_lock, 0, "$", &int_zero, &int_zero},
255 {"down", LOCK, sem, 0, "$"},
256 {"up", UNLOCK, sem, 0, "$"},
257 {"down_trylock", LOCK, sem, 0, "$", &int_zero, &int_zero},
258 {"down_timeout", LOCK, sem, 0, "$", &int_zero, &int_zero},
259 {"down_interruptible", LOCK, sem, 0, "$", &int_zero, &int_zero},
260 {"down_killable", LOCK, sem, 0, "$", &int_zero, &int_zero},
263 {"mutex_lock", LOCK, mutex, 0, "$"},
264 {"mutex_unlock", UNLOCK, mutex, 0, "$"},
265 {"mutex_destroy", RESTORE, mutex, 0, "$"},
266 {"mutex_lock_nested", LOCK, mutex, 0, "$"},
267 {"mutex_lock_io", LOCK, mutex, 0, "$"},
268 {"mutex_lock_io_nested", LOCK, mutex, 0, "$"},
270 {"mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
271 {"mutex_lock_interruptible_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero},
272 {"mutex_lock_killable", LOCK, mutex, 0, "$", &int_zero, &int_zero},
273 {"mutex_lock_killable_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero},
275 {"mutex_trylock", LOCK, mutex, 0, "$", &int_one, &int_one},
277 {"ww_mutex_lock", LOCK, mutex, 0, "$"},
278 {"__ww_mutex_lock", LOCK, mutex, 0, "$"},
279 {"ww_mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
280 {"ww_mutex_unlock", UNLOCK, mutex, 0, "$"},
282 {"raw_local_irq_disable", LOCK, irq, NO_ARG, "irq"},
283 {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, "irq"},
284 {"spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
285 {"spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
286 {"_spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
287 {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
288 {"__spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
289 {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
290 {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
291 {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
292 {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
293 {"spin_trylock_irq", LOCK, irq, NO_ARG, "irq", &int_one, &int_one},
294 {"read_lock_irq", LOCK, irq, NO_ARG, "irq"},
295 {"read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
296 {"_read_lock_irq", LOCK, irq, NO_ARG, "irq"},
297 {"_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
298 {"__read_lock_irq", LOCK, irq, NO_ARG, "irq"},
299 {"_raw_read_lock_irq", LOCK, irq, NO_ARG, "irq"},
300 {"__read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
301 {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
302 {"write_lock_irq", LOCK, irq, NO_ARG, "irq"},
303 {"write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
304 {"_write_lock_irq", LOCK, irq, NO_ARG, "irq"},
305 {"_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
306 {"__write_lock_irq", LOCK, irq, NO_ARG, "irq"},
307 {"__write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
308 {"_raw_write_lock_irq", LOCK, irq, NO_ARG, "irq"},
309 {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
311 {"arch_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
312 {"arch_local_irq_restore", RESTORE, irq, 0, "$"},
313 {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
314 {"raw_local_irq_restore", RESTORE, irq, 0, "$"},
315 {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
316 {"spin_lock_irqsave", LOCK, irq, 1, "$"},
317 {"spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
318 {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
319 {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
320 {"_spin_lock_irqsave", LOCK, irq, 1, "$"},
321 {"_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
322 {"__spin_lock_irqsave_nested", LOCK, irq, 1, "$"},
323 {"__spin_lock_irqsave", LOCK, irq, 1, "$"},
324 {"__spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
325 {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
326 {"_raw_spin_lock_irqsave", LOCK, irq, 1, "$"},
327 {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
328 {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
329 {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
330 {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
331 {"spin_trylock_irqsave", LOCK, irq, 1, "$", &int_one, &int_one},
332 {"read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
333 {"read_lock_irqsave", LOCK, irq, 1, "$"},
334 {"read_unlock_irqrestore", RESTORE, irq, 1, "$"},
335 {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
336 {"_read_lock_irqsave", LOCK, irq, 1, "$"},
337 {"_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
338 {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
339 {"__read_unlock_irqrestore", RESTORE, irq, 1, "$"},
340 {"write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
341 {"write_lock_irqsave", LOCK, irq, 1, "$"},
342 {"write_unlock_irqrestore", RESTORE, irq, 1, "$"},
343 {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
344 {"_write_lock_irqsave", LOCK, irq, 1, "$"},
345 {"_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
346 {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
347 {"__write_unlock_irqrestore", RESTORE, irq, 1, "$"},
349 {"local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
350 {"_local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
351 {"__local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
352 {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
353 {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
354 {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
355 {"spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
356 {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
357 {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
358 {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
359 {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
360 {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
361 {"read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
362 {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
363 {"_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
364 {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
365 {"__read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
366 {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
367 {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
368 {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
369 {"write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
370 {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
371 {"_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
372 {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
373 {"__write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
374 {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
375 {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
376 {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, "bh"},
377 {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
378 {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
379 {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
381 {"ffs_mutex_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
383 {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, "clk"},
384 {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, "clk"},
385 {"clk_enable_lock", LOCK, enable_lock, -1, "$"},
386 {"clk_enable_unlock", UNLOCK, enable_lock, 0, "$"},
388 {"dma_resv_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
389 {"dma_resv_trylock", LOCK, mutex, 0, "$", &int_one, &int_one},
390 {"dma_resv_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
391 {"dma_resv_unlock", UNLOCK, mutex, 0, "$"},
393 {"modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
394 {"drm_ modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
395 {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
396 {"drm_exec_unlock_obj", UNLOCK, mutex, 1, "$->resv" },
397 {"modeset_unlock", UNLOCK, mutex, 0, "$"},
398 // {"nvkm_i2c_aux_acquire", LOCK, mutex,
399 {"i915_gem_object_lock_interruptible", LOCK, mutex, 0, "$->base.resv", &int_zero, &int_zero},
400 {"i915_gem_object_lock", LOCK, mutex, 0, "$->base.resv"},
401 {"msm_gem_lock", LOCK, mutex, 0, "$->resv"},
403 {"reiserfs_write_lock_nested", LOCK, mutex, 0, "$"},
404 {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, "$"},
406 {"rw_lock", LOCK, write_lock, 1, "$"},
407 {"rw_unlock", UNLOCK, write_lock, 1, "$"},
409 {"sem_lock", LOCK, mutex, 0, "$"},
410 {"sem_unlock", UNLOCK, mutex, 0, "$"},
412 {"rcu_lock_acquire", LOCK, rcu, NO_ARG, "rcu"},
413 {"rcu_lock_release", UNLOCK, rcu, NO_ARG, "rcu"},
415 {"rcu_read_lock", LOCK, rcu_read, NO_ARG, "rcu_read"},
416 {"rcu_read_unlock", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
417 {"rcu_read_lock_bh", LOCK, rcu_read, NO_ARG, "rcu_read"},
418 {"rcu_read_unlock_bh", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
420 {"rcu_read_lock_sched", LOCK, rcu_read, NO_ARG, "rcu_read"},
421 {"rcu_read_lock_sched_notrace", LOCK, rcu_read, NO_ARG, "rcu_read"},
422 {"rcu_read_unlock_sched", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
423 {"rcu_read_unlock_sched_notrace", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
425 {"gfs2_trans_begin", LOCK, sem, 0, "&$->sd_log_flush_lock", &int_zero, &int_zero},
427 {"lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
428 {"lock_sock_nested", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
429 {"lock_sock_fast", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
430 {"__lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
431 {"release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"},
432 {"__release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"},
434 {"lock_task_sighand", LOCK, spin_lock, 0, "&$->sighand->siglock", &valid_ptr_min_sval, &valid_ptr_max_sval},
436 {"rcu_nocb_unlock_irqrestore", RESTORE, spin_lock, 0, "&$->nocb_lock"},
437 {"rcu_nocb_unlock_irqrestore", RESTORE, irq, 1, "$" },
439 {"bch_write_bdev_super", IGNORE_LOCK, sem, 0, "&$->sb_write_mutex"},
440 {"bcache_write_super", IGNORE_LOCK, sem, 0, "&$->set->sb_write_mutex"},
441 {"uuid_io", IGNORE_LOCK, sem, 0, "&$->uuid_write_mutex" },
442 {"dlfb_set_video_mode", IGNORE_LOCK, sem, 0, "&$->urbs.limit_sem"},
444 {"efx_rwsem_assert_write_locked", IGNORE_LOCK, sem, 0, "&"},
446 // The i915_gem_ww_ctx_unlock_all() is too complicated
447 {"i915_gem_object_pin_pages_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"},
448 {"i915_gem_object_pin_map_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"},
449 {"i915_gem_object_fill_blt", IGNORE_LOCK, mutex, 0, "$->base.resv"},
450 {"i915_vma_pin", IGNORE_LOCK, mutex, 0, "$->base.resv"},
452 { "perf_event_period", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"},
453 { "perf_event_enable", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"},
455 { "qede_load", IGNORE_LOCK, mutex, 0, "&$->qede_lock" },
456 { "qede_unload", IGNORE_LOCK, mutex, 0, "&$->qede_lock" },
458 { "deactivate_locked_super", UNLOCK, spin_lock, 0, "&$->s_umount"},
459 { "ext4_lock_group", LOCK, spin_lock, 0, "$"},
460 { "ext4_unlock_group", UNLOCK, spin_lock, 0, "$"},
462 {"__pte_offset_map_lock", LOCK, spin_lock, 3, "*$", &valid_ptr_min_sval, &valid_ptr_max_sval},
463 {"pte_offset_map_lock", LOCK, spin_lock, 3, "*$", &valid_ptr_min_sval, &valid_ptr_max_sval},
465 {"uart_unlock_and_check_sysrq_irqrestore", UNLOCK, spin_lock, 0, "&$->lock"},
467 {"mt7530_mutex_lock", LOCK, mutex, 0, "&$->bus->mdio_lock"},
468 {"mt7530_mutex_unlock", UNLOCK, mutex, 0, "&$->bus->mdio_lock"},
470 {"class_mutex_destructor", UNLOCK, mutex, 0, "*$", NULL, NULL, &match_class_mutex_destructor},
471 {"class_rwsem_write_destructor", UNLOCK, sem, 0, "*$", NULL, NULL, &match_class_mutex_destructor},
476 struct macro_info {
477 const char *macro;
478 int action;
479 int param;
482 static struct macro_info macro_table[] = {
483 {"genpd_lock", LOCK, 0},
484 {"genpd_lock_nested", LOCK, 0},
485 {"genpd_lock_interruptible", LOCK, 0},
486 {"genpd_unlock", UNLOCK, 0},
489 static const char *false_positives[][2] = {
490 {"fs/jffs2/", "->alloc_sem"},
491 {"fs/xfs/", "->b_sema"},
492 {"mm/", "pvmw->ptl"},
493 {"drivers/gpu/drm/i915/", "->base.resv"},
496 static struct stree *start_states;
498 static struct tracker_list *locks;
500 static struct expression *ignored_reset;
501 static void reset(struct sm_state *sm, struct expression *mod_expr)
503 struct expression *faked;
505 if (mod_expr && mod_expr->type == EXPR_ASSIGNMENT &&
506 mod_expr->left == ignored_reset)
507 return;
508 faked = get_faked_expression();
509 if (faked && faked->type == EXPR_ASSIGNMENT &&
510 faked->left == ignored_reset)
511 return;
513 set_state(my_id, sm->name, sm->sym, &start_state);
516 void match_class_mutex_destructor(const char *fn, struct expression *expr, void *data)
518 struct expression *arg, *lock;
521 * What happens here is that the code looks like:
522 * class_mutex_t scope __attribute__((__cleanup__(class_mutex_destructor))) =
523 * class_mutex_constructor(&register_mutex);
524 * Then here in this hook functions expr is set to
525 * "class_mutex_destructor(&scope)". So we need to take "&scope" and
526 * trace it back to "&register_mutex". I think there is a complication
527 * as well because sometimes it's like the assignment is:
529 * scope = class_mutex_constructor(&register_mutex);
530 * but other times because we do a fake parameter assignement or
531 * something the assignment is more direct:
532 * scope = &register_mutex;
536 if (!expr || expr->type != EXPR_CALL)
537 return;
538 arg = get_argument_from_call_expr(expr->args, 0);
539 arg = strip_expr(arg);
540 if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
541 return;
542 arg = strip_expr(arg->unop);
543 lock = get_assigned_expr(arg);
544 if (!lock)
545 return;
546 if (lock->type == EXPR_CALL)
547 lock = get_argument_from_call_expr(lock->args, 0);
548 lock = strip_expr(lock);
549 if (!lock || lock->type != EXPR_PREOP || lock->op != '&')
550 return;
552 set_state_expr(my_id, lock, &unlocked);
555 static struct smatch_state *get_start_state(struct sm_state *sm)
557 struct smatch_state *orig;
559 if (!sm)
560 return NULL;
562 orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
563 if (orig)
564 return orig;
565 return NULL;
568 static struct expression *remove_spinlock_check(struct expression *expr)
570 if (expr->type != EXPR_CALL)
571 return expr;
572 if (expr->fn->type != EXPR_SYMBOL)
573 return expr;
574 if (strcmp(expr->fn->symbol_name->name, "spinlock_check"))
575 return expr;
576 expr = get_argument_from_call_expr(expr->args, 0);
577 return expr;
580 static struct expression *filter_kernel_args(struct expression *arg)
582 if (arg->type == EXPR_PREOP && arg->op == '&')
583 return strip_expr(arg->unop);
584 if (!is_pointer(arg))
585 return arg;
586 return deref_expression(strip_expr(arg));
589 static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
591 expr = remove_spinlock_check(expr);
592 expr = filter_kernel_args(expr);
593 return expr_to_str_sym(expr, sym);
596 static struct smatch_state *unmatched_state(struct sm_state *sm)
598 return &start_state;
601 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
603 if (is_impossible_path())
604 set_state(my_id, cur->name, cur->sym, &impossible);
607 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
609 if (s1 == &impossible)
610 return s2;
611 if (s2 == &impossible)
612 return s1;
613 return &merged;
616 static struct sm_state *get_best_match(const char *key, int lock_unlock)
618 struct sm_state *sm;
619 struct sm_state *match;
620 int cnt = 0;
621 int start_pos, state_len, key_len, chunks, i;
623 if (strncmp(key, "$->", 3) == 0)
624 key += 3;
626 key_len = strlen(key);
627 chunks = 0;
628 for (i = key_len - 1; i > 0; i--) {
629 if (key[i] == '>' || key[i] == '.')
630 chunks++;
631 if (chunks == 2) {
632 key += (i + 1);
633 key_len = strlen(key);
634 break;
638 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
639 if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
640 sm->state != &locked) ||
641 (lock_unlock == LOCK && sm->state != &unlocked))
642 continue;
643 state_len = strlen(sm->name);
644 if (state_len < key_len)
645 continue;
646 start_pos = state_len - key_len;
647 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
648 strcmp(sm->name + start_pos, key) == 0) {
649 cnt++;
650 match = sm;
652 } END_FOR_EACH_SM(sm);
654 if (cnt == 1)
655 return match;
656 return NULL;
659 static char *use_best_match(const char *key, int lock_unlock, struct symbol **sym)
661 struct sm_state *match;
663 match = get_best_match(key, lock_unlock);
664 if (!match) {
665 *sym = NULL;
666 return alloc_string(key);
668 *sym = match->sym;
669 return alloc_string(match->name);
672 static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
674 struct smatch_state *orig;
676 orig = get_state_stree(start_states, my_id, name, sym);
677 if (!orig)
678 set_state_stree(&start_states, my_id, name, sym, start);
679 else if (orig != start)
680 set_state_stree(&start_states, my_id, name, sym, &undefined);
683 static bool common_false_positive(const char *name)
685 const char *path, *lname;
686 int i, len_total, len_path, len_name, skip;
688 if (!get_filename())
689 return false;
691 len_total = strlen(name);
692 for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
693 path = false_positives[i][0];
694 lname = false_positives[i][1];
696 len_path = strlen(path);
697 len_name = strlen(lname);
699 if (len_name > len_total)
700 continue;
701 skip = len_total - len_name;
703 if (strncmp(get_filename(), path, len_path) == 0 &&
704 strcmp(name + skip, lname) == 0)
705 return true;
708 return false;
711 static bool sm_in_start_states(struct sm_state *sm)
713 if (!sm || !cur_func_sym)
714 return false;
715 if (sm->line == cur_func_sym->pos.line)
716 return true;
717 return false;
720 static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
722 struct sm_state *tmp;
724 if (!sm)
725 return;
727 FOR_EACH_PTR(sm->possible, tmp) {
728 if (tmp->state == state)
729 goto found;
730 } END_FOR_EACH_PTR(tmp);
732 return;
733 found:
734 // FIXME: called with read_lock held
735 // drivers/scsi/aic7xxx/aic7xxx_osm.c:1591 ahc_linux_isr() error: double locked 'flags' (orig line 1584)
736 if (strcmp(sm->name, "bottom_half") == 0)
737 return;
738 if (strstr(sm->name, "rcu"))
739 return;
740 if (strstr(sm->name, "timeline->mutex"))
741 return;
742 if (common_false_positive(sm->name))
743 return;
745 if (state == &locked && sm_in_start_states(tmp)) {
746 // sm_warning("called with lock held. '%s'", sm->name);
747 } else {
748 // sm_msg("error: double %s '%s' (orig line %u)",
749 // state->name, sm->name, tmp->line);
753 static bool handle_macro_lock_unlock(void)
755 struct expression *expr, *arg;
756 struct macro_info *info;
757 struct sm_state *sm;
758 struct symbol *sym;
759 const char *macro;
760 char *name;
761 bool ret = false;
762 int i;
764 expr = last_ptr_list((struct ptr_list *)big_expression_stack);
765 while (expr && expr->type == EXPR_ASSIGNMENT)
766 expr = strip_expr(expr->right);
767 if (!expr || expr->type != EXPR_CALL)
768 return false;
770 macro = get_macro_name(expr->pos);
771 if (!macro)
772 return false;
774 for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
775 info = &macro_table[i];
777 if (strcmp(macro, info->macro) != 0)
778 continue;
779 arg = get_argument_from_call_expr(expr->args, info->param);
780 name = expr_to_str_sym(arg, &sym);
781 if (!name || !sym)
782 goto free;
783 sm = get_sm_state(my_id, name, sym);
785 if (info->action == LOCK) {
786 if (!get_start_state(sm))
787 set_start_state(name, sym, &unlocked);
788 if (sm && sm->line != expr->pos.line)
789 warn_on_double(sm, &locked);
790 set_state(my_id, name, sym, &locked);
791 } else {
792 if (!get_start_state(sm))
793 set_start_state(name, sym, &locked);
794 if (sm && sm->line != expr->pos.line)
795 warn_on_double(sm, &unlocked);
796 set_state(my_id, name, sym, &unlocked);
798 ret = true;
799 free:
800 free_string(name);
801 return ret;
803 return false;
806 static bool is_local_IRQ_save(const char *name, struct symbol *sym, struct lock_info *info)
808 if (name && strcmp(name, "flags") == 0)
809 return true;
810 if (!sym)
811 return false;
812 if (!sym->ident || strcmp(sym->ident->name, name) != 0)
813 return false;
814 if (!info)
815 return false;
816 return strstr(info->function, "irq") && strstr(info->function, "save");
819 static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
821 struct sm_state *sm;
822 bool delete_null = false;
824 if (!info && handle_macro_lock_unlock())
825 return;
827 add_tracker(&locks, my_id, name, sym);
829 sm = get_sm_state(my_id, name, sym);
830 if (!get_start_state(sm))
831 set_start_state(name, sym, &unlocked);
832 if (!sm && !is_local_IRQ_save(name, sym, info) && !sym) {
833 sm = get_best_match(name, LOCK);
834 if (sm) {
835 name = sm->name;
836 if (sm->sym)
837 sym = sm->sym;
838 else
839 delete_null = true;
842 warn_on_double(sm, &locked);
843 if (delete_null)
844 set_state(my_id, name, NULL, &ignore);
846 set_state(my_id, name, sym, &locked);
849 static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
851 struct sm_state *sm;
852 bool delete_null = false;
854 if (__path_is_null())
855 return;
857 if (!info && handle_macro_lock_unlock())
858 return;
860 add_tracker(&locks, my_id, name, sym);
861 sm = get_sm_state(my_id, name, sym);
862 if (!sm && !info && !is_local_IRQ_save(name, sym, info)) {
863 sm = get_best_match(name, UNLOCK);
864 if (sm) {
865 name = sm->name;
866 if (sm->sym)
867 sym = sm->sym;
868 else
869 delete_null = true;
872 if (!get_start_state(sm))
873 set_start_state(name, sym, &locked);
874 warn_on_double(sm, &unlocked);
875 if (delete_null)
876 set_state(my_id, name, NULL, &ignore);
877 set_state(my_id, name, sym, &unlocked);
880 static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
882 struct sm_state *sm;
884 if (__path_is_null())
885 return;
887 sm = get_sm_state(my_id, name, sym);
888 if (!get_start_state(sm))
889 set_start_state(name, sym, &locked);
891 add_tracker(&locks, my_id, name, sym);
892 set_state(my_id, name, sym, &restore);
895 static int get_db_type(struct sm_state *sm)
898 * Bottom half is complicated because it's nestable.
899 * Say it's merged at the start and we lock and unlock then
900 * it should go back to merged.
902 if (sm->state == get_start_state(sm)) {
903 if (sm->state == &locked)
904 return KNOWN_LOCKED;
905 if (sm->state == &unlocked)
906 return KNOWN_UNLOCKED;
909 if (sm->state == &locked)
910 return LOCK;
911 if (sm->state == &unlocked)
912 return UNLOCK;
913 if (sm->state == &restore)
914 return RESTORE;
915 return LOCK;
918 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
920 struct smatch_state *start;
921 struct sm_state *sm;
922 const char *param_name;
923 int param;
925 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
926 if (sm->state != &locked &&
927 sm->state != &unlocked &&
928 sm->state != &restore)
929 continue;
930 if (sm->name[0] == '$')
931 continue;
934 * If the state is locked at the end, that doesn't mean
935 * anything. It could be been locked at the start. Or
936 * it could be &merged at the start but its locked now
937 * because of implications and not because we set the
938 * state.
940 * This is slightly a hack, but when we change the state, we
941 * call set_start_state() so if get_start_state() returns NULL
942 * that means we haven't manually the locked state.
944 start = get_start_state(sm);
945 if (sm->state == &restore) {
946 if (start != &locked)
947 continue;
948 } else if (!start || sm->state == start)
949 continue; /* !start means it was passed in */
951 param = get_param_key_from_sm(sm, expr, &param_name);
952 sql_insert_return_states(return_id, return_ranges,
953 get_db_type(sm),
954 param, param_name, "");
955 } END_FOR_EACH_SM(sm);
958 static int sm_both_locked_and_unlocked(struct sm_state *sm)
960 int is_locked = 0;
961 int is_unlocked = 0;
962 struct sm_state *tmp;
964 if (sm->state != &merged)
965 return 0;
967 FOR_EACH_PTR(sm->possible, tmp) {
968 if (tmp->state == &locked)
969 is_locked = 1;
970 if (tmp->state == &unlocked)
971 is_unlocked = 1;
972 } END_FOR_EACH_PTR(tmp);
974 return is_locked && is_unlocked;
978 static bool is_EINTR(struct range_list *rl)
980 sval_t sval;
982 if (!rl_to_sval(rl, &sval))
983 return false;
984 return sval.value == -4;
987 static bool sym_in_lock_table(struct symbol *sym)
989 int i;
991 if (!sym || !sym->ident)
992 return false;
994 for (i = 0; lock_table[i].function != NULL; i++) {
995 if (strcmp(lock_table[i].function, sym->ident->name) == 0)
996 return true;
998 return false;
1001 static bool func_in_lock_table(struct expression *expr)
1003 if (expr->type != EXPR_SYMBOL)
1004 return false;
1005 return sym_in_lock_table(expr->symbol);
1008 #define NUM_BUCKETS (RET_UNKNOWN + 1)
1010 static void check_lock(char *name, struct symbol *sym)
1012 struct range_list *locked_lines = NULL;
1013 struct range_list *unlocked_lines = NULL;
1014 int locked_buckets[NUM_BUCKETS] = {};
1015 int unlocked_buckets[NUM_BUCKETS] = {};
1016 struct stree *stree, *orig;
1017 struct sm_state *return_sm;
1018 struct sm_state *sm;
1019 sval_t line = sval_type_val(&int_ctype, 0);
1020 int bucket;
1021 int i;
1023 if (sym_in_lock_table(cur_func_sym))
1024 return;
1026 FOR_EACH_PTR(get_all_return_strees(), stree) {
1027 orig = __swap_cur_stree(stree);
1029 if (is_impossible_path())
1030 goto swap_stree;
1032 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
1033 if (!return_sm)
1034 goto swap_stree;
1035 line.value = return_sm->line;
1037 sm = get_sm_state(my_id, name, sym);
1038 if (!sm)
1039 goto swap_stree;
1041 if (parent_is_gone_var_sym(sm->name, sm->sym))
1042 goto swap_stree;
1044 if (sm->state != &locked &&
1045 sm->state != &unlocked &&
1046 sm->state != &restore)
1047 goto swap_stree;
1049 if ((sm->state == &unlocked || sm->state == &restore) &&
1050 is_EINTR(estate_rl(return_sm->state)))
1051 goto swap_stree;
1053 bucket = success_fail_return(estate_rl(return_sm->state));
1054 if (sm->state == &locked) {
1055 add_range(&locked_lines, line, line);
1056 locked_buckets[bucket] = true;
1058 if (sm->state == &unlocked || sm->state == &restore) {
1059 add_range(&unlocked_lines, line, line);
1060 unlocked_buckets[bucket] = true;
1062 swap_stree:
1063 __swap_cur_stree(orig);
1064 } END_FOR_EACH_PTR(stree);
1067 if (!locked_lines || !unlocked_lines)
1068 return;
1070 for (i = 0; i < NUM_BUCKETS; i++) {
1071 if (locked_buckets[i] && unlocked_buckets[i])
1072 goto complain;
1075 if (locked_buckets[RET_FAIL])
1076 goto complain;
1078 return;
1080 complain:
1081 if (common_false_positive(name))
1082 return;
1084 sm_msg("warn: inconsistent returns '%s'.", name);
1085 sm_printf(" Locked on : %s\n", show_rl(locked_lines));
1086 sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines));
1089 static void match_func_end(struct symbol *sym)
1091 struct tracker *tracker;
1093 FOR_EACH_PTR(locks, tracker) {
1094 check_lock(tracker->name, tracker->sym);
1095 } END_FOR_EACH_PTR(tracker);
1098 static void db_param_locked_unlocked(struct expression *expr, int param, const char *key, int lock_unlock, struct lock_info *info)
1100 struct expression *call, *arg;
1101 char *name;
1102 struct symbol *sym;
1104 if (info && info->action == IGNORE_LOCK)
1105 return;
1107 call = expr;
1108 while (call->type == EXPR_ASSIGNMENT)
1109 call = strip_expr(call->right);
1110 if (call->type != EXPR_CALL)
1111 return;
1113 if (!info && func_in_lock_table(call->fn))
1114 return;
1116 if (param == -2) {
1117 if (!info)
1118 name = use_best_match(key, lock_unlock, &sym);
1119 else {
1120 name = alloc_string(info->key);
1121 sym = NULL;
1123 } else if (param == -1) {
1124 if (expr->type != EXPR_ASSIGNMENT)
1125 return;
1126 ignored_reset = expr->left;
1128 name = get_variable_from_key(expr->left, key, &sym);
1129 } else {
1130 arg = get_argument_from_call_expr(call->args, param);
1131 if (!arg)
1132 return;
1134 arg = remove_spinlock_check(arg);
1135 name = get_variable_from_key(arg, key, &sym);
1138 if (!name || !sym)
1139 goto free;
1141 if (lock_unlock == LOCK)
1142 do_lock(name, sym, info);
1143 else if (lock_unlock == UNLOCK)
1144 do_unlock(name, sym, info);
1145 else if (lock_unlock == RESTORE)
1146 do_restore(name, sym, info);
1148 free:
1149 free_string(name);
1152 static void db_param_locked(struct expression *expr, int param, char *key, char *value)
1154 db_param_locked_unlocked(expr, param, key, LOCK, NULL);
1157 static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
1159 db_param_locked_unlocked(expr, param, key, UNLOCK, NULL);
1162 static void db_param_restore(struct expression *expr, int param, char *key, char *value)
1164 db_param_locked_unlocked(expr, param, key, RESTORE, NULL);
1167 static void match_lock_unlock(const char *fn, struct expression *expr, void *data)
1169 struct lock_info *info = data;
1170 struct expression *parent;
1172 if (info->arg == -1) {
1173 parent = expr_get_parent_expr(expr);
1174 while (parent && parent->type != EXPR_ASSIGNMENT)
1175 parent = expr_get_parent_expr(parent);
1176 if (!parent || parent->type != EXPR_ASSIGNMENT)
1177 return;
1178 expr = parent;
1181 db_param_locked_unlocked(expr, info->arg, info->key, info->action, info);
1184 static void match_lock_held(const char *fn, struct expression *call_expr,
1185 struct expression *assign_expr, void *data)
1187 struct lock_info *info = data;
1189 db_param_locked_unlocked(assign_expr ?: call_expr, info->arg, info->key, info->action, info);
1192 static void match_assign(struct expression *expr)
1194 struct smatch_state *state;
1196 /* This is only for the DB */
1197 if (is_fake_var_assign(expr))
1198 return;
1199 state = get_state_expr(my_id, expr->right);
1200 if (!state)
1201 return;
1202 set_state_expr(my_id, expr->left, state);
1205 static struct stree *printed;
1206 static void call_info_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
1208 int locked_type = 0;
1210 if (sm->state == &locked)
1211 locked_type = LOCK;
1212 else if (sm->state == &unlocked)
1213 locked_type = UNLOCK;
1214 else if (slist_has_state(sm->possible, &locked) ||
1215 slist_has_state(sm->possible, &half_locked))
1216 locked_type = HALF_LOCKED;
1217 else
1218 return;
1220 avl_insert(&printed, sm);
1221 sql_insert_caller_info(call, locked_type, param, printed_name, "");
1224 static void match_call_info(struct expression *expr)
1226 struct sm_state *sm;
1227 const char *name;
1228 int locked_type;
1230 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
1231 if (sm->state == &locked)
1232 locked_type = LOCK;
1233 else if (sm->state == &half_locked ||
1234 slist_has_state(sm->possible, &locked))
1235 locked_type = HALF_LOCKED;
1236 else
1237 continue;
1239 if (avl_lookup(printed, sm))
1240 continue;
1242 if (strcmp(sm->name, "bottom_half") == 0)
1243 name = "bh";
1244 else if (strcmp(sm->name, "rcu_read") == 0)
1245 name = "rcu_read_lock";
1246 else
1247 name = sm->name;
1249 if (strncmp(name, "__fake_param", 12) == 0 ||
1250 strchr(name, '$'))
1251 continue;
1253 sql_insert_caller_info(expr, locked_type, -2, name, "");
1254 } END_FOR_EACH_SM(sm);
1255 free_stree(&printed);
1258 static void set_locked(const char *name, struct symbol *sym, char *value)
1260 set_state(my_id, name, sym, &locked);
1263 static void set_half_locked(const char *name, struct symbol *sym, char *value)
1265 set_state(my_id, name, sym, &half_locked);
1268 static void set_unlocked(const char *name, struct symbol *sym, char *value)
1270 set_state(my_id, name, sym, &unlocked);
1273 static void match_after_func(struct symbol *sym)
1275 free_stree(&start_states);
1278 static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
1279 struct expression *assign_expr, void *_index)
1281 struct expression *lock, *ctx;
1282 char *lock_name;
1283 struct symbol *sym;
1285 lock = get_argument_from_call_expr(call_expr->args, 0);
1286 ctx = get_argument_from_call_expr(call_expr->args, 1);
1287 if (!expr_is_zero(ctx))
1288 return;
1290 lock_name = lock_to_name_sym(lock, &sym);
1291 if (!lock_name || !sym)
1292 goto free;
1293 do_lock(lock_name, sym, NULL);
1294 free:
1295 free_string(lock_name);
1298 /* print_held_locks() is used in check_call_tree.c */
1299 void print_held_locks(void)
1301 struct stree *stree;
1302 struct sm_state *sm;
1303 int i = 0;
1305 stree = __get_cur_stree();
1306 FOR_EACH_MY_SM(my_id, stree, sm) {
1307 if (sm->state != &locked)
1308 continue;
1309 if (i++)
1310 sm_printf(" ");
1311 sm_printf("'%s'", sm->name);
1312 } END_FOR_EACH_SM(sm);
1315 static void load_table(struct lock_info *lock_table)
1317 int i;
1319 for (i = 0; lock_table[i].function != NULL; i++) {
1320 struct lock_info *lock = &lock_table[i];
1322 if (lock->call_back) {
1323 add_function_hook(lock->function, lock->call_back, lock);
1324 } else if (lock->implies_start) {
1325 return_implies_state_sval(lock->function,
1326 *lock->implies_start,
1327 *lock->implies_end,
1328 &match_lock_held, lock);
1329 } else {
1330 add_function_hook(lock->function, &match_lock_unlock, lock);
1335 static bool is_smp_config(void)
1337 struct ident *id;
1339 id = built_in_ident("CONFIG_SMP");
1340 return !!lookup_symbol(id, NS_MACRO);
1343 void check_locking(int id)
1345 my_id = id;
1347 if (option_project != PROJ_KERNEL)
1348 return;
1350 if (!is_smp_config())
1351 return;
1353 load_table(lock_table);
1355 set_dynamic_states(my_id);
1356 add_unmatched_state_hook(my_id, &unmatched_state);
1357 add_pre_merge_hook(my_id, &pre_merge_hook);
1358 add_merge_hook(my_id, &merge_func);
1359 add_modification_hook(my_id, &reset);
1361 add_hook(&match_assign, ASSIGNMENT_HOOK);
1362 add_hook(&match_func_end, END_FUNC_HOOK);
1364 add_hook(&match_after_func, AFTER_FUNC_HOOK);
1365 add_function_data((unsigned long *)&start_states);
1367 add_caller_info_callback(my_id, call_info_callback);
1368 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
1370 add_split_return_callback(match_return_info);
1371 select_return_states_hook(LOCK, &db_param_locked);
1372 select_return_states_hook(UNLOCK, &db_param_unlocked);
1373 select_return_states_hook(RESTORE, &db_param_restore);
1375 select_caller_name_sym(set_locked, LOCK);
1376 select_caller_name_sym(set_half_locked, HALF_LOCKED);
1377 select_caller_name_sym(set_unlocked, UNLOCK);
1379 return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);