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