locking: delete some NULL checks
[smatch.git] / check_locking.c
blob73bb21ec90f0c7aa46e34e3e3fc62056fbe02f0c
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 static struct lock_info lock_table[] = {
82 {"spin_lock", LOCK, spin_lock, 0, "$"},
83 {"spin_unlock", UNLOCK, spin_lock, 0, "$"},
84 {"spin_lock_nested", LOCK, spin_lock, 0, "$"},
85 {"_spin_lock", LOCK, spin_lock, 0, "$"},
86 {"_spin_unlock", UNLOCK, spin_lock, 0, "$"},
87 {"_spin_lock_nested", LOCK, spin_lock, 0, "$"},
88 {"__spin_lock", LOCK, spin_lock, 0, "$"},
89 {"__spin_unlock", UNLOCK, spin_lock, 0, "$"},
90 {"__spin_lock_nested", LOCK, spin_lock, 0, "$"},
91 {"raw_spin_lock", LOCK, spin_lock, 0, "$"},
92 {"raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
93 {"_raw_spin_lock", LOCK, spin_lock, 0, "$"},
94 {"_raw_spin_lock_nested", LOCK, spin_lock, 0, "$"},
95 {"_raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
96 {"__raw_spin_lock", LOCK, spin_lock, 0, "$"},
97 {"__raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
99 {"spin_lock_irq", LOCK, spin_lock, 0, "$"},
100 {"spin_unlock_irq", 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 {"_raw_spin_lock_irq", LOCK, spin_lock, 0, "$"},
106 {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
107 {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
108 {"spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
109 {"spin_unlock_irqrestore", 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 {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
115 {"_raw_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 {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
119 {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
120 {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
121 {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
122 {"spin_lock_bh", LOCK, spin_lock, 0, "$"},
123 {"spin_unlock_bh", UNLOCK, 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, "$"},
129 {"spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
130 {"_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
131 {"__spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
132 {"raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
133 {"_raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
134 {"spin_trylock_irq", LOCK, spin_lock, 0, "$", &int_one, &int_one},
135 {"spin_trylock_irqsave", LOCK, spin_lock, 0, "$", &int_one, &int_one},
136 {"spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
137 {"_spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
138 {"__spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one},
139 {"__raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one},
140 {"_atomic_dec_and_lock", LOCK, spin_lock, 1, "$", &int_one, &int_one},
142 {"read_lock", LOCK, read_lock, 0, "$"},
143 {"down_read", LOCK, read_lock, 0, "$"},
144 {"down_read_nested", LOCK, read_lock, 0, "$"},
145 {"down_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
146 {"down_read_killable", LOCK, read_lock, 0, "$", &int_zero, &int_zero},
147 {"up_read", UNLOCK, read_lock, 0, "$"},
148 {"read_unlock", UNLOCK, read_lock, 0, "$"},
149 {"_read_lock", LOCK, 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 {"_raw_read_lock", LOCK, read_lock, 0, "$"},
154 {"_raw_read_unlock", UNLOCK, read_lock, 0, "$"},
155 {"__raw_read_lock", LOCK, read_lock, 0, "$"},
156 {"__raw_read_unlock", UNLOCK, read_lock, 0, "$"},
157 {"read_lock_irq", LOCK, read_lock, 0, "$"},
158 {"read_unlock_irq" , 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 {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, "$"},
164 {"_raw_read_lock_irq", LOCK, read_lock, 0, "$"},
165 {"_raw_read_lock_bh", LOCK, read_lock, 0, "$"},
166 {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
167 {"read_lock_irqsave", LOCK, read_lock, 0, "$"},
168 {"read_unlock_irqrestore", 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_bh", LOCK, read_lock, 0, "$"},
174 {"read_unlock_bh", 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 {"__raw_read_lock_bh", LOCK, read_lock, 0, "$"},
180 {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
182 {"_raw_read_lock_irqsave", LOCK, read_lock, 0, "$"},
183 {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
184 {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
185 {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
186 {"_raw_spin_lock_bh", LOCK, read_lock, 0, "$"},
187 {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
188 {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, "$"},
189 {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, "$"},
190 {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
191 {"_raw_write_lock_irqsave", LOCK, write_lock, 0, "$"},
192 {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
193 {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
194 {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
195 {"__raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
196 {"__raw_write_unlock_irq", UNLOCK, irq, 0, "irq"},
197 {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
198 {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
200 {"generic__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
201 {"read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
202 {"_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
203 {"raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
204 {"_raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
205 {"__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
206 {"__read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one},
208 {"write_lock", LOCK, write_lock, 0, "$"},
209 {"down_write", LOCK, write_lock, 0, "$"},
210 {"down_write_nested", LOCK, write_lock, 0, "$"},
211 {"up_write", UNLOCK, write_lock, 0, "$"},
212 {"write_unlock", UNLOCK, write_lock, 0, "$"},
213 {"_write_lock", LOCK, 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_irq", LOCK, write_lock, 0, "$"},
218 {"write_unlock_irq", 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 {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
224 {"write_lock_irqsave", LOCK, write_lock, 0, "$"},
225 {"write_unlock_irqrestore", 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_bh", LOCK, write_lock, 0, "$"},
231 {"write_unlock_bh", 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 {"_raw_write_lock", LOCK, write_lock, 0, "$"},
237 {"__raw_write_lock", LOCK, write_lock, 0, "$"},
238 {"_raw_write_unlock", UNLOCK, write_lock, 0, "$"},
239 {"__raw_write_unlock", UNLOCK, write_lock, 0, "$"},
240 {"_raw_write_lock_bh", LOCK, write_lock, 0, "$"},
241 {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, "$"},
242 {"_raw_write_lock_irq", LOCK, write_lock, 0, "$"},
244 {"write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
245 {"_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
246 {"raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
247 {"_raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
248 {"__write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
249 {"__raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
250 {"down_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one},
251 {"down_write_killable", LOCK, write_lock, 0, "$", &int_zero, &int_zero},
253 {"down", LOCK, sem, 0, "$"},
254 {"up", UNLOCK, sem, 0, "$"},
255 {"down_trylock", LOCK, sem, 0, "$", &int_zero, &int_zero},
256 {"down_timeout", LOCK, sem, 0, "$", &int_zero, &int_zero},
257 {"down_interruptible", LOCK, sem, 0, "$", &int_zero, &int_zero},
258 {"down_killable", LOCK, sem, 0, "$", &int_zero, &int_zero},
261 {"mutex_lock", LOCK, mutex, 0, "$"},
262 {"mutex_unlock", UNLOCK, mutex, 0, "$"},
263 {"mutex_destroy", RESTORE, mutex, 0, "$"},
264 {"mutex_lock_nested", LOCK, mutex, 0, "$"},
265 {"mutex_lock_io", LOCK, mutex, 0, "$"},
266 {"mutex_lock_io_nested", LOCK, mutex, 0, "$"},
268 {"mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
269 {"mutex_lock_interruptible_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero},
270 {"mutex_lock_killable", LOCK, mutex, 0, "$", &int_zero, &int_zero},
271 {"mutex_lock_killable_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero},
273 {"mutex_trylock", LOCK, mutex, 0, "$", &int_one, &int_one},
275 {"ww_mutex_lock", LOCK, mutex, 0, "$"},
276 {"__ww_mutex_lock", LOCK, mutex, 0, "$"},
277 {"ww_mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
278 {"ww_mutex_unlock", UNLOCK, mutex, 0, "$"},
280 {"raw_local_irq_disable", LOCK, irq, NO_ARG, "irq"},
281 {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, "irq"},
282 {"spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
283 {"spin_unlock_irq", 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 {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
289 {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
290 {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
291 {"spin_trylock_irq", LOCK, irq, NO_ARG, "irq", &int_one, &int_one},
292 {"read_lock_irq", LOCK, irq, NO_ARG, "irq"},
293 {"read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
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 {"_raw_read_lock_irq", LOCK, irq, NO_ARG, "irq"},
298 {"__read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
299 {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
300 {"write_lock_irq", LOCK, irq, NO_ARG, "irq"},
301 {"write_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 {"_raw_write_lock_irq", LOCK, irq, NO_ARG, "irq"},
307 {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
309 {"arch_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
310 {"arch_local_irq_restore", RESTORE, irq, 0, "$"},
311 {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
312 {"raw_local_irq_restore", RESTORE, irq, 0, "$"},
313 {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
314 {"spin_lock_irqsave", LOCK, irq, 1, "$"},
315 {"spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
316 {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
317 {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
318 {"_spin_lock_irqsave", LOCK, irq, 1, "$"},
319 {"_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
320 {"__spin_lock_irqsave_nested", LOCK, irq, 1, "$"},
321 {"__spin_lock_irqsave", LOCK, irq, 1, "$"},
322 {"__spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
323 {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
324 {"_raw_spin_lock_irqsave", LOCK, irq, 1, "$"},
325 {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
326 {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
327 {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
328 {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
329 {"spin_trylock_irqsave", LOCK, irq, 1, "$", &int_one, &int_one},
330 {"read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
331 {"read_lock_irqsave", LOCK, irq, 1, "$"},
332 {"read_unlock_irqrestore", RESTORE, irq, 1, "$"},
333 {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
334 {"_read_lock_irqsave", LOCK, irq, 1, "$"},
335 {"_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
336 {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
337 {"__read_unlock_irqrestore", RESTORE, irq, 1, "$"},
338 {"write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
339 {"write_lock_irqsave", LOCK, irq, 1, "$"},
340 {"write_unlock_irqrestore", RESTORE, irq, 1, "$"},
341 {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
342 {"_write_lock_irqsave", LOCK, irq, 1, "$"},
343 {"_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
344 {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
345 {"__write_unlock_irqrestore", RESTORE, irq, 1, "$"},
347 {"local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
348 {"_local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
349 {"__local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
350 {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
351 {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
352 {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
353 {"spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
354 {"spin_unlock_bh", 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 {"read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
360 {"read_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 {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
366 {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
367 {"write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
368 {"write_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 {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
374 {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, "bh"},
375 {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
376 {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
377 {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one},
379 {"ffs_mutex_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
381 {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, "clk"},
382 {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, "clk"},
383 {"clk_enable_lock", LOCK, enable_lock, -1, "$"},
384 {"clk_enable_unlock", UNLOCK, enable_lock, 0, "$"},
386 {"dma_resv_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
387 {"dma_resv_trylock", LOCK, mutex, 0, "$", &int_one, &int_one},
388 {"dma_resv_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
389 {"dma_resv_unlock", UNLOCK, mutex, 0, "$"},
391 {"modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
392 {"drm_ modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero},
393 {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero},
394 {"modeset_unlock", UNLOCK, mutex, 0, "$"},
395 // {"nvkm_i2c_aux_acquire", LOCK, mutex,
396 {"i915_gem_object_lock_interruptible", LOCK, mutex, 0, "$->base.resv", &int_zero, &int_zero},
397 {"i915_gem_object_lock", LOCK, mutex, 0, "$->base.resv"},
398 {"msm_gem_lock", LOCK, mutex, 0, "$->resv"},
400 {"reiserfs_write_lock_nested", LOCK, mutex, 0, "$"},
401 {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, "$"},
403 {"rw_lock", LOCK, write_lock, 1, "$"},
404 {"rw_unlock", UNLOCK, write_lock, 1, "$"},
406 {"sem_lock", LOCK, mutex, 0, "$"},
407 {"sem_unlock", UNLOCK, mutex, 0, "$"},
409 {"rcu_lock_acquire", LOCK, rcu, NO_ARG, "rcu"},
410 {"rcu_lock_release", UNLOCK, rcu, NO_ARG, "rcu"},
412 {"rcu_read_lock", LOCK, rcu_read, NO_ARG, "rcu_read"},
413 {"rcu_read_unlock", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
414 {"rcu_read_lock_bh", LOCK, rcu_read, NO_ARG, "rcu_read"},
415 {"rcu_read_unlock_bh", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
417 {"rcu_read_lock_sched", LOCK, rcu_read, NO_ARG, "rcu_read"},
418 {"rcu_read_lock_sched_notrace", LOCK, rcu_read, NO_ARG, "rcu_read"},
419 {"rcu_read_unlock_sched", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
420 {"rcu_read_unlock_sched_notrace", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
422 {"gfs2_trans_begin", LOCK, sem, 0, "&$->sd_log_flush_lock", &int_zero, &int_zero},
424 {"lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
425 {"lock_sock_nested", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
426 {"lock_sock_fast", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
427 {"__lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"},
428 {"release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"},
429 {"__release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"},
431 {"lock_task_sighand", LOCK, spin_lock, 0, "&$->sighand->siglock", &valid_ptr_min_sval, &valid_ptr_max_sval},
433 {"rcu_nocb_unlock_irqrestore", RESTORE, spin_lock, 0, "&$->nocb_lock"},
434 {"rcu_nocb_unlock_irqrestore", RESTORE, irq, 1, "$" },
436 {"bch_write_bdev_super", IGNORE_LOCK, sem, 0, "&$->sb_write_mutex"},
437 {"dlfb_set_video_mode", IGNORE_LOCK, sem, 0, "&$->urbs.limit_sem"},
439 {"efx_rwsem_assert_write_locked", IGNORE_LOCK, sem, 0, "&"},
441 // The i915_gem_ww_ctx_unlock_all() is too complicated
442 {"i915_gem_object_pin_pages_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"},
443 {"i915_gem_object_pin_map_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"},
444 {"i915_gem_object_fill_blt", IGNORE_LOCK, mutex, 0, "$->base.resv"},
445 {"i915_vma_pin", IGNORE_LOCK, mutex, 0, "$->base.resv"},
447 { "perf_event_period", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"},
448 { "perf_event_enable", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"},
453 struct macro_info {
454 const char *macro;
455 int action;
456 int param;
459 static struct macro_info macro_table[] = {
460 {"genpd_lock", LOCK, 0},
461 {"genpd_lock_nested", LOCK, 0},
462 {"genpd_lock_interruptible", LOCK, 0},
463 {"genpd_unlock", UNLOCK, 0},
466 static const char *false_positives[][2] = {
467 {"fs/jffs2/", "->alloc_sem"},
468 {"fs/xfs/", "->b_sema"},
469 {"mm/", "pvmw->ptl"},
470 {"drivers/gpu/drm/i915/", "->base.resv"},
473 static struct stree *start_states;
475 static struct tracker_list *locks;
477 static struct expression *ignored_reset;
478 static void reset(struct sm_state *sm, struct expression *mod_expr)
480 struct expression *faked;
482 if (mod_expr && mod_expr->type == EXPR_ASSIGNMENT &&
483 mod_expr->left == ignored_reset)
484 return;
485 faked = get_faked_expression();
486 if (faked && faked->type == EXPR_ASSIGNMENT &&
487 faked->left == ignored_reset)
488 return;
490 set_state(my_id, sm->name, sm->sym, &start_state);
493 static struct smatch_state *get_start_state(struct sm_state *sm)
495 struct smatch_state *orig;
497 if (!sm)
498 return NULL;
500 orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
501 if (orig)
502 return orig;
503 return NULL;
506 static struct expression *remove_spinlock_check(struct expression *expr)
508 if (expr->type != EXPR_CALL)
509 return expr;
510 if (expr->fn->type != EXPR_SYMBOL)
511 return expr;
512 if (strcmp(expr->fn->symbol_name->name, "spinlock_check"))
513 return expr;
514 expr = get_argument_from_call_expr(expr->args, 0);
515 return expr;
518 static struct expression *filter_kernel_args(struct expression *arg)
520 if (arg->type == EXPR_PREOP && arg->op == '&')
521 return strip_expr(arg->unop);
522 if (!is_pointer(arg))
523 return arg;
524 return deref_expression(strip_expr(arg));
527 static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
529 expr = remove_spinlock_check(expr);
530 expr = filter_kernel_args(expr);
531 return expr_to_str_sym(expr, sym);
534 static struct smatch_state *unmatched_state(struct sm_state *sm)
536 return &start_state;
539 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
541 if (is_impossible_path())
542 set_state(my_id, cur->name, cur->sym, &impossible);
545 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
547 if (s1 == &impossible)
548 return s2;
549 if (s2 == &impossible)
550 return s1;
551 return &merged;
554 static struct sm_state *get_best_match(const char *key, int lock_unlock)
556 struct sm_state *sm;
557 struct sm_state *match;
558 int cnt = 0;
559 int start_pos, state_len, key_len, chunks, i;
561 if (strncmp(key, "$->", 3) == 0)
562 key += 3;
564 key_len = strlen(key);
565 chunks = 0;
566 for (i = key_len - 1; i > 0; i--) {
567 if (key[i] == '>' || key[i] == '.')
568 chunks++;
569 if (chunks == 2) {
570 key += (i + 1);
571 key_len = strlen(key);
572 break;
576 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
577 if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
578 sm->state != &locked) ||
579 (lock_unlock == LOCK && sm->state != &unlocked))
580 continue;
581 state_len = strlen(sm->name);
582 if (state_len < key_len)
583 continue;
584 start_pos = state_len - key_len;
585 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
586 strcmp(sm->name + start_pos, key) == 0) {
587 cnt++;
588 match = sm;
590 } END_FOR_EACH_SM(sm);
592 if (cnt == 1)
593 return match;
594 return NULL;
597 static char *use_best_match(const char *key, int lock_unlock, struct symbol **sym)
599 struct sm_state *match;
601 match = get_best_match(key, lock_unlock);
602 if (!match) {
603 *sym = NULL;
604 return alloc_string(key);
606 *sym = match->sym;
607 return alloc_string(match->name);
610 static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
612 struct smatch_state *orig;
614 orig = get_state_stree(start_states, my_id, name, sym);
615 if (!orig)
616 set_state_stree(&start_states, my_id, name, sym, start);
617 else if (orig != start)
618 set_state_stree(&start_states, my_id, name, sym, &undefined);
621 static bool common_false_positive(const char *name)
623 const char *path, *lname;
624 int i, len_total, len_path, len_name, skip;
626 if (!get_filename())
627 return false;
629 len_total = strlen(name);
630 for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
631 path = false_positives[i][0];
632 lname = false_positives[i][1];
634 len_path = strlen(path);
635 len_name = strlen(lname);
637 if (len_name > len_total)
638 continue;
639 skip = len_total - len_name;
641 if (strncmp(get_filename(), path, len_path) == 0 &&
642 strcmp(name + skip, lname) == 0)
643 return true;
646 return false;
649 static bool sm_in_start_states(struct sm_state *sm)
651 if (!sm || !cur_func_sym)
652 return false;
653 if (sm->line == cur_func_sym->pos.line)
654 return true;
655 return false;
658 static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
660 struct sm_state *tmp;
662 if (!sm)
663 return;
665 FOR_EACH_PTR(sm->possible, tmp) {
666 if (tmp->state == state)
667 goto found;
668 } END_FOR_EACH_PTR(tmp);
670 return;
671 found:
672 // FIXME: called with read_lock held
673 // drivers/scsi/aic7xxx/aic7xxx_osm.c:1591 ahc_linux_isr() error: double locked 'flags' (orig line 1584)
674 if (strcmp(sm->name, "bottom_half") == 0)
675 return;
676 if (strstr(sm->name, "rcu"))
677 return;
678 if (strstr(sm->name, "timeline->mutex"))
679 return;
680 if (common_false_positive(sm->name))
681 return;
683 if (state == &locked && sm_in_start_states(tmp)) {
684 // sm_warning("called with lock held. '%s'", sm->name);
685 } else {
686 // sm_msg("error: double %s '%s' (orig line %u)",
687 // state->name, sm->name, tmp->line);
691 static bool handle_macro_lock_unlock(void)
693 struct expression *expr, *arg;
694 struct macro_info *info;
695 struct sm_state *sm;
696 struct symbol *sym;
697 const char *macro;
698 char *name;
699 bool ret = false;
700 int i;
702 expr = last_ptr_list((struct ptr_list *)big_expression_stack);
703 while (expr && expr->type == EXPR_ASSIGNMENT)
704 expr = strip_expr(expr->right);
705 if (!expr || expr->type != EXPR_CALL)
706 return false;
708 macro = get_macro_name(expr->pos);
709 if (!macro)
710 return false;
712 for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
713 info = &macro_table[i];
715 if (strcmp(macro, info->macro) != 0)
716 continue;
717 arg = get_argument_from_call_expr(expr->args, info->param);
718 name = expr_to_str_sym(arg, &sym);
719 if (!name || !sym)
720 goto free;
721 sm = get_sm_state(my_id, name, sym);
723 if (info->action == LOCK) {
724 if (!get_start_state(sm))
725 set_start_state(name, sym, &unlocked);
726 if (sm && sm->line != expr->pos.line)
727 warn_on_double(sm, &locked);
728 set_state(my_id, name, sym, &locked);
729 } else {
730 if (!get_start_state(sm))
731 set_start_state(name, sym, &locked);
732 if (sm && sm->line != expr->pos.line)
733 warn_on_double(sm, &unlocked);
734 set_state(my_id, name, sym, &unlocked);
736 ret = true;
737 free:
738 free_string(name);
739 return ret;
741 return false;
744 static bool is_local_IRQ_save(const char *name, struct symbol *sym, struct lock_info *info)
746 if (name && strcmp(name, "flags") == 0)
747 return true;
748 if (!sym)
749 return false;
750 if (!sym->ident || strcmp(sym->ident->name, name) != 0)
751 return false;
752 if (!info)
753 return false;
754 return strstr(info->function, "irq") && strstr(info->function, "save");
757 static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
759 struct sm_state *sm;
760 bool delete_null = false;
762 if (!info && handle_macro_lock_unlock())
763 return;
765 add_tracker(&locks, my_id, name, sym);
767 sm = get_sm_state(my_id, name, sym);
768 if (!get_start_state(sm))
769 set_start_state(name, sym, &unlocked);
770 if (!sm && !is_local_IRQ_save(name, sym, info) && !sym) {
771 sm = get_best_match(name, LOCK);
772 if (sm) {
773 name = sm->name;
774 if (sm->sym)
775 sym = sm->sym;
776 else
777 delete_null = true;
780 warn_on_double(sm, &locked);
781 if (delete_null)
782 set_state(my_id, name, NULL, &ignore);
784 set_state(my_id, name, sym, &locked);
787 static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
789 struct sm_state *sm;
790 bool delete_null = false;
792 if (__path_is_null())
793 return;
795 if (!info && handle_macro_lock_unlock())
796 return;
798 add_tracker(&locks, my_id, name, sym);
799 sm = get_sm_state(my_id, name, sym);
800 if (!sm && !info && !is_local_IRQ_save(name, sym, info)) {
801 sm = get_best_match(name, UNLOCK);
802 if (sm) {
803 name = sm->name;
804 if (sm->sym)
805 sym = sm->sym;
806 else
807 delete_null = true;
810 if (!get_start_state(sm))
811 set_start_state(name, sym, &locked);
812 warn_on_double(sm, &unlocked);
813 if (delete_null)
814 set_state(my_id, name, NULL, &ignore);
815 set_state(my_id, name, sym, &unlocked);
818 static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
820 struct sm_state *sm;
822 if (__path_is_null())
823 return;
825 sm = get_sm_state(my_id, name, sym);
826 if (!get_start_state(sm))
827 set_start_state(name, sym, &locked);
829 add_tracker(&locks, my_id, name, sym);
830 set_state(my_id, name, sym, &restore);
833 static int get_db_type(struct sm_state *sm)
836 * Bottom half is complicated because it's nestable.
837 * Say it's merged at the start and we lock and unlock then
838 * it should go back to merged.
840 if (sm->state == get_start_state(sm)) {
841 if (sm->state == &locked)
842 return KNOWN_LOCKED;
843 if (sm->state == &unlocked)
844 return KNOWN_UNLOCKED;
847 if (sm->state == &locked)
848 return LOCK;
849 if (sm->state == &unlocked)
850 return UNLOCK;
851 if (sm->state == &restore)
852 return RESTORE;
853 return LOCK;
856 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
858 struct smatch_state *start;
859 struct sm_state *sm;
860 const char *param_name;
861 int param;
863 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
864 if (sm->state != &locked &&
865 sm->state != &unlocked &&
866 sm->state != &restore)
867 continue;
868 if (sm->name[0] == '$')
869 continue;
872 * If the state is locked at the end, that doesn't mean
873 * anything. It could be been locked at the start. Or
874 * it could be &merged at the start but its locked now
875 * because of implications and not because we set the
876 * state.
878 * This is slightly a hack, but when we change the state, we
879 * call set_start_state() so if get_start_state() returns NULL
880 * that means we haven't manually the locked state.
882 start = get_start_state(sm);
883 if (sm->state == &restore) {
884 if (start != &locked)
885 continue;
886 } else if (!start || sm->state == start)
887 continue; /* !start means it was passed in */
889 param = get_param_key_from_sm(sm, expr, &param_name);
890 sql_insert_return_states(return_id, return_ranges,
891 get_db_type(sm),
892 param, param_name, "");
893 } END_FOR_EACH_SM(sm);
896 static int sm_both_locked_and_unlocked(struct sm_state *sm)
898 int is_locked = 0;
899 int is_unlocked = 0;
900 struct sm_state *tmp;
902 if (sm->state != &merged)
903 return 0;
905 FOR_EACH_PTR(sm->possible, tmp) {
906 if (tmp->state == &locked)
907 is_locked = 1;
908 if (tmp->state == &unlocked)
909 is_unlocked = 1;
910 } END_FOR_EACH_PTR(tmp);
912 return is_locked && is_unlocked;
915 enum {
916 ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS,
919 static bool is_EINTR(struct range_list *rl)
921 sval_t sval;
923 if (!rl_to_sval(rl, &sval))
924 return false;
925 return sval.value == -4;
928 static int success_fail_positive(struct range_list *rl)
930 /* void returns are the same as success (zero in the kernel) */
931 if (!rl)
932 return ZERO;
934 if (rl_type(rl)->type != SYM_PTR &&
935 !is_whole_rl(rl) &&
936 sval_is_negative(rl_min(rl)))
937 return NEGATIVE;
939 if (rl_min(rl).value == 0 && rl_max(rl).value == 0)
940 return ZERO;
942 if (is_err_ptr(rl_min(rl)) &&
943 is_err_ptr(rl_max(rl)))
944 return ERR_PTR;
947 * Trying to match ERR_PTR(ret) but without the expression struct.
948 * Ugly...
950 if (type_bits(&long_ctype) == 64 &&
951 rl_type(rl)->type == SYM_PTR &&
952 rl_min(rl).value == INT_MIN)
953 return ERR_PTR;
955 return POSITIVE;
958 static bool sym_in_lock_table(struct symbol *sym)
960 int i;
962 if (!sym || !sym->ident)
963 return false;
965 for (i = 0; lock_table[i].function != NULL; i++) {
966 if (strcmp(lock_table[i].function, sym->ident->name) == 0)
967 return true;
969 return false;
972 static bool func_in_lock_table(struct expression *expr)
974 if (expr->type != EXPR_SYMBOL)
975 return false;
976 return sym_in_lock_table(expr->symbol);
979 static void check_lock(char *name, struct symbol *sym)
981 struct range_list *locked_lines = NULL;
982 struct range_list *unlocked_lines = NULL;
983 int locked_buckets[NUM_BUCKETS] = {};
984 int unlocked_buckets[NUM_BUCKETS] = {};
985 struct stree *stree, *orig;
986 struct sm_state *return_sm;
987 struct sm_state *sm;
988 sval_t line = sval_type_val(&int_ctype, 0);
989 int bucket;
990 int i;
992 if (sym_in_lock_table(cur_func_sym))
993 return;
995 FOR_EACH_PTR(get_all_return_strees(), stree) {
996 orig = __swap_cur_stree(stree);
998 if (is_impossible_path())
999 goto swap_stree;
1001 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
1002 if (!return_sm)
1003 goto swap_stree;
1004 line.value = return_sm->line;
1006 sm = get_sm_state(my_id, name, sym);
1007 if (!sm)
1008 goto swap_stree;
1010 if (parent_is_gone_var_sym(sm->name, sm->sym))
1011 goto swap_stree;
1013 if (sm->state != &locked &&
1014 sm->state != &unlocked &&
1015 sm->state != &restore)
1016 goto swap_stree;
1018 if ((sm->state == &unlocked || sm->state == &restore) &&
1019 is_EINTR(estate_rl(return_sm->state)))
1020 goto swap_stree;
1022 bucket = success_fail_positive(estate_rl(return_sm->state));
1023 if (sm->state == &locked) {
1024 add_range(&locked_lines, line, line);
1025 locked_buckets[bucket] = true;
1027 if (sm->state == &unlocked || sm->state == &restore) {
1028 add_range(&unlocked_lines, line, line);
1029 unlocked_buckets[bucket] = true;
1031 swap_stree:
1032 __swap_cur_stree(orig);
1033 } END_FOR_EACH_PTR(stree);
1036 if (!locked_lines || !unlocked_lines)
1037 return;
1039 for (i = 0; i < NUM_BUCKETS; i++) {
1040 if (locked_buckets[i] && unlocked_buckets[i])
1041 goto complain;
1043 if (locked_buckets[NEGATIVE] &&
1044 (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE]))
1045 goto complain;
1047 if (locked_buckets[ERR_PTR])
1048 goto complain;
1050 return;
1052 complain:
1053 if (common_false_positive(name))
1054 return;
1056 sm_msg("warn: inconsistent returns '%s'.", name);
1057 sm_printf(" Locked on : %s\n", show_rl(locked_lines));
1058 sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines));
1061 static void match_func_end(struct symbol *sym)
1063 struct tracker *tracker;
1065 FOR_EACH_PTR(locks, tracker) {
1066 check_lock(tracker->name, tracker->sym);
1067 } END_FOR_EACH_PTR(tracker);
1070 static void db_param_locked_unlocked(struct expression *expr, int param, const char *key, int lock_unlock, struct lock_info *info)
1072 struct expression *call, *arg;
1073 char *name;
1074 struct symbol *sym;
1076 if (info && info->action == IGNORE_LOCK)
1077 return;
1079 call = expr;
1080 while (call->type == EXPR_ASSIGNMENT)
1081 call = strip_expr(call->right);
1082 if (call->type != EXPR_CALL)
1083 return;
1085 if (!info && func_in_lock_table(call->fn))
1086 return;
1088 if (param == -2) {
1089 if (!info)
1090 name = use_best_match(key, lock_unlock, &sym);
1091 else {
1092 name = alloc_string(info->key);
1093 sym = NULL;
1095 } else if (param == -1) {
1096 if (expr->type != EXPR_ASSIGNMENT)
1097 return;
1098 ignored_reset = expr->left;
1100 name = get_variable_from_key(expr->left, key, &sym);
1101 } else {
1102 arg = get_argument_from_call_expr(call->args, param);
1103 if (!arg)
1104 return;
1106 arg = remove_spinlock_check(arg);
1107 name = get_variable_from_key(arg, key, &sym);
1110 if (!name || !sym)
1111 goto free;
1113 if (lock_unlock == LOCK)
1114 do_lock(name, sym, info);
1115 else if (lock_unlock == UNLOCK)
1116 do_unlock(name, sym, info);
1117 else if (lock_unlock == RESTORE)
1118 do_restore(name, sym, info);
1120 free:
1121 free_string(name);
1124 static void db_param_locked(struct expression *expr, int param, char *key, char *value)
1126 db_param_locked_unlocked(expr, param, key, LOCK, NULL);
1129 static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
1131 db_param_locked_unlocked(expr, param, key, UNLOCK, NULL);
1134 static void db_param_restore(struct expression *expr, int param, char *key, char *value)
1136 db_param_locked_unlocked(expr, param, key, RESTORE, NULL);
1139 static void match_lock_unlock(const char *fn, struct expression *expr, void *data)
1141 struct lock_info *info = data;
1142 struct expression *parent;
1144 if (info->arg == -1) {
1145 parent = expr_get_parent_expr(expr);
1146 while (parent && parent->type != EXPR_ASSIGNMENT)
1147 parent = expr_get_parent_expr(parent);
1148 if (!parent || parent->type != EXPR_ASSIGNMENT)
1149 return;
1150 expr = parent;
1153 db_param_locked_unlocked(expr, info->arg, info->key, info->action, info);
1156 static void match_lock_held(const char *fn, struct expression *call_expr,
1157 struct expression *assign_expr, void *data)
1159 struct lock_info *info = data;
1161 db_param_locked_unlocked(assign_expr ?: call_expr, info->arg, info->key, info->action, info);
1164 static void match_assign(struct expression *expr)
1166 struct smatch_state *state;
1168 /* This is only for the DB */
1169 if (is_fake_var_assign(expr))
1170 return;
1171 state = get_state_expr(my_id, expr->right);
1172 if (!state)
1173 return;
1174 set_state_expr(my_id, expr->left, state);
1177 static struct stree *printed;
1178 static void call_info_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
1180 int locked_type = 0;
1182 if (sm->state == &locked)
1183 locked_type = LOCK;
1184 else if (sm->state == &unlocked)
1185 locked_type = UNLOCK;
1186 else if (slist_has_state(sm->possible, &locked) ||
1187 slist_has_state(sm->possible, &half_locked))
1188 locked_type = HALF_LOCKED;
1189 else
1190 return;
1192 avl_insert(&printed, sm);
1193 sql_insert_caller_info(call, locked_type, param, printed_name, "");
1196 static void match_call_info(struct expression *expr)
1198 struct sm_state *sm;
1199 const char *name;
1200 int locked_type;
1202 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
1203 if (sm->state == &locked)
1204 locked_type = LOCK;
1205 else if (sm->state == &half_locked ||
1206 slist_has_state(sm->possible, &locked))
1207 locked_type = HALF_LOCKED;
1208 else
1209 continue;
1211 if (avl_lookup(printed, sm))
1212 continue;
1214 if (strcmp(sm->name, "bottom_half") == 0)
1215 name = "bh";
1216 else if (strcmp(sm->name, "rcu_read") == 0)
1217 name = "rcu_read_lock";
1218 else
1219 name = sm->name;
1221 if (strncmp(name, "__fake_param", 12) == 0 ||
1222 strchr(name, '$'))
1223 continue;
1225 sql_insert_caller_info(expr, locked_type, -2, name, "");
1226 } END_FOR_EACH_SM(sm);
1227 free_stree(&printed);
1230 static void set_locked(const char *name, struct symbol *sym, char *value)
1232 set_state(my_id, name, sym, &locked);
1235 static void set_half_locked(const char *name, struct symbol *sym, char *value)
1237 set_state(my_id, name, sym, &half_locked);
1240 static void set_unlocked(const char *name, struct symbol *sym, char *value)
1242 set_state(my_id, name, sym, &unlocked);
1245 static void match_after_func(struct symbol *sym)
1247 free_stree(&start_states);
1250 static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
1251 struct expression *assign_expr, void *_index)
1253 struct expression *lock, *ctx;
1254 char *lock_name;
1255 struct symbol *sym;
1257 lock = get_argument_from_call_expr(call_expr->args, 0);
1258 ctx = get_argument_from_call_expr(call_expr->args, 1);
1259 if (!expr_is_zero(ctx))
1260 return;
1262 lock_name = lock_to_name_sym(lock, &sym);
1263 if (!lock_name || !sym)
1264 goto free;
1265 do_lock(lock_name, sym, NULL);
1266 free:
1267 free_string(lock_name);
1270 /* print_held_locks() is used in check_call_tree.c */
1271 void print_held_locks(void)
1273 struct stree *stree;
1274 struct sm_state *sm;
1275 int i = 0;
1277 stree = __get_cur_stree();
1278 FOR_EACH_MY_SM(my_id, stree, sm) {
1279 if (sm->state != &locked)
1280 continue;
1281 if (i++)
1282 sm_printf(" ");
1283 sm_printf("'%s'", sm->name);
1284 } END_FOR_EACH_SM(sm);
1287 static void load_table(struct lock_info *lock_table)
1289 int i;
1291 for (i = 0; lock_table[i].function != NULL; i++) {
1292 struct lock_info *lock = &lock_table[i];
1294 if (lock->call_back) {
1295 add_function_hook(lock->function, lock->call_back, lock);
1296 } else if (lock->implies_start) {
1297 return_implies_state_sval(lock->function,
1298 *lock->implies_start,
1299 *lock->implies_end,
1300 &match_lock_held, lock);
1301 } else {
1302 add_function_hook(lock->function, &match_lock_unlock, lock);
1307 static bool is_smp_config(void)
1309 struct ident *id;
1311 id = built_in_ident("CONFIG_SMP");
1312 return !!lookup_symbol(id, NS_MACRO);
1315 void check_locking(int id)
1317 my_id = id;
1319 if (option_project != PROJ_KERNEL)
1320 return;
1322 if (!is_smp_config())
1323 return;
1325 load_table(lock_table);
1327 set_dynamic_states(my_id);
1328 add_unmatched_state_hook(my_id, &unmatched_state);
1329 add_pre_merge_hook(my_id, &pre_merge_hook);
1330 add_merge_hook(my_id, &merge_func);
1331 add_modification_hook(my_id, &reset);
1333 add_hook(&match_assign, ASSIGNMENT_HOOK);
1334 add_hook(&match_func_end, END_FUNC_HOOK);
1336 add_hook(&match_after_func, AFTER_FUNC_HOOK);
1337 add_function_data((unsigned long *)&start_states);
1339 add_caller_info_callback(my_id, call_info_callback);
1340 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
1342 add_split_return_callback(match_return_info);
1343 select_return_states_hook(LOCK, &db_param_locked);
1344 select_return_states_hook(UNLOCK, &db_param_unlocked);
1345 select_return_states_hook(RESTORE, &db_param_restore);
1347 select_caller_name_sym(set_locked, LOCK);
1348 select_caller_name_sym(set_half_locked, HALF_LOCKED);
1349 select_caller_name_sym(set_unlocked, UNLOCK);
1351 return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);