sscanf_return: warn about propagating scanf() returns
[smatch.git] / check_locking.c
blob2135739f612d4d0dcdf48c5962b10a8512dfd6fb
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 sval_t zero_sval = { .type = &int_ctype };
82 static sval_t one_sval = { .type = &int_ctype, .value = 1 };
84 static struct lock_info lock_table[] = {
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 {"__spin_lock", LOCK, spin_lock, 0, "$"},
92 {"__spin_unlock", UNLOCK, spin_lock, 0, "$"},
93 {"__spin_lock_nested", LOCK, spin_lock, 0, "$"},
94 {"raw_spin_lock", LOCK, spin_lock, 0, "$"},
95 {"raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
96 {"_raw_spin_lock", LOCK, spin_lock, 0, "$"},
97 {"_raw_spin_lock_nested", LOCK, spin_lock, 0, "$"},
98 {"_raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
99 {"__raw_spin_lock", LOCK, spin_lock, 0, "$"},
100 {"__raw_spin_unlock", UNLOCK, spin_lock, 0, "$"},
102 {"spin_lock_irq", LOCK, spin_lock, 0, "$"},
103 {"spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
104 {"_spin_lock_irq", LOCK, spin_lock, 0, "$"},
105 {"_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
106 {"__spin_lock_irq", LOCK, spin_lock, 0, "$"},
107 {"__spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
108 {"_raw_spin_lock_irq", LOCK, spin_lock, 0, "$"},
109 {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
110 {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"},
111 {"spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
112 {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
113 {"_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
114 {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
115 {"__spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
116 {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
117 {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
118 {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
119 {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"},
120 {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"},
121 {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
122 {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
123 {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
124 {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"},
125 {"spin_lock_bh", LOCK, spin_lock, 0, "$"},
126 {"spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
127 {"_spin_lock_bh", LOCK, spin_lock, 0, "$"},
128 {"_spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
129 {"__spin_lock_bh", LOCK, spin_lock, 0, "$"},
130 {"__spin_unlock_bh", UNLOCK, spin_lock, 0, "$"},
132 {"spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
133 {"_spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
134 {"__spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
135 {"raw_spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
136 {"_raw_spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
137 {"spin_trylock_irq", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
138 {"spin_trylock_irqsave", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
139 {"spin_trylock_bh", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
140 {"_spin_trylock_bh", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
141 {"__spin_trylock_bh", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
142 {"__raw_spin_trylock", LOCK, spin_lock, 0, "$", &one_sval, &one_sval},
143 {"_atomic_dec_and_lock", LOCK, spin_lock, 1, "$", &one_sval, &one_sval},
145 {"read_lock", LOCK, read_lock, 0, "$"},
146 {"down_read", LOCK, read_lock, 0, "$"},
147 {"down_read_nested", LOCK, read_lock, 0, "$"},
148 {"down_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
149 {"down_read_killable", LOCK, read_lock, 0, "$", &zero_sval, &zero_sval},
150 {"up_read", UNLOCK, read_lock, 0, "$"},
151 {"read_unlock", UNLOCK, read_lock, 0, "$"},
152 {"_read_lock", LOCK, read_lock, 0, "$"},
153 {"_read_unlock", UNLOCK, read_lock, 0, "$"},
154 {"__read_lock", LOCK, read_lock, 0, "$"},
155 {"__read_unlock", UNLOCK, read_lock, 0, "$"},
156 {"_raw_read_lock", LOCK, read_lock, 0, "$"},
157 {"_raw_read_unlock", UNLOCK, read_lock, 0, "$"},
158 {"__raw_read_lock", LOCK, read_lock, 0, "$"},
159 {"__raw_read_unlock", UNLOCK, read_lock, 0, "$"},
160 {"read_lock_irq", LOCK, read_lock, 0, "$"},
161 {"read_unlock_irq" , UNLOCK, read_lock, 0, "$"},
162 {"_read_lock_irq", LOCK, read_lock, 0, "$"},
163 {"_read_unlock_irq", UNLOCK, read_lock, 0, "$"},
164 {"__read_lock_irq", LOCK, read_lock, 0, "$"},
165 {"__read_unlock_irq", UNLOCK, read_lock, 0, "$"},
166 {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, "$"},
167 {"_raw_read_lock_irq", LOCK, read_lock, 0, "$"},
168 {"_raw_read_lock_bh", LOCK, read_lock, 0, "$"},
169 {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
170 {"read_lock_irqsave", LOCK, read_lock, 0, "$"},
171 {"read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
172 {"_read_lock_irqsave", LOCK, read_lock, 0, "$"},
173 {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
174 {"__read_lock_irqsave", LOCK, read_lock, 0, "$"},
175 {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
176 {"read_lock_bh", LOCK, read_lock, 0, "$"},
177 {"read_unlock_bh", UNLOCK, read_lock, 0, "$"},
178 {"_read_lock_bh", LOCK, read_lock, 0, "$"},
179 {"_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
180 {"__read_lock_bh", LOCK, read_lock, 0, "$"},
181 {"__read_unlock_bh", UNLOCK, read_lock, 0, "$"},
182 {"__raw_read_lock_bh", LOCK, read_lock, 0, "$"},
183 {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"},
185 {"_raw_read_lock_irqsave", LOCK, read_lock, 0, "$"},
186 {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
187 {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"},
188 {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
189 {"_raw_spin_lock_bh", LOCK, read_lock, 0, "$"},
190 {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
191 {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, "$"},
192 {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, "$"},
193 {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
194 {"_raw_write_lock_irqsave", LOCK, write_lock, 0, "$"},
195 {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
196 {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
197 {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
198 {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
199 {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
201 {"generic__raw_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
202 {"read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
203 {"_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
204 {"raw_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
205 {"_raw_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
206 {"__raw_read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
207 {"__read_trylock", LOCK, read_lock, 0, "$", &one_sval, &one_sval},
209 {"write_lock", LOCK, write_lock, 0, "$"},
210 {"down_write", LOCK, write_lock, 0, "$"},
211 {"down_write_nested", LOCK, write_lock, 0, "$"},
212 {"up_write", UNLOCK, write_lock, 0, "$"},
213 {"write_unlock", UNLOCK, write_lock, 0, "$"},
214 {"_write_lock", LOCK, write_lock, 0, "$"},
215 {"_write_unlock", UNLOCK, write_lock, 0, "$"},
216 {"__write_lock", LOCK, write_lock, 0, "$"},
217 {"__write_unlock", UNLOCK, write_lock, 0, "$"},
218 {"write_lock_irq", LOCK, write_lock, 0, "$"},
219 {"write_unlock_irq", UNLOCK, write_lock, 0, "$"},
220 {"_write_lock_irq", LOCK, write_lock, 0, "$"},
221 {"_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
222 {"__write_lock_irq", LOCK, write_lock, 0, "$"},
223 {"__write_unlock_irq", UNLOCK, write_lock, 0, "$"},
224 {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"},
225 {"write_lock_irqsave", LOCK, write_lock, 0, "$"},
226 {"write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
227 {"_write_lock_irqsave", LOCK, write_lock, 0, "$"},
228 {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
229 {"__write_lock_irqsave", LOCK, write_lock, 0, "$"},
230 {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"},
231 {"write_lock_bh", LOCK, write_lock, 0, "$"},
232 {"write_unlock_bh", UNLOCK, write_lock, 0, "$"},
233 {"_write_lock_bh", LOCK, write_lock, 0, "$"},
234 {"_write_unlock_bh", UNLOCK, write_lock, 0, "$"},
235 {"__write_lock_bh", LOCK, write_lock, 0, "$"},
236 {"__write_unlock_bh", UNLOCK, write_lock, 0, "$"},
237 {"_raw_write_lock", LOCK, write_lock, 0, "$"},
238 {"__raw_write_lock", LOCK, write_lock, 0, "$"},
239 {"_raw_write_unlock", UNLOCK, write_lock, 0, "$"},
240 {"__raw_write_unlock", UNLOCK, write_lock, 0, "$"},
241 {"_raw_write_lock_bh", LOCK, write_lock, 0, "$"},
242 {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, "$"},
243 {"_raw_write_lock_irq", LOCK, write_lock, 0, "$"},
245 {"write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
246 {"_write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
247 {"raw_write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
248 {"_raw_write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
249 {"__write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
250 {"__raw_write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
251 {"down_write_trylock", LOCK, write_lock, 0, "$", &one_sval, &one_sval},
252 {"down_write_killable", LOCK, write_lock, 0, "$", &zero_sval, &zero_sval},
254 {"down", LOCK, sem, 0, "$"},
255 {"up", UNLOCK, sem, 0, "$"},
256 {"down_trylock", LOCK, sem, 0, "$", &zero_sval, &zero_sval},
257 {"down_timeout", LOCK, sem, 0, "$", &zero_sval, &zero_sval},
258 {"down_interruptible", LOCK, sem, 0, "$", &zero_sval, &zero_sval},
259 {"down_killable", LOCK, sem, 0, "$", &zero_sval, &zero_sval},
262 {"mutex_lock", LOCK, mutex, 0, "$"},
263 {"mutex_unlock", UNLOCK, mutex, 0, "$"},
264 {"mutex_destroy", RESTORE, mutex, 0, "$"},
265 {"mutex_lock_nested", LOCK, mutex, 0, "$"},
266 {"mutex_lock_io", LOCK, mutex, 0, "$"},
267 {"mutex_lock_io_nested", LOCK, mutex, 0, "$"},
269 {"mutex_lock_interruptible", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
270 {"mutex_lock_interruptible_nested", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
271 {"mutex_lock_killable", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
272 {"mutex_lock_killable_nested", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
274 {"mutex_trylock", LOCK, mutex, 0, "$", &one_sval, &one_sval},
276 {"ww_mutex_lock", LOCK, mutex, 0, "$"},
277 {"__ww_mutex_lock", LOCK, mutex, 0, "$"},
278 {"ww_mutex_lock_interruptible", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
279 {"ww_mutex_unlock", UNLOCK, mutex, 0, "$"},
281 {"raw_local_irq_disable", LOCK, irq, NO_ARG, "irq"},
282 {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, "irq"},
283 {"spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
284 {"spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
285 {"_spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
286 {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
287 {"__spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
288 {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
289 {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, "irq"},
290 {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
291 {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
292 {"spin_trylock_irq", LOCK, irq, NO_ARG, "irq", &one_sval, &one_sval},
293 {"read_lock_irq", LOCK, irq, NO_ARG, "irq"},
294 {"read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
295 {"_read_lock_irq", LOCK, irq, NO_ARG, "irq"},
296 {"_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
297 {"__read_lock_irq", LOCK, irq, NO_ARG, "irq"},
298 {"_raw_read_lock_irq", LOCK, irq, NO_ARG, "irq"},
299 {"__read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
300 {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
301 {"write_lock_irq", LOCK, irq, NO_ARG, "irq"},
302 {"write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
303 {"_write_lock_irq", LOCK, irq, NO_ARG, "irq"},
304 {"_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
305 {"__write_lock_irq", LOCK, irq, NO_ARG, "irq"},
306 {"__write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
307 {"_raw_write_lock_irq", LOCK, irq, NO_ARG, "irq"},
308 {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"},
310 {"arch_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
311 {"arch_local_irq_restore", RESTORE, irq, 0, "$"},
312 {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, "$"},
313 {"raw_local_irq_restore", RESTORE, irq, 0, "$"},
314 {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
315 {"spin_lock_irqsave", LOCK, irq, 1, "$"},
316 {"spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
317 {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
318 {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
319 {"_spin_lock_irqsave", LOCK, irq, 1, "$"},
320 {"_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
321 {"__spin_lock_irqsave_nested", LOCK, irq, 1, "$"},
322 {"__spin_lock_irqsave", LOCK, irq, 1, "$"},
323 {"__spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
324 {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
325 {"_raw_spin_lock_irqsave", LOCK, irq, 1, "$"},
326 {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
327 {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
328 {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"},
329 {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, "$"},
330 {"spin_trylock_irqsave", LOCK, irq, 1, "$", &one_sval, &one_sval},
331 {"read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
332 {"read_lock_irqsave", LOCK, irq, 1, "$"},
333 {"read_unlock_irqrestore", RESTORE, irq, 1, "$"},
334 {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
335 {"_read_lock_irqsave", LOCK, irq, 1, "$"},
336 {"_read_unlock_irqrestore", RESTORE, irq, 1, "$"},
337 {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
338 {"__read_unlock_irqrestore", RESTORE, irq, 1, "$"},
339 {"write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
340 {"write_lock_irqsave", LOCK, irq, 1, "$"},
341 {"write_unlock_irqrestore", RESTORE, irq, 1, "$"},
342 {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
343 {"_write_lock_irqsave", LOCK, irq, 1, "$"},
344 {"_write_unlock_irqrestore", RESTORE, irq, 1, "$"},
345 {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, "$"},
346 {"__write_unlock_irqrestore", RESTORE, irq, 1, "$"},
348 {"local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
349 {"_local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"},
350 {"__local_bh_disable", LOCK, 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 {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"},
354 {"spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
355 {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
356 {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
357 {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
358 {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
359 {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
360 {"read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
361 {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
362 {"_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
363 {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
364 {"__read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
365 {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
366 {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
367 {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
368 {"write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
369 {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
370 {"_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
371 {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
372 {"__write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
373 {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"},
374 {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"},
375 {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, "bh"},
376 {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &one_sval, &one_sval},
377 {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &one_sval, &one_sval},
378 {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &one_sval, &one_sval},
380 {"ffs_mutex_lock", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
382 {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, "clk"},
383 {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, "clk"},
384 {"clk_enable_lock", LOCK, enable_lock, -1, "$"},
385 {"clk_enable_unlock", UNLOCK, enable_lock, 0, "$"},
387 {"dma_resv_lock", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
388 {"dma_resv_trylock", LOCK, mutex, 0, "$", &one_sval, &one_sval},
389 {"dma_resv_lock_interruptible", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
390 {"dma_resv_unlock", UNLOCK, mutex, 0, "$"},
392 {"modeset_lock", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
393 {"drm_ modeset_lock", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
394 {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, "$", &zero_sval, &zero_sval},
395 {"modeset_unlock", UNLOCK, mutex, 0, "$"},
396 // {"nvkm_i2c_aux_acquire", LOCK, mutex,
397 // {"i915_gem_object_lock_interruptible", LOCK, mutex,
399 {"reiserfs_write_lock_nested", LOCK, mutex, 0, "$"},
400 {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, "$"},
402 {"rw_lock", LOCK, write_lock, 1, "$"},
403 {"rw_unlock", UNLOCK, write_lock, 1, "$"},
405 {"sem_lock", LOCK, mutex, 0, "$"},
406 {"sem_unlock", UNLOCK, mutex, 0, "$"},
408 {"rcu_lock_acquire", LOCK, rcu, NO_ARG, "rcu"},
409 {"rcu_lock_release", UNLOCK, rcu, NO_ARG, "rcu"},
411 {"rcu_read_lock", LOCK, rcu_read, NO_ARG, "rcu_read"},
412 {"rcu_read_unlock", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
413 {"rcu_read_lock_bh", LOCK, rcu_read, NO_ARG, "rcu_read"},
414 {"rcu_read_unlock_bh", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
416 {"rcu_read_lock_sched", LOCK, rcu_read, NO_ARG, "rcu_read"},
417 {"rcu_read_lock_sched_notrace", LOCK, rcu_read, NO_ARG, "rcu_read"},
418 {"rcu_read_unlock_sched", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
419 {"rcu_read_unlock_sched_notrace", UNLOCK, rcu_read, NO_ARG, "rcu_read"},
421 {"bch_write_bdev_super", IGNORE_LOCK, sem, 0, "&$->sb_write_mutex"},
422 {"dlfb_set_video_mode", IGNORE_LOCK, sem, 0, "&$->urbs.limit_sem"},
427 struct macro_info {
428 const char *macro;
429 int action;
430 int param;
433 static struct macro_info macro_table[] = {
434 {"genpd_lock", LOCK, 0},
435 {"genpd_lock_nested", LOCK, 0},
436 {"genpd_lock_interruptible", LOCK, 0},
437 {"genpd_unlock", UNLOCK, 0},
440 static const char *false_positives[][2] = {
441 {"fs/jffs2/", "->alloc_sem"},
442 {"fs/xfs/", "->b_sema"},
443 {"mm/", "pvmw->ptl"},
446 static struct stree *start_states;
448 static struct tracker_list *locks;
450 static struct expression *ignored_reset;
451 static void reset(struct sm_state *sm, struct expression *mod_expr)
453 struct expression *faked;
455 if (mod_expr && mod_expr->type == EXPR_ASSIGNMENT &&
456 mod_expr->left == ignored_reset)
457 return;
458 faked = get_faked_expression();
459 if (faked && faked->type == EXPR_ASSIGNMENT &&
460 faked->left == ignored_reset)
461 return;
463 set_state(my_id, sm->name, sm->sym, &start_state);
466 static struct smatch_state *get_start_state(struct sm_state *sm)
468 struct smatch_state *orig;
470 if (!sm)
471 return NULL;
473 orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
474 if (orig)
475 return orig;
476 return NULL;
479 static struct expression *remove_spinlock_check(struct expression *expr)
481 if (expr->type != EXPR_CALL)
482 return expr;
483 if (expr->fn->type != EXPR_SYMBOL)
484 return expr;
485 if (strcmp(expr->fn->symbol_name->name, "spinlock_check"))
486 return expr;
487 expr = get_argument_from_call_expr(expr->args, 0);
488 return expr;
491 static struct expression *filter_kernel_args(struct expression *arg)
493 if (arg->type == EXPR_PREOP && arg->op == '&')
494 return strip_expr(arg->unop);
495 if (!is_pointer(arg))
496 return arg;
497 return deref_expression(strip_expr(arg));
500 static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
502 expr = remove_spinlock_check(expr);
503 expr = filter_kernel_args(expr);
504 return expr_to_str_sym(expr, sym);
507 static struct smatch_state *unmatched_state(struct sm_state *sm)
509 return &start_state;
512 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
514 if (is_impossible_path())
515 set_state(my_id, cur->name, cur->sym, &impossible);
518 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
520 if (s1 == &impossible)
521 return s2;
522 if (s2 == &impossible)
523 return s1;
524 return &merged;
527 static struct sm_state *get_best_match(const char *key, int lock_unlock)
529 struct sm_state *sm;
530 struct sm_state *match;
531 int cnt = 0;
532 int start_pos, state_len, key_len, chunks, i;
534 if (strncmp(key, "$->", 3) == 0)
535 key += 3;
537 key_len = strlen(key);
538 chunks = 0;
539 for (i = key_len - 1; i > 0; i--) {
540 if (key[i] == '>' || key[i] == '.')
541 chunks++;
542 if (chunks == 2) {
543 key += (i + 1);
544 key_len = strlen(key);
545 break;
549 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
550 if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
551 sm->state != &locked) ||
552 (lock_unlock == LOCK && sm->state != &unlocked))
553 continue;
554 state_len = strlen(sm->name);
555 if (state_len < key_len)
556 continue;
557 start_pos = state_len - key_len;
558 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
559 strcmp(sm->name + start_pos, key) == 0) {
560 cnt++;
561 match = sm;
563 } END_FOR_EACH_SM(sm);
565 if (cnt == 1)
566 return match;
567 return NULL;
570 static char *use_best_match(const char *key, int lock_unlock, struct symbol **sym)
572 struct sm_state *match;
574 match = get_best_match(key, lock_unlock);
575 if (!match) {
576 *sym = NULL;
577 return alloc_string(key);
579 *sym = match->sym;
580 return alloc_string(match->name);
583 static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
585 struct smatch_state *orig;
587 orig = get_state_stree(start_states, my_id, name, sym);
588 if (!orig)
589 set_state_stree(&start_states, my_id, name, sym, start);
590 else if (orig != start)
591 set_state_stree(&start_states, my_id, name, sym, &undefined);
594 static bool common_false_positive(const char *name)
596 const char *path, *lname;
597 int i, len_total, len_path, len_name, skip;
599 if (!get_filename())
600 return false;
602 len_total = strlen(name);
603 for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
604 path = false_positives[i][0];
605 lname = false_positives[i][1];
607 len_path = strlen(path);
608 len_name = strlen(lname);
610 if (len_name > len_total)
611 continue;
612 skip = len_total - len_name;
614 if (strncmp(get_filename(), path, len_path) == 0 &&
615 strcmp(name + skip, lname) == 0)
616 return true;
619 return false;
622 static bool sm_in_start_states(struct sm_state *sm)
624 if (!sm || !cur_func_sym)
625 return false;
626 if (sm->line == cur_func_sym->pos.line)
627 return true;
628 return false;
631 static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
633 struct sm_state *tmp;
635 if (!sm)
636 return;
638 FOR_EACH_PTR(sm->possible, tmp) {
639 if (tmp->state == state)
640 goto found;
641 } END_FOR_EACH_PTR(tmp);
643 return;
644 found:
645 // FIXME: called with read_lock held
646 // drivers/scsi/aic7xxx/aic7xxx_osm.c:1591 ahc_linux_isr() error: double locked 'flags' (orig line 1584)
647 if (strcmp(sm->name, "bottom_half") == 0)
648 return;
649 if (strstr(sm->name, "rcu"))
650 return;
651 if (common_false_positive(sm->name))
652 return;
654 if (state == &locked && sm_in_start_states(tmp)) {
655 // sm_warning("called with lock held. '%s'", sm->name);
656 } else {
657 // sm_msg("error: double %s '%s' (orig line %u)",
658 // state->name, sm->name, tmp->line);
662 static bool handle_macro_lock_unlock(void)
664 struct expression *expr, *arg;
665 struct macro_info *info;
666 struct sm_state *sm;
667 struct symbol *sym;
668 const char *macro;
669 char *name;
670 bool ret = false;
671 int i;
673 expr = last_ptr_list((struct ptr_list *)big_expression_stack);
674 while (expr && expr->type == EXPR_ASSIGNMENT)
675 expr = strip_expr(expr->right);
676 if (!expr || expr->type != EXPR_CALL)
677 return false;
679 macro = get_macro_name(expr->pos);
680 if (!macro)
681 return false;
683 for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
684 info = &macro_table[i];
686 if (strcmp(macro, info->macro) != 0)
687 continue;
688 arg = get_argument_from_call_expr(expr->args, info->param);
689 name = expr_to_str_sym(arg, &sym);
690 if (!name || !sym)
691 goto free;
692 sm = get_sm_state(my_id, name, sym);
694 if (info->action == LOCK) {
695 if (!get_start_state(sm))
696 set_start_state(name, sym, &unlocked);
697 if (sm && sm->line != expr->pos.line)
698 warn_on_double(sm, &locked);
699 set_state(my_id, name, sym, &locked);
700 } else {
701 if (!get_start_state(sm))
702 set_start_state(name, sym, &locked);
703 if (sm && sm->line != expr->pos.line)
704 warn_on_double(sm, &unlocked);
705 set_state(my_id, name, sym, &unlocked);
707 ret = true;
708 free:
709 free_string(name);
710 return ret;
712 return false;
715 static bool is_local_IRQ_save(const char *name, struct symbol *sym, struct lock_info *info)
717 if (name && strcmp(name, "flags") == 0)
718 return true;
719 if (!sym)
720 return false;
721 if (!sym->ident || strcmp(sym->ident->name, name) != 0)
722 return false;
723 if (!info)
724 return false;
725 return strstr(info->function, "irq") && strstr(info->function, "save");
728 static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
730 struct sm_state *sm;
731 bool delete_null = false;
733 if (!info && handle_macro_lock_unlock())
734 return;
736 add_tracker(&locks, my_id, name, sym);
738 sm = get_sm_state(my_id, name, sym);
739 if (!get_start_state(sm))
740 set_start_state(name, sym, &unlocked);
741 if (!sm && !is_local_IRQ_save(name, sym, info) && sym) {
742 sm = get_best_match(name, LOCK);
743 if (sm) {
744 name = sm->name;
745 if (sm->sym)
746 sym = sm->sym;
747 else
748 delete_null = true;
751 warn_on_double(sm, &locked);
752 if (delete_null)
753 set_state(my_id, name, NULL, &ignore);
755 set_state(my_id, name, sym, &locked);
758 static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
760 struct sm_state *sm;
761 bool delete_null = false;
763 if (__path_is_null())
764 return;
766 if (!info && handle_macro_lock_unlock())
767 return;
769 add_tracker(&locks, my_id, name, sym);
770 sm = get_sm_state(my_id, name, sym);
771 if (!sm && !info && !is_local_IRQ_save(name, sym, info)) {
772 sm = get_best_match(name, UNLOCK);
773 if (sm) {
774 name = sm->name;
775 if (sm->sym)
776 sym = sm->sym;
777 else
778 delete_null = true;
781 if (!get_start_state(sm))
782 set_start_state(name, sym, &locked);
783 warn_on_double(sm, &unlocked);
784 if (delete_null)
785 set_state(my_id, name, NULL, &ignore);
786 set_state(my_id, name, sym, &unlocked);
789 static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
791 struct sm_state *sm;
793 if (__path_is_null())
794 return;
796 sm = get_sm_state(my_id, name, sym);
797 if (!get_start_state(sm))
798 set_start_state(name, sym, &locked);
800 add_tracker(&locks, my_id, name, sym);
801 set_state(my_id, name, sym, &restore);
804 static int get_db_type(struct sm_state *sm)
807 * Bottom half is complicated because it's nestable.
808 * Say it's merged at the start and we lock and unlock then
809 * it should go back to merged.
811 if (sm->state == get_start_state(sm)) {
812 if (sm->state == &locked)
813 return KNOWN_LOCKED;
814 if (sm->state == &unlocked)
815 return KNOWN_UNLOCKED;
818 if (sm->state == &locked)
819 return LOCK;
820 if (sm->state == &unlocked)
821 return UNLOCK;
822 if (sm->state == &restore)
823 return RESTORE;
824 return LOCK;
827 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
829 struct smatch_state *start;
830 struct sm_state *sm;
831 const char *param_name;
832 int param;
834 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
835 if (sm->state != &locked &&
836 sm->state != &unlocked &&
837 sm->state != &restore)
838 continue;
839 if (sm->name[0] == '$')
840 continue;
843 * If the state is locked at the end, that doesn't mean
844 * anything. It could be been locked at the start. Or
845 * it could be &merged at the start but its locked now
846 * because of implications and not because we set the
847 * state.
849 * This is slightly a hack, but when we change the state, we
850 * call set_start_state() so if get_start_state() returns NULL
851 * that means we haven't manually the locked state.
853 start = get_start_state(sm);
854 if (sm->state == &restore) {
855 if (start != &locked)
856 continue;
857 } else if (!start || sm->state == start)
858 continue; /* !start means it was passed in */
860 param = get_param_key_from_sm(sm, expr, &param_name);
861 sql_insert_return_states(return_id, return_ranges,
862 get_db_type(sm),
863 param, param_name, "");
864 } END_FOR_EACH_SM(sm);
867 static int sm_both_locked_and_unlocked(struct sm_state *sm)
869 int is_locked = 0;
870 int is_unlocked = 0;
871 struct sm_state *tmp;
873 if (sm->state != &merged)
874 return 0;
876 FOR_EACH_PTR(sm->possible, tmp) {
877 if (tmp->state == &locked)
878 is_locked = 1;
879 if (tmp->state == &unlocked)
880 is_unlocked = 1;
881 } END_FOR_EACH_PTR(tmp);
883 return is_locked && is_unlocked;
886 enum {
887 ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS,
890 static bool is_EINTR(struct range_list *rl)
892 sval_t sval;
894 if (!rl_to_sval(rl, &sval))
895 return false;
896 return sval.value == -4;
899 static int success_fail_positive(struct range_list *rl)
901 /* void returns are the same as success (zero in the kernel) */
902 if (!rl)
903 return ZERO;
905 if (rl_type(rl)->type != SYM_PTR &&
906 !is_whole_rl(rl) &&
907 sval_is_negative(rl_min(rl)))
908 return NEGATIVE;
910 if (rl_min(rl).value == 0 && rl_max(rl).value == 0)
911 return ZERO;
913 if (is_err_ptr(rl_min(rl)) &&
914 is_err_ptr(rl_max(rl)))
915 return ERR_PTR;
918 * Trying to match ERR_PTR(ret) but without the expression struct.
919 * Ugly...
921 if (type_bits(&long_ctype) == 64 &&
922 rl_type(rl)->type == SYM_PTR &&
923 rl_min(rl).value == INT_MIN)
924 return ERR_PTR;
926 return POSITIVE;
929 static bool sym_in_lock_table(struct symbol *sym)
931 int i;
933 if (!sym || !sym->ident)
934 return false;
936 for (i = 0; lock_table[i].function != NULL; i++) {
937 if (strcmp(lock_table[i].function, sym->ident->name) == 0)
938 return true;
940 return false;
943 static bool func_in_lock_table(struct expression *expr)
945 if (expr->type != EXPR_SYMBOL)
946 return false;
947 return sym_in_lock_table(expr->symbol);
950 static void check_lock(char *name, struct symbol *sym)
952 struct range_list *locked_lines = NULL;
953 struct range_list *unlocked_lines = NULL;
954 int locked_buckets[NUM_BUCKETS] = {};
955 int unlocked_buckets[NUM_BUCKETS] = {};
956 struct stree *stree, *orig;
957 struct sm_state *return_sm;
958 struct sm_state *sm;
959 sval_t line = sval_type_val(&int_ctype, 0);
960 int bucket;
961 int i;
963 if (sym_in_lock_table(cur_func_sym))
964 return;
966 FOR_EACH_PTR(get_all_return_strees(), stree) {
967 orig = __swap_cur_stree(stree);
969 if (is_impossible_path())
970 goto swap_stree;
972 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
973 if (!return_sm)
974 goto swap_stree;
975 line.value = return_sm->line;
977 sm = get_sm_state(my_id, name, sym);
978 if (!sm)
979 goto swap_stree;
981 if (parent_is_gone_var_sym(sm->name, sm->sym))
982 goto swap_stree;
984 if (sm->state != &locked &&
985 sm->state != &unlocked &&
986 sm->state != &restore)
987 goto swap_stree;
989 if ((sm->state == &unlocked || sm->state == &restore) &&
990 is_EINTR(estate_rl(return_sm->state)))
991 goto swap_stree;
993 bucket = success_fail_positive(estate_rl(return_sm->state));
994 if (sm->state == &locked) {
995 add_range(&locked_lines, line, line);
996 locked_buckets[bucket] = true;
998 if (sm->state == &unlocked || sm->state == &restore) {
999 add_range(&unlocked_lines, line, line);
1000 unlocked_buckets[bucket] = true;
1002 swap_stree:
1003 __swap_cur_stree(orig);
1004 } END_FOR_EACH_PTR(stree);
1007 if (!locked_lines || !unlocked_lines)
1008 return;
1010 for (i = 0; i < NUM_BUCKETS; i++) {
1011 if (locked_buckets[i] && unlocked_buckets[i])
1012 goto complain;
1014 if (locked_buckets[NEGATIVE] &&
1015 (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE]))
1016 goto complain;
1018 if (locked_buckets[ERR_PTR])
1019 goto complain;
1021 return;
1023 complain:
1024 sm_msg("warn: inconsistent returns '%s'.", name);
1025 sm_printf(" Locked on : %s\n", show_rl(locked_lines));
1026 sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines));
1029 static void match_func_end(struct symbol *sym)
1031 struct tracker *tracker;
1033 FOR_EACH_PTR(locks, tracker) {
1034 check_lock(tracker->name, tracker->sym);
1035 } END_FOR_EACH_PTR(tracker);
1038 static void db_param_locked_unlocked(struct expression *expr, int param, const char *key, int lock_unlock, struct lock_info *info)
1040 struct expression *call, *arg;
1041 char *name;
1042 struct symbol *sym;
1044 if (info && info->action == IGNORE_LOCK)
1045 return;
1047 call = expr;
1048 while (call->type == EXPR_ASSIGNMENT)
1049 call = strip_expr(call->right);
1050 if (call->type != EXPR_CALL)
1051 return;
1053 if (!info && func_in_lock_table(call->fn))
1054 return;
1056 if (param == -2) {
1057 if (!info)
1058 name = use_best_match(key, lock_unlock, &sym);
1059 else {
1060 name = alloc_string(info->key);
1061 sym = NULL;
1063 } else if (param == -1) {
1064 if (expr->type != EXPR_ASSIGNMENT)
1065 return;
1066 ignored_reset = expr->left;
1068 name = get_variable_from_key(expr->left, key, &sym);
1069 if (!name || !sym)
1070 goto free;
1071 } else {
1072 arg = get_argument_from_call_expr(call->args, param);
1073 if (!arg)
1074 return;
1076 arg = remove_spinlock_check(arg);
1077 name = get_variable_from_key(arg, key, &sym);
1078 if (!name || !sym)
1079 goto free;
1082 if (!name || !sym)
1083 goto free;
1085 if (lock_unlock == LOCK)
1086 do_lock(name, sym, info);
1087 else if (lock_unlock == UNLOCK)
1088 do_unlock(name, sym, info);
1089 else if (lock_unlock == RESTORE)
1090 do_restore(name, sym, info);
1092 free:
1093 free_string(name);
1096 static void db_param_locked(struct expression *expr, int param, char *key, char *value)
1098 db_param_locked_unlocked(expr, param, key, LOCK, NULL);
1101 static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
1103 db_param_locked_unlocked(expr, param, key, UNLOCK, NULL);
1106 static void db_param_restore(struct expression *expr, int param, char *key, char *value)
1108 db_param_locked_unlocked(expr, param, key, RESTORE, NULL);
1111 static void match_lock_unlock(const char *fn, struct expression *expr, void *data)
1113 struct lock_info *info = data;
1114 struct expression *parent;
1116 if (info->arg == -1) {
1117 parent = expr_get_parent_expr(expr);
1118 while (parent && parent->type != EXPR_ASSIGNMENT)
1119 parent = expr_get_parent_expr(parent);
1120 if (!parent || parent->type != EXPR_ASSIGNMENT)
1121 return;
1122 expr = parent;
1125 db_param_locked_unlocked(expr, info->arg, info->key, info->action, info);
1128 static void match_lock_held(const char *fn, struct expression *call_expr,
1129 struct expression *assign_expr, void *data)
1131 struct lock_info *info = data;
1133 db_param_locked_unlocked(assign_expr ?: call_expr, info->arg, info->key, info->action, info);
1136 static void match_assign(struct expression *expr)
1138 struct smatch_state *state;
1140 /* This is only for the DB */
1141 if (!__in_fake_var_assign)
1142 return;
1143 state = get_state_expr(my_id, expr->right);
1144 if (!state)
1145 return;
1146 set_state_expr(my_id, expr->left, state);
1149 static struct stree *printed;
1150 static void call_info_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
1152 int locked_type = 0;
1154 if (sm->state == &locked)
1155 locked_type = LOCK;
1156 else if (sm->state == &unlocked)
1157 locked_type = UNLOCK;
1158 else if (slist_has_state(sm->possible, &locked) ||
1159 slist_has_state(sm->possible, &half_locked))
1160 locked_type = HALF_LOCKED;
1161 else
1162 return;
1164 avl_insert(&printed, sm);
1165 sql_insert_caller_info(call, locked_type, param, printed_name, "");
1168 static void match_call_info(struct expression *expr)
1170 struct sm_state *sm;
1171 const char *name;
1172 int locked_type;
1174 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
1175 if (sm->state == &locked)
1176 locked_type = LOCK;
1177 else if (sm->state == &half_locked ||
1178 slist_has_state(sm->possible, &locked))
1179 locked_type = HALF_LOCKED;
1180 else
1181 continue;
1183 if (avl_lookup(printed, sm))
1184 continue;
1186 if (strcmp(sm->name, "bottom_half") == 0)
1187 name = "bh";
1188 else if (strcmp(sm->name, "rcu_read") == 0)
1189 name = "rcu_read_lock";
1190 else
1191 name = sm->name;
1193 sql_insert_caller_info(expr, locked_type, -2, name, "");
1194 } END_FOR_EACH_SM(sm);
1195 free_stree(&printed);
1198 static void set_locked_called_state(const char *name, struct symbol *sym,
1199 char *key, char *value,
1200 struct smatch_state *state)
1202 char fullname[256];
1204 if (name && key[0] == '$')
1205 snprintf(fullname, sizeof(fullname), "%s%s", name, key + 1);
1206 else
1207 snprintf(fullname, sizeof(fullname), "%s", key);
1209 if (strstr(fullname, ">>") || strstr(fullname, "..")) {
1210 // sm_msg("warn: invalid lock name. name = '%s' key = '%s'", name, key);
1211 return;
1214 set_state(my_id, fullname, sym, state);
1217 static void set_locked(const char *name, struct symbol *sym, char *key, char *value)
1219 set_locked_called_state(name, sym, key, value, &locked);
1222 static void set_half_locked(const char *name, struct symbol *sym, char *key, char *value)
1224 set_locked_called_state(name, sym, key, value, &half_locked);
1227 static void set_unlocked(const char *name, struct symbol *sym, char *key, char *value)
1229 set_locked_called_state(name, sym, key, value, &unlocked);
1232 static void match_after_func(struct symbol *sym)
1234 free_stree(&start_states);
1237 static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
1238 struct expression *assign_expr, void *_index)
1240 struct expression *lock, *ctx;
1241 char *lock_name;
1242 struct symbol *sym;
1244 lock = get_argument_from_call_expr(call_expr->args, 0);
1245 ctx = get_argument_from_call_expr(call_expr->args, 1);
1246 if (!expr_is_zero(ctx))
1247 return;
1249 lock_name = lock_to_name_sym(lock, &sym);
1250 if (!lock_name || !sym)
1251 goto free;
1252 do_lock(lock_name, sym, NULL);
1253 free:
1254 free_string(lock_name);
1257 /* print_held_locks() is used in check_call_tree.c */
1258 void print_held_locks(void)
1260 struct stree *stree;
1261 struct sm_state *sm;
1262 int i = 0;
1264 stree = __get_cur_stree();
1265 FOR_EACH_MY_SM(my_id, stree, sm) {
1266 if (sm->state != &locked)
1267 continue;
1268 if (i++)
1269 sm_printf(" ");
1270 sm_printf("'%s'", sm->name);
1271 } END_FOR_EACH_SM(sm);
1274 static void load_table(struct lock_info *lock_table)
1276 int i;
1278 for (i = 0; lock_table[i].function != NULL; i++) {
1279 struct lock_info *lock = &lock_table[i];
1281 if (lock->call_back) {
1282 add_function_hook(lock->function, lock->call_back, lock);
1283 } else if (lock->implies_start) {
1284 return_implies_state_sval(lock->function,
1285 *lock->implies_start,
1286 *lock->implies_end,
1287 &match_lock_held, lock);
1288 } else {
1289 add_function_hook(lock->function, &match_lock_unlock, lock);
1294 static bool is_smp_config(void)
1296 struct ident *id;
1298 id = built_in_ident("CONFIG_SMP");
1299 return !!lookup_symbol(id, NS_MACRO);
1302 void check_locking(int id)
1304 my_id = id;
1306 if (option_project != PROJ_KERNEL)
1307 return;
1309 if (!is_smp_config())
1310 return;
1312 load_table(lock_table);
1314 set_dynamic_states(my_id);
1315 add_unmatched_state_hook(my_id, &unmatched_state);
1316 add_pre_merge_hook(my_id, &pre_merge_hook);
1317 add_merge_hook(my_id, &merge_func);
1318 add_modification_hook(my_id, &reset);
1320 add_hook(&match_assign, ASSIGNMENT_HOOK);
1321 add_hook(&match_func_end, END_FUNC_HOOK);
1323 add_hook(&match_after_func, AFTER_FUNC_HOOK);
1324 add_function_data((unsigned long *)&start_states);
1326 add_caller_info_callback(my_id, call_info_callback);
1327 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
1329 add_split_return_callback(match_return_info);
1330 select_return_states_hook(LOCK, &db_param_locked);
1331 select_return_states_hook(UNLOCK, &db_param_unlocked);
1332 select_return_states_hook(RESTORE, &db_param_restore);
1334 select_caller_info_hook(set_locked, LOCK);
1335 select_caller_info_hook(set_half_locked, HALF_LOCKED);
1336 select_caller_info_hook(set_unlocked, UNLOCK);
1338 return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);