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
[] = {
27 "_spin_lock_irqsave_nested",
34 "_write_lock_irqsave",
43 static const char *unlock_funcs
[] = {
45 "_spin_unlock_irqrestore",
50 "_read_unlock_irqrestore",
54 "_write_unlock_irqrestore",
62 /* These are return 1 if they aquire the lock */
63 static const char *conditional_funcs
[] = {
68 "generic__raw_read_trylock",
74 "__raw_write_trylock",
75 "__raw_write_trylock",
80 /* These functions return 0 on success */
81 static const char *reverse_cond_funcs
[] = {
84 "mutex_lock_interruptible",
85 "mutex_lock_interruptible_nested",
86 "mutex_lock_killable",
87 "mutex_lock_killable_nested",
91 /* todo still need to handle__raw_spin_is_locked */
99 static struct locked_call lock_needed
[] = {
100 {"tty_ldisc_ref_wait", "tty_ldisc_lock"},
105 static struct tracker_list
*starts_locked
;
106 static struct tracker_list
*starts_unlocked
;
108 struct locks_on_return
{
110 struct tracker_list
*locked
;
111 struct tracker_list
*unlocked
;
113 DECLARE_PTR_LIST(return_list
, struct locks_on_return
);
114 static struct return_list
*all_returns
;
120 * merge_func() is used merging NULL and a state should be merge plus
121 * the state that the function was originally called with. This way
122 * we can sometimes avoid being &undefined.
124 static struct smatch_state
*merge_func(const char *name
, struct symbol
*sym
,
125 struct smatch_state
*s1
,
126 struct smatch_state
*s2
)
133 if (in_tracker_list(starts_locked
, name
, my_id
, sym
))
135 if (in_tracker_list(starts_unlocked
, name
, my_id
, sym
))
137 if (is_locked
&& is_unlocked
)
139 if (s2
== &locked
&& is_locked
)
141 if (s2
== &unlocked
&& is_unlocked
)
146 static char *match_func(const char *list
[], char *fn_name
,
147 struct expression_list
*args
)
149 struct expression
*lock_expr
;
152 for (i
= 0; list
[i
]; i
++) {
153 if (!strcmp(fn_name
, list
[i
])) {
154 lock_expr
= get_argument_from_call_expr(args
, 0);
155 return get_variable_from_expr(lock_expr
, NULL
);
161 static char kernel
[] = "kernel";
162 static char *match_lock_func(char *fn_name
, struct expression_list
*args
)
166 arg
= match_func(lock_funcs
, fn_name
, args
);
169 if (!strcmp(fn_name
, "lock_kernel"))
170 return alloc_string(kernel
);
174 static char *match_unlock_func(char *fn_name
, struct expression_list
*args
)
178 arg
= match_func(unlock_funcs
, fn_name
, args
);
181 if (!strcmp(fn_name
, "unlock_kernel"))
182 return alloc_string(kernel
);
186 static void check_locks_needed(const char *fn_name
)
188 struct smatch_state
*state
;
191 for (i
= 0; i
< sizeof(lock_needed
)/sizeof(struct locked_call
); i
++) {
192 if (!strcmp(fn_name
, lock_needed
[i
].function
)) {
193 state
= get_state(lock_needed
[i
].lock
, my_id
, NULL
);
194 if (state
!= &locked
) {
195 smatch_msg("error: %s called without holding '%s' lock",
196 lock_needed
[i
].function
,
197 lock_needed
[i
].lock
);
203 static void match_call(struct expression
*expr
)
209 fn_name
= get_variable_from_expr(expr
->fn
, NULL
);
213 if ((lock_name
= match_lock_func(fn_name
, expr
->args
))) {
214 sm
= get_sm_state(lock_name
, my_id
, NULL
);
216 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
217 if (sm
&& slist_has_state(sm
->possible
, &locked
))
218 smatch_msg("error: double lock '%s'", lock_name
);
219 set_state(lock_name
, my_id
, NULL
, &locked
);
220 } else if ((lock_name
= match_unlock_func(fn_name
, expr
->args
))) {
221 sm
= get_sm_state(lock_name
, my_id
, NULL
);
223 add_tracker(&starts_locked
, lock_name
, my_id
, NULL
);
224 if (sm
&& slist_has_state(sm
->possible
, &unlocked
))
225 smatch_msg("error: double unlock '%s'", lock_name
);
226 set_state(lock_name
, my_id
, NULL
, &unlocked
);
228 check_locks_needed(fn_name
);
229 free_string(lock_name
);
230 free_string(fn_name
);
234 static void match_condition(struct expression
*expr
)
239 if (expr
->type
!= EXPR_CALL
)
242 fn_name
= get_variable_from_expr(expr
->fn
, NULL
);
246 if ((lock_name
= match_func(conditional_funcs
, fn_name
, expr
->args
))) {
247 if (!get_state(lock_name
, my_id
, NULL
))
248 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
249 set_true_false_states(lock_name
, my_id
, NULL
, &locked
, &unlocked
);
251 if ((lock_name
= match_func(reverse_cond_funcs
, fn_name
, expr
->args
))) {
252 if (!get_state(lock_name
, my_id
, NULL
))
253 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
254 set_true_false_states(lock_name
, my_id
, NULL
, &unlocked
, &locked
);
256 free_string(lock_name
);
257 free_string(fn_name
);
261 static struct locks_on_return
*alloc_return(int line
)
263 struct locks_on_return
*ret
;
265 ret
= malloc(sizeof(*ret
));
268 ret
->unlocked
= NULL
;
272 static void check_possible(struct sm_state
*sm
)
274 struct sm_state
*tmp
;
279 FOR_EACH_PTR(sm
->possible
, tmp
) {
280 if (tmp
->state
== &locked
)
282 else if (tmp
->state
== &unlocked
)
284 else if (tmp
->state
== &undefined
)
286 } END_FOR_EACH_PTR(tmp
);
287 if ((islocked
&& isunlocked
) || undef
)
288 smatch_msg("warn: '%s' is sometimes locked here and "
289 "sometimes unlocked.", sm
->name
);
292 static void match_return(struct statement
*stmt
)
294 struct locks_on_return
*ret
;
295 struct state_list
*slist
;
296 struct sm_state
*tmp
;
298 ret
= alloc_return(get_lineno());
300 slist
= get_all_states(my_id
);
301 FOR_EACH_PTR(slist
, tmp
) {
302 if (tmp
->state
== &locked
) {
303 add_tracker(&ret
->locked
, tmp
->name
, tmp
->owner
,
305 } else if (tmp
->state
== &unlocked
) {
306 add_tracker(&ret
->unlocked
, tmp
->name
, tmp
->owner
,
311 } END_FOR_EACH_PTR(tmp
);
313 add_ptr_list(&all_returns
, ret
);
316 static void check_returns_consistently(struct tracker
*lock
,
317 struct smatch_state
*start
)
319 int returns_locked
= 0;
320 int returns_unlocked
= 0;
321 struct locks_on_return
*tmp
;
323 FOR_EACH_PTR(all_returns
, tmp
) {
324 if (in_tracker_list(tmp
->unlocked
, lock
->name
, lock
->owner
,
326 returns_unlocked
= tmp
->line
;
327 else if (in_tracker_list(tmp
->locked
, lock
->name
, lock
->owner
,
329 returns_locked
= tmp
->line
;
330 else if (start
== &locked
)
331 returns_locked
= tmp
->line
;
332 else if (start
== &unlocked
)
333 returns_unlocked
= tmp
->line
;
334 } END_FOR_EACH_PTR(tmp
);
336 if (returns_locked
&& returns_unlocked
)
337 smatch_msg("warn: lock '%s' held on line %d but not on %d.",
338 lock
->name
, returns_locked
, returns_unlocked
);
342 static void clear_lists()
344 struct locks_on_return
*tmp
;
346 free_trackers_and_list(&starts_locked
);
347 free_trackers_and_list(&starts_unlocked
);
349 FOR_EACH_PTR(all_returns
, tmp
) {
350 free_trackers_and_list(&tmp
->locked
);
351 free_trackers_and_list(&tmp
->unlocked
);
352 } END_FOR_EACH_PTR(tmp
);
353 __free_ptr_list((struct ptr_list
**)&all_returns
);
356 static void check_consistency(struct symbol
*sym
)
363 FOR_EACH_PTR(starts_locked
, tmp
) {
364 if (in_tracker_list(starts_unlocked
, tmp
->name
, tmp
->owner
,
366 smatch_msg("error: locking inconsistency. We assume "
367 "'%s' is both locked and unlocked at the "
370 } END_FOR_EACH_PTR(tmp
);
372 FOR_EACH_PTR(starts_locked
, tmp
) {
373 check_returns_consistently(tmp
, &locked
);
374 } END_FOR_EACH_PTR(tmp
);
376 FOR_EACH_PTR(starts_unlocked
, tmp
) {
377 check_returns_consistently(tmp
, &unlocked
);
378 } END_FOR_EACH_PTR(tmp
);
383 void register_locking(int id
)
386 add_merge_hook(my_id
, &merge_func
);
387 add_hook(&match_condition
, CONDITION_HOOK
);
388 add_hook(&match_call
, FUNCTION_CALL_HOOK
);
389 add_hook(&match_return
, RETURN_HOOK
);
390 add_hook(&check_consistency
, END_FUNC_HOOK
);