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",
41 static const char *unlock_funcs
[] = {
43 "_spin_unlock_irqrestore",
48 "_read_unlock_irqrestore",
52 "_write_unlock_irqrestore",
59 static const char *conditional_funcs
[] = {
64 "generic__raw_read_trylock",
70 "__raw_write_trylock",
71 "__raw_write_trylock",
75 /* These functions return 0 on success */
76 static const char *reverse_cond_funcs
[] = {
82 /* todo still need to handle__raw_spin_is_locked */
90 static struct locked_call lock_needed
[] = {
91 {"tty_ldisc_ref_wait", "tty_ldisc_lock"},
96 static struct tracker_list
*starts_locked
;
97 static struct tracker_list
*starts_unlocked
;
99 struct locks_on_return
{
101 struct tracker_list
*locked
;
102 struct tracker_list
*unlocked
;
104 DECLARE_PTR_LIST(return_list
, struct locks_on_return
);
105 static struct return_list
*all_returns
;
110 static char *match_func(const char *list
[], char *fn_name
,
111 struct expression_list
*args
)
113 struct expression
*lock_expr
;
116 for (i
= 0; list
[i
]; i
++) {
117 if (!strcmp(fn_name
, list
[i
])) {
118 lock_expr
= get_argument_from_call_expr(args
, 0);
119 return get_variable_from_expr(lock_expr
, NULL
);
125 static char kernel
[] = "kernel";
126 static char *match_lock_func(char *fn_name
, struct expression_list
*args
)
130 arg
= match_func(lock_funcs
, fn_name
, args
);
133 if (!strcmp(fn_name
, "lock_kernel"))
134 return alloc_string(kernel
);
138 static char *match_unlock_func(char *fn_name
, struct expression_list
*args
)
142 arg
= match_func(unlock_funcs
, fn_name
, args
);
145 if (!strcmp(fn_name
, "unlock_kernel"))
146 return alloc_string(kernel
);
150 static void check_locks_needed(const char *fn_name
)
152 struct smatch_state
*state
;
155 for (i
= 0; i
< sizeof(lock_needed
)/sizeof(struct locked_call
); i
++) {
156 if (!strcmp(fn_name
, lock_needed
[i
].function
)) {
157 state
= get_state(lock_needed
[i
].lock
, my_id
, NULL
);
158 if (state
!= &locked
) {
159 smatch_msg("%s called without holding '%s' lock",
160 lock_needed
[i
].function
,
161 lock_needed
[i
].lock
);
167 static void match_call(struct expression
*expr
)
173 fn_name
= get_variable_from_expr(expr
->fn
, NULL
);
177 if ((lock_name
= match_lock_func(fn_name
, expr
->args
))) {
178 sm
= get_sm_state(lock_name
, my_id
, NULL
);
180 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
181 if (sm
&& slist_has_state(sm
->possible
, &locked
))
182 smatch_msg("double lock '%s'", lock_name
);
183 set_state(lock_name
, my_id
, NULL
, &locked
);
184 } else if ((lock_name
= match_unlock_func(fn_name
, expr
->args
))) {
185 sm
= get_sm_state(lock_name
, my_id
, NULL
);
187 add_tracker(&starts_locked
, lock_name
, my_id
, NULL
);
188 if (sm
&& slist_has_state(sm
->possible
, &unlocked
))
189 smatch_msg("double unlock '%s'", lock_name
);
190 set_state(lock_name
, my_id
, NULL
, &unlocked
);
192 check_locks_needed(fn_name
);
193 free_string(lock_name
);
194 free_string(fn_name
);
198 static void match_condition(struct expression
*expr
)
203 if (expr
->type
!= EXPR_CALL
)
206 fn_name
= get_variable_from_expr(expr
->fn
, NULL
);
210 if ((lock_name
= match_func(conditional_funcs
, fn_name
, expr
->args
))) {
211 if (!get_state(lock_name
, my_id
, NULL
))
212 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
213 set_true_false_states(lock_name
, my_id
, NULL
, &locked
, &unlocked
);
215 if ((lock_name
= match_func(reverse_cond_funcs
, fn_name
, expr
->args
))) {
216 if (!get_state(lock_name
, my_id
, NULL
))
217 add_tracker(&starts_unlocked
, lock_name
, my_id
, NULL
);
218 set_true_false_states(lock_name
, my_id
, NULL
, &unlocked
, &locked
);
220 free_string(lock_name
);
221 free_string(fn_name
);
225 static struct locks_on_return
*alloc_return(int line
)
227 struct locks_on_return
*ret
;
229 ret
= malloc(sizeof(*ret
));
232 ret
->unlocked
= NULL
;
236 static void check_possible(struct sm_state
*sm
)
238 struct sm_state
*tmp
;
243 FOR_EACH_PTR(sm
->possible
, tmp
) {
244 if (tmp
->state
== &locked
)
246 else if (tmp
->state
== &unlocked
)
248 else if (tmp
->state
== &undefined
)
250 } END_FOR_EACH_PTR(tmp
);
251 if (islocked
&& (isunlocked
|| undef
))
252 smatch_msg("Unclear if '%s' is locked or unlocked.", sm
->name
);
255 static void match_return(struct statement
*stmt
)
257 struct locks_on_return
*ret
;
258 struct state_list
*slist
;
259 struct sm_state
*tmp
;
261 ret
= alloc_return(get_lineno());
263 slist
= get_all_states(my_id
);
264 FOR_EACH_PTR(slist
, tmp
) {
265 if (tmp
->state
== &locked
) {
266 add_tracker(&ret
->locked
, tmp
->name
, tmp
->owner
,
268 } else if (tmp
->state
== &unlocked
) {
269 add_tracker(&ret
->unlocked
, tmp
->name
, tmp
->owner
,
274 } END_FOR_EACH_PTR(tmp
);
276 add_ptr_list(&all_returns
, ret
);
279 static void check_returns_consistently(struct tracker
*lock
,
280 struct smatch_state
*start
)
282 int returns_locked
= 0;
283 int returns_unlocked
= 0;
284 struct locks_on_return
*tmp
;
286 FOR_EACH_PTR(all_returns
, tmp
) {
287 if (in_tracker_list(tmp
->unlocked
, lock
->name
, lock
->owner
,
289 returns_unlocked
= tmp
->line
;
290 else if (in_tracker_list(tmp
->locked
, lock
->name
, lock
->owner
,
292 returns_locked
= tmp
->line
;
293 else if (start
== &locked
)
294 returns_locked
= tmp
->line
;
295 else if (start
== &unlocked
)
296 returns_unlocked
= tmp
->line
;
297 } END_FOR_EACH_PTR(tmp
);
299 if (returns_locked
&& returns_unlocked
)
300 smatch_msg("Lock '%s' held on line %d but not on %d.",
301 lock
->name
, returns_locked
, returns_unlocked
);
305 static void clear_lists()
307 struct locks_on_return
*tmp
;
309 free_trackers_and_list(&starts_locked
);
310 free_trackers_and_list(&starts_unlocked
);
312 FOR_EACH_PTR(all_returns
, tmp
) {
313 free_trackers_and_list(&tmp
->locked
);
314 free_trackers_and_list(&tmp
->unlocked
);
315 } END_FOR_EACH_PTR(tmp
);
316 __free_ptr_list((struct ptr_list
**)&all_returns
);
319 static void check_consistency(struct symbol
*sym
)
326 FOR_EACH_PTR(starts_locked
, tmp
) {
327 if (in_tracker_list(starts_unlocked
, tmp
->name
, tmp
->owner
,
329 smatch_msg("Locking inconsistency. We assume '%s' is "
330 "both locked and unlocked at the start.",
332 } END_FOR_EACH_PTR(tmp
);
334 FOR_EACH_PTR(starts_locked
, tmp
) {
335 check_returns_consistently(tmp
, &locked
);
336 } END_FOR_EACH_PTR(tmp
);
338 FOR_EACH_PTR(starts_unlocked
, tmp
) {
339 check_returns_consistently(tmp
, &unlocked
);
340 } END_FOR_EACH_PTR(tmp
);
345 void register_locking(int id
)
348 add_hook(&match_condition
, CONDITION_HOOK
);
349 add_hook(&match_call
, FUNCTION_CALL_HOOK
);
350 add_hook(&match_return
, RETURN_HOOK
);
351 add_hook(&check_consistency
, END_FUNC_HOOK
);