2 * sparse/check_locking.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
11 * This test checks that locks are held the same across all returns.
13 * Of course, some functions are designed to only hold the locks on success.
14 * Oh well... We can rewrite it later if we want.
19 #include "smatch_slist.h"
21 static const char *lock_funcs
[] = {
28 "_spin_lock_irqsave_nested",
35 "_write_lock_irqsave",
44 static const char *unlock_funcs
[] = {
47 "_spin_unlock_irqrestore",
52 "_read_unlock_irqrestore",
56 "_write_unlock_irqrestore",
64 /* These are return 1 if they aquire the lock */
65 static const char *conditional_funcs
[] = {
70 "generic__raw_read_trylock",
76 "__raw_write_trylock",
77 "__raw_write_trylock",
82 /* These functions return 0 on success and negative on failure */
83 static const char *reverse_cond_funcs
[] = {
86 "mutex_lock_interruptible",
87 "mutex_lock_interruptible_nested",
88 "mutex_lock_killable",
89 "mutex_lock_killable_nested",
93 /* todo still need to handle__raw_spin_is_locked */
100 static struct locked_call lock_needed
[] = {
101 {"tty_ldisc_ref_wait", "tty_ldisc_lock"},
106 static struct tracker_list
*starts_locked
;
107 static struct tracker_list
*starts_unlocked
;
109 struct locks_on_return
{
111 struct tracker_list
*locked
;
112 struct tracker_list
*unlocked
;
114 DECLARE_PTR_LIST(return_list
, struct locks_on_return
);
115 static struct return_list
*all_returns
;
121 static struct smatch_state
*get_start_state(struct sm_state
*sm
)
126 if (in_tracker_list(starts_locked
, my_id
, sm
->name
, sm
->sym
))
128 if (in_tracker_list(starts_unlocked
, my_id
, sm
->name
, sm
->sym
))
130 if (is_locked
&& is_unlocked
)
139 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
144 static char *get_lock_name(struct expression
*expr
, void *data
)
146 struct expression
*lock_expr
;
149 return alloc_string((char *)data
);
151 lock_expr
= get_argument_from_call_expr(expr
->args
, 0);
152 return get_variable_from_expr(lock_expr
, NULL
);
156 static void match_lock_func(const char *fn
, struct expression
*expr
, void *data
)
161 lock_name
= get_lock_name(expr
, data
);
164 sm
= get_sm_state(my_id
, lock_name
, NULL
);
166 add_tracker(&starts_unlocked
, my_id
, lock_name
, NULL
);
167 if (sm
&& slist_has_state(sm
->possible
, &locked
))
168 sm_msg("error: double lock '%s'", lock_name
);
169 set_state(my_id
, lock_name
, NULL
, &locked
);
170 free_string(lock_name
);
173 static void match_unlock_func(const char *fn
, struct expression
*expr
,
179 lock_name
= get_lock_name(expr
, data
);
182 sm
= get_sm_state(my_id
, lock_name
, NULL
);
184 add_tracker(&starts_locked
, my_id
, lock_name
, NULL
);
185 if (sm
&& slist_has_state(sm
->possible
, &unlocked
))
186 sm_msg("error: double unlock '%s'", lock_name
);
187 set_state(my_id
, lock_name
, NULL
, &unlocked
);
188 free_string(lock_name
);
191 static void match_lock_failed(const char *fn
, struct expression
*expr
,
192 struct expression
*unused
, void *data
)
197 lock_name
= get_lock_name(expr
, data
);
200 sm
= get_sm_state(my_id
, lock_name
, NULL
);
202 add_tracker(&starts_unlocked
, my_id
, lock_name
, NULL
);
203 set_state(my_id
, lock_name
, NULL
, &unlocked
);
204 free_string(lock_name
);
207 static void match_lock_aquired(const char *fn
, struct expression
*expr
,
208 struct expression
*unused
, void *data
)
213 lock_name
= get_lock_name(expr
, data
);
216 sm
= get_sm_state(my_id
, lock_name
, NULL
);
218 add_tracker(&starts_unlocked
, my_id
, lock_name
, NULL
);
219 if (sm
&& slist_has_state(sm
->possible
, &locked
))
220 sm_msg("error: double lock '%s'", lock_name
);
221 set_state(my_id
, lock_name
, NULL
, &locked
);
222 free_string(lock_name
);
225 static void match_lock_needed(const char *fn
, struct expression
*expr
,
228 struct smatch_state
*state
;
231 state
= get_state(my_id
, (char *)data
, NULL
);
232 if (state
== &locked
)
234 fn_name
= get_variable_from_expr(expr
->fn
, NULL
);
236 sm_msg("Internal error.");
239 sm_msg("error: %s called without holding '%s' lock", fn_name
,
241 free_string(fn_name
);
244 static void match_locks_on_non_zero(const char *fn
, struct expression
*expr
,
250 lock_name
= get_lock_name(expr
, data
);
253 sm
= get_sm_state(my_id
, lock_name
, NULL
);
255 add_tracker(&starts_unlocked
, my_id
, lock_name
, NULL
);
256 set_true_false_states(my_id
, lock_name
, NULL
, &locked
, &unlocked
);
257 free_string(lock_name
);
260 static struct locks_on_return
*alloc_return(int line
)
262 struct locks_on_return
*ret
;
264 ret
= malloc(sizeof(*ret
));
267 ret
->unlocked
= NULL
;
271 static void check_possible(struct sm_state
*sm
)
273 struct sm_state
*tmp
;
278 FOR_EACH_PTR(sm
->possible
, tmp
) {
279 if (tmp
->state
== &locked
)
281 if (tmp
->state
== &unlocked
)
283 if (tmp
->state
== &start_state
) {
284 struct smatch_state
*s
;
286 s
= get_start_state(tmp
);
289 else if (s
== &unlocked
)
294 if (tmp
->state
== &undefined
)
295 undef
= 1; // i don't think this is possible any more.
296 } END_FOR_EACH_PTR(tmp
);
297 if ((islocked
&& isunlocked
) || undef
)
298 sm_msg("warn: '%s' is sometimes locked here and "
299 "sometimes unlocked.", sm
->name
);
302 static void match_return(struct expression
*ret_value
)
304 struct locks_on_return
*ret
;
305 struct state_list
*slist
;
306 struct sm_state
*tmp
;
311 ret
= alloc_return(get_lineno());
313 slist
= get_all_states(my_id
);
314 FOR_EACH_PTR(slist
, tmp
) {
315 if (tmp
->state
== &locked
) {
316 add_tracker(&ret
->locked
, tmp
->owner
, tmp
->name
,
318 } else if (tmp
->state
== &unlocked
) {
319 add_tracker(&ret
->unlocked
, tmp
->owner
, tmp
->name
,
321 } else if (tmp
->state
== &start_state
) {
322 struct smatch_state
*s
;
324 s
= get_start_state(tmp
);
326 add_tracker(&ret
->locked
, tmp
->owner
, tmp
->name
,
329 add_tracker(&ret
->unlocked
, tmp
->owner
,tmp
->name
,
334 } END_FOR_EACH_PTR(tmp
);
336 add_ptr_list(&all_returns
, ret
);
339 static void print_inconsistent_returns(struct tracker
*lock
,
340 struct smatch_state
*start
)
342 struct locks_on_return
*tmp
;
345 sm_printf("%s +%d %s(%d) ", get_filename(), get_lineno(), get_function(), get_func_pos());
346 sm_printf("warn: inconsistent returns %s:", lock
->name
);
347 sm_printf(" locked (");
349 FOR_EACH_PTR(all_returns
, tmp
) {
350 if (in_tracker_list(tmp
->unlocked
, lock
->owner
, lock
->name
, lock
->sym
))
352 if (in_tracker_list(tmp
->locked
, lock
->owner
, lock
->name
, lock
->sym
)) {
355 sm_printf("%d", tmp
->line
);
358 if (start
== &locked
) {
361 sm_printf("%d", tmp
->line
);
363 } END_FOR_EACH_PTR(tmp
);
365 sm_printf(") unlocked (");
367 FOR_EACH_PTR(all_returns
, tmp
) {
368 if (in_tracker_list(tmp
->unlocked
, lock
->owner
, lock
->name
, lock
->sym
)) {
371 sm_printf("%d", tmp
->line
);
374 if (in_tracker_list(tmp
->locked
, lock
->owner
, lock
->name
, lock
->sym
)) {
377 if (start
== &unlocked
) {
380 sm_printf("%d", tmp
->line
);
382 } END_FOR_EACH_PTR(tmp
);
386 static void check_returns_consistently(struct tracker
*lock
,
387 struct smatch_state
*start
)
389 int returns_locked
= 0;
390 int returns_unlocked
= 0;
391 struct locks_on_return
*tmp
;
393 FOR_EACH_PTR(all_returns
, tmp
) {
394 if (in_tracker_list(tmp
->unlocked
, lock
->owner
, lock
->name
,
396 returns_unlocked
= tmp
->line
;
397 else if (in_tracker_list(tmp
->locked
, lock
->owner
, lock
->name
,
399 returns_locked
= tmp
->line
;
400 else if (start
== &locked
)
401 returns_locked
= tmp
->line
;
402 else if (start
== &unlocked
)
403 returns_unlocked
= tmp
->line
;
404 } END_FOR_EACH_PTR(tmp
);
406 if (returns_locked
&& returns_unlocked
)
407 print_inconsistent_returns(lock
, start
);
410 static void check_consistency(struct symbol
*sym
)
417 FOR_EACH_PTR(starts_locked
, tmp
) {
418 if (in_tracker_list(starts_unlocked
, tmp
->owner
, tmp
->name
,
420 sm_msg("error: locking inconsistency. We assume "
421 "'%s' is both locked and unlocked at the "
424 } END_FOR_EACH_PTR(tmp
);
426 FOR_EACH_PTR(starts_locked
, tmp
) {
427 check_returns_consistently(tmp
, &locked
);
428 } END_FOR_EACH_PTR(tmp
);
430 FOR_EACH_PTR(starts_unlocked
, tmp
) {
431 check_returns_consistently(tmp
, &unlocked
);
432 } END_FOR_EACH_PTR(tmp
);
436 static void clear_lists(void)
438 struct locks_on_return
*tmp
;
440 free_trackers_and_list(&starts_locked
);
441 free_trackers_and_list(&starts_unlocked
);
443 FOR_EACH_PTR(all_returns
, tmp
) {
444 free_trackers_and_list(&tmp
->locked
);
445 free_trackers_and_list(&tmp
->unlocked
);
447 } END_FOR_EACH_PTR(tmp
);
448 __free_ptr_list((struct ptr_list
**)&all_returns
);
451 static void match_func_end(struct symbol
*sym
)
453 check_consistency(sym
);
457 void check_locking(int id
)
461 if (option_project
!= PROJ_KERNEL
)
465 add_unmatched_state_hook(my_id
, &unmatched_state
);
466 add_hook(&match_return
, RETURN_HOOK
);
467 add_hook(&match_func_end
, END_FUNC_HOOK
);
469 for (i
= 0; lock_funcs
[i
]; i
++) {
470 add_function_hook(lock_funcs
[i
], &match_lock_func
, NULL
);
472 add_function_hook("lock_kernel", &match_lock_func
, (void *)"kernel");
473 for (i
= 0; unlock_funcs
[i
]; i
++) {
474 add_function_hook(unlock_funcs
[i
], &match_unlock_func
, NULL
);
476 add_function_hook("unlock_kernel", &match_unlock_func
, (void *)"kernel");
477 for (i
= 0; i
< sizeof(lock_needed
)/sizeof(struct locked_call
); i
++) {
478 add_function_hook(lock_needed
[i
].function
, &match_lock_needed
,
479 (void *)lock_needed
[i
].lock
);
482 for (i
= 0; conditional_funcs
[i
]; i
++) {
483 add_conditional_hook(conditional_funcs
[i
],
484 &match_locks_on_non_zero
, NULL
);
487 for (i
= 0; reverse_cond_funcs
[i
]; i
++) {
488 return_implies_state(reverse_cond_funcs
[i
], whole_range
.min
, -1,
489 &match_lock_failed
, NULL
);
490 return_implies_state(reverse_cond_funcs
[i
], 0, 0,
491 &match_lock_aquired
, NULL
);