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
22 #include "smatch_extra.h"
23 #include "smatch_slist.h"
54 const char *get_lock_name(enum lock_type type
)
56 static const char *names
[] = {
57 [spin_lock
] = "spin_lock",
58 [read_lock
] = "read_lock",
59 [write_lock
] = "write_lock",
61 [bottom_half
] = "bottom_half",
64 [prepare_lock
] = "prepare_lock",
65 [enable_lock
] = "enable_lock",
67 [rcu_read
] = "rcu_read",
90 enum return_type return_type
;
93 static struct lock_info lock_table
[] = {
94 {"spin_lock", LOCK
, spin_lock
, 0, ret_any
},
95 {"spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
96 {"spin_lock_nested", LOCK
, spin_lock
, 0, ret_any
},
97 {"_spin_lock", LOCK
, spin_lock
, 0, ret_any
},
98 {"_spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
99 {"_spin_lock_nested", LOCK
, spin_lock
, 0, ret_any
},
100 {"__spin_lock", LOCK
, spin_lock
, 0, ret_any
},
101 {"__spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
102 {"__spin_lock_nested", LOCK
, spin_lock
, 0, ret_any
},
103 {"raw_spin_lock", LOCK
, spin_lock
, 0, ret_any
},
104 {"raw_spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
105 {"_raw_spin_lock", LOCK
, spin_lock
, 0, ret_any
},
106 {"_raw_spin_lock_nested", LOCK
, spin_lock
, 0, ret_any
},
107 {"_raw_spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
108 {"__raw_spin_lock", LOCK
, spin_lock
, 0, ret_any
},
109 {"__raw_spin_unlock", UNLOCK
, spin_lock
, 0, ret_any
},
111 {"spin_lock_irq", LOCK
, spin_lock
, 0, ret_any
},
112 {"spin_unlock_irq", UNLOCK
, spin_lock
, 0, ret_any
},
113 {"_spin_lock_irq", LOCK
, spin_lock
, 0, ret_any
},
114 {"_spin_unlock_irq", UNLOCK
, spin_lock
, 0, ret_any
},
115 {"__spin_lock_irq", LOCK
, spin_lock
, 0, ret_any
},
116 {"__spin_unlock_irq", UNLOCK
, spin_lock
, 0, ret_any
},
117 {"_raw_spin_lock_irq", LOCK
, spin_lock
, 0, ret_any
},
118 {"_raw_spin_unlock_irq", UNLOCK
, spin_lock
, 0, ret_any
},
119 {"__raw_spin_unlock_irq", UNLOCK
, spin_lock
, 0, ret_any
},
120 {"spin_lock_irqsave", LOCK
, spin_lock
, 0, ret_any
},
121 {"spin_unlock_irqrestore", UNLOCK
, spin_lock
, 0, ret_any
},
122 {"_spin_lock_irqsave", LOCK
, spin_lock
, 0, ret_any
},
123 {"_spin_unlock_irqrestore", UNLOCK
, spin_lock
, 0, ret_any
},
124 {"__spin_lock_irqsave", LOCK
, spin_lock
, 0, ret_any
},
125 {"__spin_unlock_irqrestore", UNLOCK
, spin_lock
, 0, ret_any
},
126 {"_raw_spin_lock_irqsave", LOCK
, spin_lock
, 0, ret_any
},
127 {"_raw_spin_unlock_irqrestore", UNLOCK
, spin_lock
, 0, ret_any
},
128 {"__raw_spin_lock_irqsave", LOCK
, spin_lock
, 0, ret_any
},
129 {"__raw_spin_unlock_irqrestore", UNLOCK
, spin_lock
, 0, ret_any
},
130 {"spin_lock_irqsave_nested", LOCK
, spin_lock
, 0, ret_any
},
131 {"_spin_lock_irqsave_nested", LOCK
, spin_lock
, 0, ret_any
},
132 {"__spin_lock_irqsave_nested", LOCK
, spin_lock
, 0, ret_any
},
133 {"_raw_spin_lock_irqsave_nested", LOCK
, spin_lock
, 0, ret_any
},
134 {"spin_lock_bh", LOCK
, spin_lock
, 0, ret_any
},
135 {"spin_unlock_bh", UNLOCK
, spin_lock
, 0, ret_any
},
136 {"_spin_lock_bh", LOCK
, spin_lock
, 0, ret_any
},
137 {"_spin_unlock_bh", UNLOCK
, spin_lock
, 0, ret_any
},
138 {"__spin_lock_bh", LOCK
, spin_lock
, 0, ret_any
},
139 {"__spin_unlock_bh", UNLOCK
, spin_lock
, 0, ret_any
},
141 {"spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
142 {"_spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
143 {"__spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
144 {"raw_spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
145 {"_raw_spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
146 {"spin_trylock_irq", LOCK
, spin_lock
, 0, ret_one
},
147 {"spin_trylock_irqsave", LOCK
, spin_lock
, 0, ret_one
},
148 {"spin_trylock_bh", LOCK
, spin_lock
, 0, ret_one
},
149 {"_spin_trylock_bh", LOCK
, spin_lock
, 0, ret_one
},
150 {"__spin_trylock_bh", LOCK
, spin_lock
, 0, ret_one
},
151 {"__raw_spin_trylock", LOCK
, spin_lock
, 0, ret_one
},
152 {"_atomic_dec_and_lock", LOCK
, spin_lock
, 1, ret_one
},
154 {"read_lock", LOCK
, read_lock
, 0, ret_any
},
155 {"down_read", LOCK
, read_lock
, 0, ret_any
},
156 {"down_read_nested", LOCK
, read_lock
, 0, ret_any
},
157 {"down_read_trylock", LOCK
, read_lock
, 0, ret_one
},
158 {"down_read_killable", LOCK
, read_lock
, 0, ret_zero
},
159 {"up_read", UNLOCK
, read_lock
, 0, ret_any
},
160 {"read_unlock", UNLOCK
, read_lock
, 0, ret_any
},
161 {"_read_lock", LOCK
, read_lock
, 0, ret_any
},
162 {"_read_unlock", UNLOCK
, read_lock
, 0, ret_any
},
163 {"__read_lock", LOCK
, read_lock
, 0, ret_any
},
164 {"__read_unlock", UNLOCK
, read_lock
, 0, ret_any
},
165 {"_raw_read_lock", LOCK
, read_lock
, 0, ret_any
},
166 {"_raw_read_unlock", UNLOCK
, read_lock
, 0, ret_any
},
167 {"__raw_read_lock", LOCK
, read_lock
, 0, ret_any
},
168 {"__raw_read_unlock", UNLOCK
, read_lock
, 0, ret_any
},
169 {"read_lock_irq", LOCK
, read_lock
, 0, ret_any
},
170 {"read_unlock_irq" , UNLOCK
, read_lock
, 0, ret_any
},
171 {"_read_lock_irq", LOCK
, read_lock
, 0, ret_any
},
172 {"_read_unlock_irq", UNLOCK
, read_lock
, 0, ret_any
},
173 {"__read_lock_irq", LOCK
, read_lock
, 0, ret_any
},
174 {"__read_unlock_irq", UNLOCK
, read_lock
, 0, ret_any
},
175 {"_raw_read_unlock_irq", UNLOCK
, read_lock
, 0, ret_any
},
176 {"_raw_read_lock_irq", LOCK
, read_lock
, 0, ret_any
},
177 {"_raw_read_lock_bh", LOCK
, read_lock
, 0, ret_any
},
178 {"_raw_read_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
179 {"read_lock_irqsave", LOCK
, read_lock
, 0, ret_any
},
180 {"read_unlock_irqrestore", UNLOCK
, read_lock
, 0, ret_any
},
181 {"_read_lock_irqsave", LOCK
, read_lock
, 0, ret_any
},
182 {"_read_unlock_irqrestore", UNLOCK
, read_lock
, 0, ret_any
},
183 {"__read_lock_irqsave", LOCK
, read_lock
, 0, ret_any
},
184 {"__read_unlock_irqrestore", UNLOCK
, read_lock
, 0, ret_any
},
185 {"read_lock_bh", LOCK
, read_lock
, 0, ret_any
},
186 {"read_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
187 {"_read_lock_bh", LOCK
, read_lock
, 0, ret_any
},
188 {"_read_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
189 {"__read_lock_bh", LOCK
, read_lock
, 0, ret_any
},
190 {"__read_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
191 {"__raw_read_lock_bh", LOCK
, read_lock
, 0, ret_any
},
192 {"__raw_read_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
194 {"_raw_read_lock_irqsave", LOCK
, read_lock
, 0, ret_any
},
195 {"_raw_read_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
196 {"_raw_read_unlock_irqrestore", UNLOCK
, read_lock
, 0, ret_any
},
197 {"_raw_read_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
198 {"_raw_spin_lock_bh", LOCK
, read_lock
, 0, ret_any
},
199 {"_raw_spin_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
200 {"_raw_spin_lock_nest_lock", LOCK
, read_lock
, 0, ret_any
},
201 {"_raw_spin_unlock_bh", UNLOCK
, read_lock
, 0, ret_any
},
202 {"_raw_spin_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
203 {"_raw_write_lock_irqsave", LOCK
, write_lock
, 0, ret_any
},
204 {"_raw_write_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
205 {"_raw_write_unlock_irqrestore", UNLOCK
, write_lock
, 0, ret_any
},
206 {"_raw_write_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
207 {"__raw_write_unlock_irqrestore", UNLOCK
, write_lock
, 0, ret_any
},
208 {"__raw_write_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
210 {"generic__raw_read_trylock", LOCK
, read_lock
, 0, ret_one
},
211 {"read_trylock", LOCK
, read_lock
, 0, ret_one
},
212 {"_read_trylock", LOCK
, read_lock
, 0, ret_one
},
213 {"raw_read_trylock", LOCK
, read_lock
, 0, ret_one
},
214 {"_raw_read_trylock", LOCK
, read_lock
, 0, ret_one
},
215 {"__raw_read_trylock", LOCK
, read_lock
, 0, ret_one
},
216 {"__read_trylock", LOCK
, read_lock
, 0, ret_one
},
218 {"write_lock", LOCK
, write_lock
, 0, ret_any
},
219 {"down_write", LOCK
, write_lock
, 0, ret_any
},
220 {"down_write_nested", LOCK
, write_lock
, 0, ret_any
},
221 {"up_write", UNLOCK
, write_lock
, 0, ret_any
},
222 {"write_unlock", UNLOCK
, write_lock
, 0, ret_any
},
223 {"_write_lock", LOCK
, write_lock
, 0, ret_any
},
224 {"_write_unlock", UNLOCK
, write_lock
, 0, ret_any
},
225 {"__write_lock", LOCK
, write_lock
, 0, ret_any
},
226 {"__write_unlock", UNLOCK
, write_lock
, 0, ret_any
},
227 {"write_lock_irq", LOCK
, write_lock
, 0, ret_any
},
228 {"write_unlock_irq", UNLOCK
, write_lock
, 0, ret_any
},
229 {"_write_lock_irq", LOCK
, write_lock
, 0, ret_any
},
230 {"_write_unlock_irq", UNLOCK
, write_lock
, 0, ret_any
},
231 {"__write_lock_irq", LOCK
, write_lock
, 0, ret_any
},
232 {"__write_unlock_irq", UNLOCK
, write_lock
, 0, ret_any
},
233 {"_raw_write_unlock_irq", UNLOCK
, write_lock
, 0, ret_any
},
234 {"write_lock_irqsave", LOCK
, write_lock
, 0, ret_any
},
235 {"write_unlock_irqrestore", UNLOCK
, write_lock
, 0, ret_any
},
236 {"_write_lock_irqsave", LOCK
, write_lock
, 0, ret_any
},
237 {"_write_unlock_irqrestore", UNLOCK
, write_lock
, 0, ret_any
},
238 {"__write_lock_irqsave", LOCK
, write_lock
, 0, ret_any
},
239 {"__write_unlock_irqrestore", UNLOCK
, write_lock
, 0, ret_any
},
240 {"write_lock_bh", LOCK
, write_lock
, 0, ret_any
},
241 {"write_unlock_bh", UNLOCK
, write_lock
, 0, ret_any
},
242 {"_write_lock_bh", LOCK
, write_lock
, 0, ret_any
},
243 {"_write_unlock_bh", UNLOCK
, write_lock
, 0, ret_any
},
244 {"__write_lock_bh", LOCK
, write_lock
, 0, ret_any
},
245 {"__write_unlock_bh", UNLOCK
, write_lock
, 0, ret_any
},
246 {"_raw_write_lock", LOCK
, write_lock
, 0, ret_any
},
247 {"__raw_write_lock", LOCK
, write_lock
, 0, ret_any
},
248 {"_raw_write_unlock", UNLOCK
, write_lock
, 0, ret_any
},
249 {"__raw_write_unlock", UNLOCK
, write_lock
, 0, ret_any
},
250 {"_raw_write_lock_bh", LOCK
, write_lock
, 0, ret_any
},
251 {"_raw_write_unlock_bh", UNLOCK
, write_lock
, 0, ret_any
},
252 {"_raw_write_lock_irq", LOCK
, write_lock
, 0, ret_any
},
254 {"write_trylock", LOCK
, write_lock
, 0, ret_one
},
255 {"_write_trylock", LOCK
, write_lock
, 0, ret_one
},
256 {"raw_write_trylock", LOCK
, write_lock
, 0, ret_one
},
257 {"_raw_write_trylock", LOCK
, write_lock
, 0, ret_one
},
258 {"__write_trylock", LOCK
, write_lock
, 0, ret_one
},
259 {"__raw_write_trylock", LOCK
, write_lock
, 0, ret_one
},
260 {"down_write_trylock", LOCK
, write_lock
, 0, ret_one
},
261 {"down_write_killable", LOCK
, write_lock
, 0, ret_zero
},
263 {"down", LOCK
, sem
, 0, ret_any
},
264 {"up", UNLOCK
, sem
, 0, ret_any
},
265 {"down_trylock", LOCK
, sem
, 0, ret_zero
},
266 {"down_timeout", LOCK
, sem
, 0, ret_zero
},
267 {"down_interruptible", LOCK
, sem
, 0, ret_zero
},
268 {"down_killable", LOCK
, sem
, 0, ret_zero
},
271 {"mutex_lock", LOCK
, mutex
, 0, ret_any
},
272 {"mutex_unlock", UNLOCK
, mutex
, 0, ret_any
},
273 {"mutex_destroy", RESTORE
, mutex
, 0, ret_any
},
274 {"mutex_lock_nested", LOCK
, mutex
, 0, ret_any
},
275 {"mutex_lock_io", LOCK
, mutex
, 0, ret_any
},
276 {"mutex_lock_io_nested", LOCK
, mutex
, 0, ret_any
},
278 {"mutex_lock_interruptible", LOCK
, mutex
, 0, ret_zero
},
279 {"mutex_lock_interruptible_nested", LOCK
, mutex
, 0, ret_zero
},
280 {"mutex_lock_killable", LOCK
, mutex
, 0, ret_zero
},
281 {"mutex_lock_killable_nested", LOCK
, mutex
, 0, ret_zero
},
283 {"mutex_trylock", LOCK
, mutex
, 0, ret_one
},
285 {"ww_mutex_lock", LOCK
, mutex
, 0, ret_any
},
286 {"__ww_mutex_lock", LOCK
, mutex
, 0, ret_any
},
287 {"ww_mutex_lock_interruptible", LOCK
, mutex
, 0, ret_zero
},
288 {"ww_mutex_unlock", UNLOCK
, mutex
, 0, ret_any
},
290 {"raw_local_irq_disable", LOCK
, irq
, NO_ARG
, ret_any
},
291 {"raw_local_irq_enable", UNLOCK
, irq
, NO_ARG
, ret_any
},
292 {"spin_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
293 {"spin_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
294 {"_spin_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
295 {"_spin_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
296 {"__spin_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
297 {"__spin_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
298 {"_raw_spin_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
299 {"_raw_spin_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
300 {"__raw_spin_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
301 {"spin_trylock_irq", LOCK
, irq
, NO_ARG
, ret_one
},
302 {"read_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
303 {"read_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
304 {"_read_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
305 {"_read_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
306 {"__read_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
307 {"_raw_read_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
308 {"__read_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
309 {"_raw_read_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
310 {"write_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
311 {"write_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
312 {"_write_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
313 {"_write_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
314 {"__write_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
315 {"__write_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
316 {"_raw_write_lock_irq", LOCK
, irq
, NO_ARG
, ret_any
},
317 {"_raw_write_unlock_irq", UNLOCK
, irq
, NO_ARG
, ret_any
},
319 {"arch_local_irq_save", LOCK
, irq
, RETURN_VAL
, ret_any
},
320 {"arch_local_irq_restore", RESTORE
, irq
, 0, ret_any
},
321 {"__raw_local_irq_save", LOCK
, irq
, RETURN_VAL
, ret_any
},
322 {"raw_local_irq_restore", RESTORE
, irq
, 0, ret_any
},
323 {"spin_lock_irqsave_nested", LOCK
, irq
, RETURN_VAL
, ret_any
},
324 {"spin_lock_irqsave", LOCK
, irq
, 1, ret_any
},
325 {"spin_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
326 {"_spin_lock_irqsave_nested", LOCK
, irq
, RETURN_VAL
, ret_any
},
327 {"_spin_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
328 {"_spin_lock_irqsave", LOCK
, irq
, 1, ret_any
},
329 {"_spin_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
330 {"__spin_lock_irqsave_nested", LOCK
, irq
, 1, ret_any
},
331 {"__spin_lock_irqsave", LOCK
, irq
, 1, ret_any
},
332 {"__spin_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
333 {"_raw_spin_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
334 {"_raw_spin_lock_irqsave", LOCK
, irq
, 1, ret_any
},
335 {"_raw_spin_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
336 {"__raw_spin_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
337 {"__raw_spin_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
338 {"_raw_spin_lock_irqsave_nested", LOCK
, irq
, RETURN_VAL
, ret_any
},
339 {"spin_trylock_irqsave", LOCK
, irq
, 1, ret_one
},
340 {"read_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
341 {"read_lock_irqsave", LOCK
, irq
, 1, ret_any
},
342 {"read_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
343 {"_read_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
344 {"_read_lock_irqsave", LOCK
, irq
, 1, ret_any
},
345 {"_read_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
346 {"__read_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
347 {"__read_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
348 {"write_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
349 {"write_lock_irqsave", LOCK
, irq
, 1, ret_any
},
350 {"write_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
351 {"_write_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
352 {"_write_lock_irqsave", LOCK
, irq
, 1, ret_any
},
353 {"_write_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
354 {"__write_lock_irqsave", LOCK
, irq
, RETURN_VAL
, ret_any
},
355 {"__write_unlock_irqrestore", RESTORE
, irq
, 1, ret_any
},
357 {"local_bh_disable", LOCK
, bottom_half
, NO_ARG
, ret_any
},
358 {"_local_bh_disable", LOCK
, bottom_half
, NO_ARG
, ret_any
},
359 {"__local_bh_disable", LOCK
, bottom_half
, NO_ARG
, ret_any
},
360 {"local_bh_enable", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
361 {"_local_bh_enable", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
362 {"__local_bh_enable", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
363 {"spin_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
364 {"spin_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
365 {"_spin_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
366 {"_spin_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
367 {"__spin_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
368 {"__spin_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
369 {"read_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
370 {"read_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
371 {"_read_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
372 {"_read_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
373 {"__read_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
374 {"__read_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
375 {"_raw_read_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
376 {"_raw_read_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
377 {"write_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
378 {"write_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
379 {"_write_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
380 {"_write_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
381 {"__write_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
382 {"__write_unlock_bh", UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
383 {"_raw_write_lock_bh", LOCK
, bottom_half
, NO_ARG
, ret_any
},
384 {"_raw_write_unlock_bh",UNLOCK
, bottom_half
, NO_ARG
, ret_any
},
385 {"spin_trylock_bh", LOCK
, bottom_half
, NO_ARG
, ret_one
},
386 {"_spin_trylock_bh", LOCK
, bottom_half
, NO_ARG
, ret_one
},
387 {"__spin_trylock_bh", LOCK
, bottom_half
, NO_ARG
, ret_one
},
389 {"ffs_mutex_lock", LOCK
, mutex
, 0, ret_zero
},
391 {"clk_prepare_lock", LOCK
, prepare_lock
, NO_ARG
, ret_any
},
392 {"clk_prepare_unlock", UNLOCK
, prepare_lock
, NO_ARG
, ret_any
},
393 {"clk_enable_lock", LOCK
, enable_lock
, -1, ret_any
},
394 {"clk_enable_unlock", UNLOCK
, enable_lock
, 0, ret_any
},
396 {"dma_resv_lock", LOCK
, mutex
, 0, ret_zero
},
397 {"dma_resv_trylock", LOCK
, mutex
, 0, ret_one
},
398 {"dma_resv_lock_interruptible", LOCK
, mutex
, 0, ret_zero
},
399 {"dma_resv_unlock", UNLOCK
, mutex
, 0, ret_any
},
401 {"modeset_lock", LOCK
, mutex
, 0, ret_zero
},
402 {"drm_ modeset_lock", LOCK
, mutex
, 0, ret_zero
},
403 {"drm_modeset_lock_single_interruptible", LOCK
, mutex
, 0, ret_zero
},
404 {"modeset_unlock", UNLOCK
, mutex
, 0, ret_any
},
405 // {"nvkm_i2c_aux_acquire", LOCK, mutex,
406 // {"i915_gem_object_lock_interruptible", LOCK, mutex,
408 {"reiserfs_write_lock_nested", LOCK
, mutex
, 0, ret_any
},
409 {"reiserfs_write_unlock_nested", UNLOCK
, mutex
, 0, ret_any
},
411 {"rw_lock", LOCK
, write_lock
, 1, ret_any
},
412 {"rw_unlock", UNLOCK
, write_lock
, 1, ret_any
},
414 {"sem_lock", LOCK
, mutex
, 0, ret_any
},
415 {"sem_unlock", UNLOCK
, mutex
, 0, ret_any
},
417 {"rcu_lock_acquire", LOCK
, rcu
, NO_ARG
, ret_any
},
418 {"rcu_lock_release", UNLOCK
, rcu
, NO_ARG
, ret_any
},
420 {"rcu_read_lock", LOCK
, rcu_read
, NO_ARG
, ret_any
},
421 {"rcu_read_unlock", UNLOCK
, rcu_read
, NO_ARG
, ret_any
},
422 {"rcu_read_lock_bh", LOCK
, rcu_read
, NO_ARG
, ret_any
},
423 {"rcu_read_unlock_bh", UNLOCK
, rcu_read
, NO_ARG
, ret_any
},
425 {"rcu_read_lock_sched", LOCK
, rcu_read
, NO_ARG
, ret_any
},
426 {"rcu_read_lock_sched_notrace", LOCK
, rcu_read
, NO_ARG
, ret_any
},
427 {"rcu_read_unlock_sched", UNLOCK
, rcu_read
, NO_ARG
, ret_any
},
428 {"rcu_read_unlock_sched_notrace", UNLOCK
, rcu_read
, NO_ARG
, ret_any
},
439 static struct macro_info macro_table
[] = {
440 {"genpd_lock", LOCK
, 0},
441 {"genpd_lock_nested", LOCK
, 0},
442 {"genpd_lock_interruptible", LOCK
, 0},
443 {"genpd_unlock", UNLOCK
, 0},
446 static const char *false_positives
[][2] = {
447 {"fs/jffs2/", "->alloc_sem"},
448 {"fs/xfs/", "->b_sema"},
449 {"mm/", "pvmw->ptl"},
452 static struct stree
*start_states
;
454 static struct tracker_list
*locks
;
456 static struct expression
*ignored_reset
;
457 static void reset(struct sm_state
*sm
, struct expression
*mod_expr
)
459 struct expression
*faked
;
461 if (mod_expr
&& mod_expr
->type
== EXPR_ASSIGNMENT
&&
462 mod_expr
->left
== ignored_reset
)
464 faked
= get_faked_expression();
465 if (faked
&& faked
->type
== EXPR_ASSIGNMENT
&&
466 faked
->left
== ignored_reset
)
469 set_state(my_id
, sm
->name
, sm
->sym
, &start_state
);
472 static struct smatch_state
*get_start_state(struct sm_state
*sm
)
474 struct smatch_state
*orig
;
479 orig
= get_state_stree(start_states
, my_id
, sm
->name
, sm
->sym
);
485 static struct expression
*remove_spinlock_check(struct expression
*expr
)
487 if (expr
->type
!= EXPR_CALL
)
489 if (expr
->fn
->type
!= EXPR_SYMBOL
)
491 if (strcmp(expr
->fn
->symbol_name
->name
, "spinlock_check"))
493 expr
= get_argument_from_call_expr(expr
->args
, 0);
497 static struct expression
*filter_kernel_args(struct expression
*arg
)
499 if (arg
->type
== EXPR_PREOP
&& arg
->op
== '&')
500 return strip_expr(arg
->unop
);
501 if (!is_pointer(arg
))
503 return deref_expression(strip_expr(arg
));
506 static char *lock_to_name_sym(struct expression
*expr
, struct symbol
**sym
)
508 expr
= remove_spinlock_check(expr
);
509 expr
= filter_kernel_args(expr
);
510 return expr_to_str_sym(expr
, sym
);
513 static char *get_full_name(struct expression
*expr
, int index
, struct symbol
**sym
)
515 struct lock_info
*lock
= &lock_table
[index
];
516 struct expression
*arg
;
519 if (lock
->arg
== RETURN_VAL
) {
520 return expr_to_var_sym(strip_expr(expr
->left
), sym
);
521 } else if (lock
->arg
== NO_ARG
) {
522 return alloc_string(get_lock_name(lock
->type
));
524 arg
= get_argument_from_call_expr(expr
->args
, lock
->arg
);
527 return lock_to_name_sym(arg
, sym
);
531 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
536 static void pre_merge_hook(struct sm_state
*cur
, struct sm_state
*other
)
538 if (is_impossible_path())
539 set_state(my_id
, cur
->name
, cur
->sym
, &impossible
);
542 static struct smatch_state
*merge_func(struct smatch_state
*s1
, struct smatch_state
*s2
)
544 if (s1
== &impossible
)
546 if (s2
== &impossible
)
551 static struct smatch_state
*action_to_state(enum action lock_unlock
)
553 switch (lock_unlock
) {
564 static struct sm_state
*get_best_match(const char *key
, enum action lock_unlock
)
567 struct sm_state
*match
;
569 int start_pos
, state_len
, key_len
, chunks
, i
;
571 if (strncmp(key
, "$->", 3) == 0)
574 key_len
= strlen(key
);
576 for (i
= key_len
- 1; i
> 0; i
--) {
577 if (key
[i
] == '>' || key
[i
] == '.')
581 key_len
= strlen(key
);
586 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
587 if (((lock_unlock
== UNLOCK
|| lock_unlock
== RESTORE
) &&
588 sm
->state
!= &locked
) ||
589 (lock_unlock
== LOCK
&& sm
->state
!= &unlocked
))
591 state_len
= strlen(sm
->name
);
592 if (state_len
< key_len
)
594 start_pos
= state_len
- key_len
;
595 if ((start_pos
== 0 || !isalnum(sm
->name
[start_pos
- 1])) &&
596 strcmp(sm
->name
+ start_pos
, key
) == 0) {
600 } END_FOR_EACH_SM(sm
);
607 static void use_best_match(char *key
, enum action lock_unlock
)
609 struct sm_state
*match
;
611 match
= get_best_match(key
, lock_unlock
);
613 set_state(my_id
, match
->name
, match
->sym
, action_to_state(lock_unlock
));
615 set_state(my_id
, key
, NULL
, action_to_state(lock_unlock
));
618 static void set_start_state(const char *name
, struct symbol
*sym
, struct smatch_state
*start
)
620 struct smatch_state
*orig
;
622 orig
= get_state_stree(start_states
, my_id
, name
, sym
);
624 set_state_stree(&start_states
, my_id
, name
, sym
, start
);
625 else if (orig
!= start
)
626 set_state_stree(&start_states
, my_id
, name
, sym
, &undefined
);
629 static bool common_false_positive(const char *name
)
631 const char *path
, *lname
;
632 int i
, len_total
, len_path
, len_name
, skip
;
637 len_total
= strlen(name
);
638 for (i
= 0; i
< ARRAY_SIZE(false_positives
); i
++) {
639 path
= false_positives
[i
][0];
640 lname
= false_positives
[i
][1];
642 len_path
= strlen(path
);
643 len_name
= strlen(lname
);
645 if (len_name
> len_total
)
647 skip
= len_total
- len_name
;
649 if (strncmp(get_filename(), path
, len_path
) == 0 &&
650 strcmp(name
+ skip
, lname
) == 0)
657 static bool sm_in_start_states(struct sm_state
*sm
)
659 if (!sm
|| !cur_func_sym
)
661 if (sm
->line
== cur_func_sym
->pos
.line
)
666 static void warn_on_double(struct sm_state
*sm
, struct smatch_state
*state
)
668 struct sm_state
*tmp
;
673 FOR_EACH_PTR(sm
->possible
, tmp
) {
674 if (tmp
->state
== state
)
676 } END_FOR_EACH_PTR(tmp
);
680 // FIXME: called with read_lock held
681 // drivers/scsi/aic7xxx/aic7xxx_osm.c:1591 ahc_linux_isr() error: double locked 'flags' (orig line 1584)
682 if (strcmp(sm
->name
, "bottom_half") == 0)
684 if (strstr(sm
->name
, "rcu"))
686 if (common_false_positive(sm
->name
))
689 // if (state == &locked && sm_in_start_states(tmp))
690 // sm_warning("called with lock held. '%s'", sm->name);
692 // sm_msg("error: double %s '%s' (orig line %u)",
693 // state->name, sm->name, tmp->line);
696 static bool handle_macro_lock_unlock(void)
698 struct expression
*expr
, *arg
;
699 struct macro_info
*info
;
707 expr
= last_ptr_list((struct ptr_list
*)big_expression_stack
);
708 while (expr
&& expr
->type
== EXPR_ASSIGNMENT
)
709 expr
= strip_expr(expr
->right
);
710 if (!expr
|| expr
->type
!= EXPR_CALL
)
713 macro
= get_macro_name(expr
->pos
);
717 for (i
= 0; i
< ARRAY_SIZE(macro_table
); i
++) {
718 info
= ¯o_table
[i
];
720 if (strcmp(macro
, info
->macro
) != 0)
722 arg
= get_argument_from_call_expr(expr
->args
, info
->param
);
723 name
= expr_to_str_sym(arg
, &sym
);
726 sm
= get_sm_state(my_id
, name
, sym
);
728 if (info
->action
== LOCK
) {
729 if (!get_start_state(sm
))
730 set_start_state(name
, sym
, &unlocked
);
731 if (sm
&& sm
->line
!= expr
->pos
.line
)
732 warn_on_double(sm
, &locked
);
733 set_state(my_id
, name
, sym
, &locked
);
735 if (!get_start_state(sm
))
736 set_start_state(name
, sym
, &locked
);
737 if (sm
&& sm
->line
!= expr
->pos
.line
)
738 warn_on_double(sm
, &unlocked
);
739 set_state(my_id
, name
, sym
, &unlocked
);
749 static bool is_local_IRQ_save(const char *name
, struct symbol
*sym
, struct lock_info
*info
)
751 if (name
&& strcmp(name
, "flags") == 0)
755 if (!sym
->ident
|| strcmp(sym
->ident
->name
, name
) != 0)
759 return strstr(info
->function
, "irq") && strstr(info
->function
, "save");
762 static void do_lock(const char *name
, struct symbol
*sym
, struct lock_info
*info
)
765 bool delete_null
= false;
767 if (handle_macro_lock_unlock())
770 add_tracker(&locks
, my_id
, name
, sym
);
772 sm
= get_sm_state(my_id
, name
, sym
);
773 if (!get_start_state(sm
))
774 set_start_state(name
, sym
, &unlocked
);
775 if (!sm
&& !is_local_IRQ_save(name
, sym
, info
) && sym
) {
776 sm
= get_sm_state(my_id
, name
, NULL
);
780 warn_on_double(sm
, &locked
);
782 delete_state(my_id
, name
, NULL
);
783 delete_state(my_id
, name
, NULL
);
785 set_state(my_id
, name
, sym
, &locked
);
788 static void do_lock_failed(const char *name
, struct symbol
*sym
, struct lock_info
*info
)
790 add_tracker(&locks
, my_id
, name
, sym
);
791 if (!is_local_IRQ_save(name
, sym
, info
) && sym
)
792 delete_state(my_id
, name
, NULL
);
793 set_state(my_id
, name
, sym
, &unlocked
);
796 static void do_unlock(const char *name
, struct symbol
*sym
, struct lock_info
*info
)
799 bool delete_null
= false;
801 if (__path_is_null())
804 if (handle_macro_lock_unlock())
807 add_tracker(&locks
, my_id
, name
, sym
);
808 sm
= get_sm_state(my_id
, name
, sym
);
809 if (!sm
&& !is_local_IRQ_save(name
, sym
, info
)) {
810 sm
= get_best_match(name
, UNLOCK
);
819 if (!get_start_state(sm
))
820 set_start_state(name
, sym
, &locked
);
821 warn_on_double(sm
, &unlocked
);
823 delete_state(my_id
, name
, NULL
);
824 set_state(my_id
, name
, sym
, &unlocked
);
827 static void do_restore(const char *name
, struct symbol
*sym
, struct lock_info
*info
)
831 if (__path_is_null())
834 sm
= get_sm_state(my_id
, name
, sym
);
835 if (!get_start_state(sm
))
836 set_start_state(name
, sym
, &locked
);
838 add_tracker(&locks
, my_id
, name
, sym
);
839 set_state(my_id
, name
, sym
, &restore
);
842 static void match_lock_held(const char *fn
, struct expression
*call_expr
,
843 struct expression
*assign_expr
, void *_index
)
845 int index
= PTR_INT(_index
);
846 struct lock_info
*lock
= &lock_table
[index
];
850 if (lock
->arg
== NO_ARG
) {
851 lock_name
= get_full_name(NULL
, index
, &sym
);
852 } else if (lock
->arg
== RETURN_VAL
) {
855 lock_name
= get_full_name(assign_expr
, index
, &sym
);
857 lock_name
= get_full_name(call_expr
, index
, &sym
);
861 do_lock(lock_name
, sym
, lock
);
862 free_string(lock_name
);
865 static void match_lock_failed(const char *fn
, struct expression
*call_expr
,
866 struct expression
*assign_expr
, void *_index
)
868 int index
= PTR_INT(_index
);
869 struct lock_info
*lock
= &lock_table
[index
];
873 if (lock
->arg
== NO_ARG
) {
874 lock_name
= get_full_name(NULL
, index
, &sym
);
875 } else if (lock
->arg
== RETURN_VAL
) {
878 lock_name
= get_full_name(assign_expr
, index
, &sym
);
880 lock_name
= get_full_name(call_expr
, index
, &sym
);
884 do_lock_failed(lock_name
, sym
, lock
);
885 free_string(lock_name
);
888 static void match_returns_locked(const char *fn
, struct expression
*expr
,
891 int index
= PTR_INT(_index
);
892 struct lock_info
*lock
= &lock_table
[index
];
896 if (lock
->arg
!= RETURN_VAL
)
898 full_name
= get_full_name(expr
, index
, &sym
);
901 do_lock(full_name
, sym
, lock
);
904 static void match_lock_unlock(const char *fn
, struct expression
*expr
, void *_index
)
906 int index
= PTR_INT(_index
);
907 struct lock_info
*lock
= &lock_table
[index
];
911 full_name
= get_full_name(expr
, index
, &sym
);
914 switch (lock
->action
) {
916 do_lock(full_name
, sym
, lock
);
919 do_unlock(full_name
, sym
, lock
);
922 do_restore(full_name
, sym
, lock
);
925 free_string(full_name
);
928 static int get_db_type(struct sm_state
*sm
)
931 * Bottom half is complicated because it's nestable.
932 * Say it's merged at the start and we lock and unlock then
933 * it should go back to merged.
935 if (sm
->state
== get_start_state(sm
)) {
936 if (sm
->state
== &locked
)
938 if (sm
->state
== &unlocked
)
939 return KNOWN_UNLOCKED
;
942 if (sm
->state
== &locked
)
944 if (sm
->state
== &unlocked
)
946 if (sm
->state
== &restore
)
947 return LOCK_RESTORED
;
951 static struct stree
*already_done
;
952 static void save_recorded_state(int return_id
, int param
, const char *param_name
,
953 struct smatch_state
*state
)
955 /* using the sym like this is an ugly hack */
956 set_state_stree(&already_done
, my_id
, param_name
,
957 (struct symbol
*)(unsigned long)((return_id
<< 8) | param
),
961 static struct smatch_state
*get_recorded_state(int return_id
, int param
, const char *param_name
)
963 return get_state_stree(already_done
, my_id
, param_name
,
964 (struct symbol
*)(unsigned long)((return_id
<< 8) | param
));
967 static void match_return_info(int return_id
, char *return_ranges
, struct expression
*expr
)
969 struct smatch_state
*start
, *recorded
;
971 const char *param_name
;
974 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
975 if (sm
->state
!= &locked
&&
976 sm
->state
!= &unlocked
&&
977 sm
->state
!= &restore
)
979 if (sm
->name
[0] == '$')
983 * If the state is locked at the end, that doesn't mean
984 * anything. It could be been locked at the start. Or
985 * it could be &merged at the start but its locked now
986 * because of implications and not because we set the
989 * This is slightly a hack, but when we change the state, we
990 * call set_start_state() so if get_start_state() returns NULL
991 * that means we haven't manually the locked state.
993 start
= get_start_state(sm
);
994 if (sm
->state
== &restore
) {
995 if (start
!= &locked
)
997 } else if (!start
|| sm
->state
== start
)
998 continue; /* !start means it was passed in */
1000 param
= get_param_key_from_sm(sm
, expr
, ¶m_name
);
1001 recorded
= get_recorded_state(return_id
, param
, param_name
);
1002 if (recorded
&& recorded
== sm
->state
)
1004 save_recorded_state(return_id
, param
, param_name
, sm
->state
);
1005 sql_insert_return_states(return_id
, return_ranges
,
1007 param
, param_name
, "");
1008 } END_FOR_EACH_SM(sm
);
1012 ERR_PTR
, VALID_PTR
, NEGATIVE
, ZERO
, POSITIVE
, NUM_BUCKETS
,
1015 static bool is_EINTR(struct range_list
*rl
)
1019 if (!rl_to_sval(rl
, &sval
))
1021 return sval
.value
== -4;
1024 static int success_fail_positive(struct range_list
*rl
)
1026 /* void returns are the same as success (zero in the kernel) */
1030 if (rl_type(rl
)->type
!= SYM_PTR
&&
1032 sval_is_negative(rl_min(rl
)))
1035 if (rl_min(rl
).value
== 0 && rl_max(rl
).value
== 0)
1038 if (is_err_ptr(rl_min(rl
)) &&
1039 is_err_ptr(rl_max(rl
)))
1043 * Trying to match ERR_PTR(ret) but without the expression struct.
1046 if (type_bits(&long_ctype
) == 64 &&
1047 rl_type(rl
)->type
== SYM_PTR
&&
1048 rl_min(rl
).value
== INT_MIN
)
1054 static bool sym_in_lock_table(struct symbol
*sym
)
1058 if (!sym
|| !sym
->ident
)
1061 for (i
= 0; lock_table
[i
].function
!= NULL
; i
++) {
1062 if (strcmp(lock_table
[i
].function
, sym
->ident
->name
) == 0)
1068 static bool func_in_lock_table(struct expression
*expr
)
1070 if (expr
->type
!= EXPR_SYMBOL
)
1072 return sym_in_lock_table(expr
->symbol
);
1075 static void check_lock(char *name
, struct symbol
*sym
)
1077 struct range_list
*locked_lines
= NULL
;
1078 struct range_list
*unlocked_lines
= NULL
;
1079 int locked_buckets
[NUM_BUCKETS
] = {};
1080 int unlocked_buckets
[NUM_BUCKETS
] = {};
1081 struct stree
*stree
, *orig
;
1082 struct sm_state
*return_sm
;
1083 struct sm_state
*sm
;
1084 sval_t line
= sval_type_val(&int_ctype
, 0);
1088 if (sym_in_lock_table(cur_func_sym
))
1091 FOR_EACH_PTR(get_all_return_strees(), stree
) {
1092 orig
= __swap_cur_stree(stree
);
1094 if (is_impossible_path())
1097 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
1100 line
.value
= return_sm
->line
;
1102 sm
= get_sm_state(my_id
, name
, sym
);
1106 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
1109 if (sm
->state
!= &locked
&&
1110 sm
->state
!= &unlocked
&&
1111 sm
->state
!= &restore
)
1114 if ((sm
->state
== &unlocked
|| sm
->state
== &restore
) &&
1115 is_EINTR(estate_rl(return_sm
->state
)))
1118 bucket
= success_fail_positive(estate_rl(return_sm
->state
));
1119 if (sm
->state
== &locked
) {
1120 add_range(&locked_lines
, line
, line
);
1121 locked_buckets
[bucket
] = true;
1123 if (sm
->state
== &unlocked
|| sm
->state
== &restore
) {
1124 add_range(&unlocked_lines
, line
, line
);
1125 unlocked_buckets
[bucket
] = true;
1128 __swap_cur_stree(orig
);
1129 } END_FOR_EACH_PTR(stree
);
1132 if (!locked_lines
|| !unlocked_lines
)
1135 for (i
= 0; i
< NUM_BUCKETS
; i
++) {
1136 if (locked_buckets
[i
] && unlocked_buckets
[i
])
1139 if (locked_buckets
[NEGATIVE
] &&
1140 (unlocked_buckets
[ZERO
] || unlocked_buckets
[POSITIVE
]))
1143 if (locked_buckets
[ERR_PTR
])
1149 sm_msg("warn: inconsistent returns '%s'.", name
);
1150 sm_printf(" Locked on : %s\n", show_rl(locked_lines
));
1151 sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines
));
1154 static void match_func_end(struct symbol
*sym
)
1156 struct tracker
*tracker
;
1158 FOR_EACH_PTR(locks
, tracker
) {
1159 check_lock(tracker
->name
, tracker
->sym
);
1160 } END_FOR_EACH_PTR(tracker
);
1163 static void register_lock(int index
)
1165 struct lock_info
*lock
= &lock_table
[index
];
1166 void *idx
= INT_PTR(index
);
1168 if (lock
->return_type
== ret_one
) {
1169 return_implies_state(lock
->function
, 1, 1, &match_lock_held
, idx
);
1170 return_implies_state(lock
->function
, 0, 0, &match_lock_failed
, idx
);
1171 } else if (lock
->return_type
== ret_any
&& lock
->arg
== RETURN_VAL
) {
1172 add_function_assign_hook(lock
->function
, &match_returns_locked
, idx
);
1173 } else if (lock
->return_type
== ret_any
) {
1174 add_function_hook(lock
->function
, &match_lock_unlock
, idx
);
1175 } else if (lock
->return_type
== ret_zero
) {
1176 return_implies_state(lock
->function
, 0, 0, &match_lock_held
, idx
);
1177 return_implies_state(lock
->function
, -4095, -1, &match_lock_failed
, idx
);
1178 } else if (lock
->return_type
== ret_valid_ptr
) {
1179 return_implies_state_sval(lock
->function
, valid_ptr_min_sval
, valid_ptr_max_sval
, &match_lock_held
, idx
);
1183 static void load_table(struct lock_info
*lock_table
)
1187 for (i
= 0; lock_table
[i
].function
!= NULL
; i
++) {
1188 if (lock_table
[i
].action
== LOCK
)
1191 add_function_hook(lock_table
[i
].function
, &match_lock_unlock
, INT_PTR(i
));
1195 static void db_param_locked_unlocked(struct expression
*expr
, int param
, char *key
, char *value
, enum action lock_unlock
)
1197 struct expression
*call
, *arg
;
1202 while (call
->type
== EXPR_ASSIGNMENT
)
1203 call
= strip_expr(call
->right
);
1204 if (call
->type
!= EXPR_CALL
)
1207 if (func_in_lock_table(call
->fn
))
1211 use_best_match(key
, lock_unlock
);
1216 if (expr
->type
!= EXPR_ASSIGNMENT
)
1218 ignored_reset
= expr
->left
;
1219 name
= get_variable_from_key(expr
->left
, key
, &sym
);
1221 arg
= get_argument_from_call_expr(call
->args
, param
);
1225 name
= get_variable_from_key(arg
, key
, &sym
);
1230 if (lock_unlock
== LOCK
)
1231 do_lock(name
, sym
, NULL
);
1232 else if (lock_unlock
== UNLOCK
)
1233 do_unlock(name
, sym
, NULL
);
1234 else if (lock_unlock
== RESTORE
)
1235 do_restore(name
, sym
, NULL
);
1241 static void db_param_locked(struct expression
*expr
, int param
, char *key
, char *value
)
1243 db_param_locked_unlocked(expr
, param
, key
, value
, LOCK
);
1246 static void db_param_unlocked(struct expression
*expr
, int param
, char *key
, char *value
)
1248 db_param_locked_unlocked(expr
, param
, key
, value
, UNLOCK
);
1251 static void db_param_restore(struct expression
*expr
, int param
, char *key
, char *value
)
1253 db_param_locked_unlocked(expr
, param
, key
, value
, RESTORE
);
1256 static void match_assign(struct expression
*expr
)
1258 struct smatch_state
*state
;
1260 /* This is only for the DB */
1261 if (!__in_fake_var_assign
)
1263 state
= get_state_expr(my_id
, expr
->right
);
1266 set_state_expr(my_id
, expr
->left
, state
);
1269 static struct stree
*printed
;
1270 static void call_info_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
1272 int locked_type
= 0;
1274 if (sm
->state
== &locked
)
1275 locked_type
= LOCKED
;
1276 else if (sm
->state
== &unlocked
)
1277 locked_type
= UNLOCKED
;
1278 else if (slist_has_state(sm
->possible
, &locked
) ||
1279 slist_has_state(sm
->possible
, &half_locked
))
1280 locked_type
= HALF_LOCKED
;
1284 avl_insert(&printed
, sm
);
1285 sql_insert_caller_info(call
, locked_type
, param
, printed_name
, "");
1288 static void match_call_info(struct expression
*expr
)
1290 struct sm_state
*sm
;
1294 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
1295 if (sm
->state
== &locked
)
1296 locked_type
= LOCKED
;
1297 else if (sm
->state
== &half_locked
||
1298 slist_has_state(sm
->possible
, &locked
))
1299 locked_type
= HALF_LOCKED
;
1303 if (avl_lookup(printed
, sm
))
1306 if (strcmp(sm
->name
, "bottom_half") == 0)
1308 else if (strcmp(sm
->name
, "rcu_read") == 0)
1309 name
= "rcu_read_lock";
1313 sql_insert_caller_info(expr
, locked_type
, -2, name
, "");
1314 } END_FOR_EACH_SM(sm
);
1315 free_stree(&printed
);
1318 static void set_locked_called_state(const char *name
, struct symbol
*sym
,
1319 char *key
, char *value
,
1320 struct smatch_state
*state
)
1324 if (name
&& key
[0] == '$')
1325 snprintf(fullname
, sizeof(fullname
), "%s%s", name
, key
+ 1);
1327 snprintf(fullname
, sizeof(fullname
), "%s", key
);
1329 if (strstr(fullname
, ">>") || strstr(fullname
, "..")) {
1330 // sm_msg("warn: invalid lock name. name = '%s' key = '%s'", name, key);
1334 set_state(my_id
, fullname
, sym
, state
);
1337 static void set_locked(const char *name
, struct symbol
*sym
, char *key
, char *value
)
1339 set_locked_called_state(name
, sym
, key
, value
, &locked
);
1342 static void set_half_locked(const char *name
, struct symbol
*sym
, char *key
, char *value
)
1344 set_locked_called_state(name
, sym
, key
, value
, &half_locked
);
1347 static void set_unlocked(const char *name
, struct symbol
*sym
, char *key
, char *value
)
1349 set_locked_called_state(name
, sym
, key
, value
, &unlocked
);
1352 static void match_after_func(struct symbol
*sym
)
1354 free_stree(&start_states
);
1355 free_stree(&already_done
);
1358 static void match_dma_resv_lock_NULL(const char *fn
, struct expression
*call_expr
,
1359 struct expression
*assign_expr
, void *_index
)
1361 struct expression
*lock
, *ctx
;
1365 lock
= get_argument_from_call_expr(call_expr
->args
, 0);
1366 ctx
= get_argument_from_call_expr(call_expr
->args
, 1);
1367 if (!expr_is_zero(ctx
))
1370 lock_name
= lock_to_name_sym(lock
, &sym
);
1371 if (!lock_name
|| !sym
)
1373 do_lock(lock_name
, sym
, NULL
);
1375 free_string(lock_name
);
1378 /* print_held_locks() is used in check_call_tree.c */
1379 void print_held_locks(void)
1381 struct stree
*stree
;
1382 struct sm_state
*sm
;
1385 stree
= __get_cur_stree();
1386 FOR_EACH_MY_SM(my_id
, stree
, sm
) {
1387 if (sm
->state
!= &locked
)
1391 sm_printf("'%s'", sm
->name
);
1392 } END_FOR_EACH_SM(sm
);
1395 static bool is_smp_config(void)
1399 id
= built_in_ident("CONFIG_SMP");
1400 return !!lookup_symbol(id
, NS_MACRO
);
1403 void check_locking(int id
)
1407 if (option_project
!= PROJ_KERNEL
)
1410 if (!is_smp_config())
1413 load_table(lock_table
);
1415 set_dynamic_states(my_id
);
1416 add_unmatched_state_hook(my_id
, &unmatched_state
);
1417 add_pre_merge_hook(my_id
, &pre_merge_hook
);
1418 add_merge_hook(my_id
, &merge_func
);
1419 add_modification_hook(my_id
, &reset
);
1421 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
1422 add_hook(&match_func_end
, END_FUNC_HOOK
);
1424 add_hook(&match_after_func
, AFTER_FUNC_HOOK
);
1425 add_function_data((unsigned long *)&start_states
);
1427 add_caller_info_callback(my_id
, call_info_callback
);
1428 add_hook(&match_call_info
, FUNCTION_CALL_HOOK
);
1430 add_split_return_callback(match_return_info
);
1431 select_return_states_hook(LOCKED
, &db_param_locked
);
1432 select_return_states_hook(UNLOCKED
, &db_param_unlocked
);
1433 select_return_states_hook(LOCK_RESTORED
, &db_param_restore
);
1435 select_caller_info_hook(set_locked
, LOCKED
);
1436 select_caller_info_hook(set_half_locked
, HALF_LOCKED
);
1437 select_caller_info_hook(set_unlocked
, UNLOCKED
);
1439 return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL
, 0);