Don't print cross_func errors if the called function checks for NULL.
[smatch.git] / check_locking.c
blob42c06b05acd1e7aa23223715083066b74d4fddbb
1 /*
2 * sparse/check_locking.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
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.
17 #include "parse.h"
18 #include "smatch.h"
19 #include "smatch_slist.h"
21 static const char *lock_funcs[] = {
22 "_spin_lock",
23 "_spin_lock_irqsave",
24 "_spin_lock_irq",
25 "_spin_lock_bh",
26 "_spin_lock_nested",
27 "_spin_lock_irqsave_nested",
28 "_raw_spin_lock",
29 "_read_lock",
30 "_read_lock_irqsave",
31 "_read_lock_irq",
32 "_read_lock_bh",
33 "_write_lock",
34 "_write_lock_irqsave",
35 "_write_lock_irq",
36 "_write_lock_bh",
37 "down",
38 NULL,
41 static const char *unlock_funcs[] = {
42 "_spin_unlock",
43 "_spin_unlock_irqrestore",
44 "_spin_unlock_irq",
45 "_spin_unlock_bh",
46 "_raw_spin_unlock",
47 "_read_unlock",
48 "_read_unlock_irqrestore",
49 "_read_unlock_irq",
50 "_read_unlock_bh",
51 "_write_unlock",
52 "_write_unlock_irqrestore",
53 "_write_unlock_irq",
54 "_write_unlock_bh",
55 "up",
56 NULL,
59 struct locked_call {
60 const char *function;
61 const char *lock;
64 static struct locked_call lock_needed[] = {
65 {"tty_ldisc_ref_wait", "tty_ldisc_lock"},
68 static int my_id;
70 static struct tracker_list *starts_locked;
71 static struct tracker_list *starts_unlocked;
73 struct locks_on_return {
74 int line;
75 struct tracker_list *locked;
76 struct tracker_list *unlocked;
78 DECLARE_PTR_LIST(return_list, struct locks_on_return);
79 static struct return_list *all_returns;
81 STATE(locked);
82 STATE(unlocked);
84 static char kernel[] = "kernel";
85 static char *match_lock_func(char *fn_name, struct expression_list *args)
87 struct expression *lock_expr;
88 int i;
90 for (i = 0; lock_funcs[i]; i++) {
91 if (!strcmp(fn_name, lock_funcs[i])) {
92 lock_expr = get_argument_from_call_expr(args, 0);
93 return get_variable_from_expr(lock_expr, NULL);
96 if (!strcmp(fn_name, "lock_kernel"))
97 return kernel;
98 return NULL;
101 static char *match_unlock_func(char *fn_name, struct expression_list *args)
103 struct expression *lock_expr;
104 int i;
106 for (i = 0; unlock_funcs[i]; i++) {
107 if (!strcmp(fn_name, unlock_funcs[i])) {
108 lock_expr = get_argument_from_call_expr(args, 0);
109 return get_variable_from_expr(lock_expr, NULL);
112 if (!strcmp(fn_name, "unlock_kernel"))
113 return kernel;
114 return NULL;
117 static void check_locks_needed(const char *fn_name)
119 struct smatch_state *state;
120 int i;
122 for (i = 0; i < sizeof(lock_needed)/sizeof(struct locked_call); i++) {
123 if (!strcmp(fn_name, lock_needed[i].function)) {
124 state = get_state(lock_needed[i].lock, my_id, NULL);
125 if (state != &locked) {
126 smatch_msg("%s called without holding '%s' lock",
127 lock_needed[i].function,
128 lock_needed[i].lock);
134 static void match_call(struct expression *expr)
136 char *fn_name;
137 char *lock_name;
139 fn_name = get_variable_from_expr(expr->fn, NULL);
140 if (!fn_name)
141 return;
143 if ((lock_name = match_lock_func(fn_name, expr->args))) {
144 if (!get_state(lock_name, my_id, NULL))
145 add_tracker(&starts_unlocked, lock_name, my_id, NULL);
146 set_state(lock_name, my_id, NULL, &locked);
147 } else if ((lock_name = match_unlock_func(fn_name, expr->args))) {
148 if (!get_state(lock_name, my_id, NULL))
149 add_tracker(&starts_locked, lock_name, my_id, NULL);
150 set_state(lock_name, my_id, NULL, &unlocked);
151 } else
152 check_locks_needed(fn_name);
153 free_string(fn_name);
154 return;
157 static void match_condition(struct expression *expr)
159 /* __raw_spin_is_locked */
162 static struct locks_on_return *alloc_return(int line)
164 struct locks_on_return *ret;
166 ret = malloc(sizeof(*ret));
167 ret->line = line;
168 ret->locked = NULL;
169 ret->unlocked = NULL;
170 return ret;
173 static void match_return(struct statement *stmt)
175 struct locks_on_return *ret;
176 struct state_list *slist;
177 struct sm_state *tmp;
179 ret = alloc_return(get_lineno());
181 slist = get_all_states(my_id);
182 FOR_EACH_PTR(slist, tmp) {
183 if (tmp->state == &locked) {
184 add_tracker(&ret->locked, tmp->name, tmp->owner,
185 tmp->sym);
186 } else if (tmp->state == &unlocked) {
187 add_tracker(&ret->unlocked, tmp->name, tmp->owner,
188 tmp->sym);
189 } else {
190 smatch_msg("Unclear if '%s' is locked or unlocked.",
191 tmp->name);
193 } END_FOR_EACH_PTR(tmp);
194 add_ptr_list(&all_returns, ret);
197 static void check_returns_consistently(struct tracker *lock,
198 struct smatch_state *start)
200 int returns_locked = 0;
201 int returns_unlocked = 0;
202 struct locks_on_return *tmp;
204 FOR_EACH_PTR(all_returns, tmp) {
205 if (in_tracker_list(tmp->unlocked, lock->name, lock->owner,
206 lock->sym))
207 returns_unlocked = tmp->line;
208 else if (in_tracker_list(tmp->locked, lock->name, lock->owner,
209 lock->sym))
210 returns_locked = tmp->line;
211 else if (start == &locked)
212 returns_locked = tmp->line;
213 else if (start == &unlocked)
214 returns_unlocked = tmp->line;
215 } END_FOR_EACH_PTR(tmp);
217 if (returns_locked && returns_unlocked)
218 smatch_msg("Lock '%s' held on line %d but not on %d.",
219 lock->name, returns_locked, returns_unlocked);
223 static void clear_lists()
225 struct locks_on_return *tmp;
227 __free_ptr_list((struct ptr_list **)&starts_locked);
228 __free_ptr_list((struct ptr_list **)&starts_unlocked);
230 FOR_EACH_PTR(all_returns, tmp) {
231 __free_ptr_list((struct ptr_list **)&tmp->locked);
232 __free_ptr_list((struct ptr_list **)&tmp->unlocked);
233 } END_FOR_EACH_PTR(tmp);
234 __free_ptr_list((struct ptr_list **)&all_returns);
237 static void check_consistency(struct symbol *sym)
239 struct tracker *tmp;
241 FOR_EACH_PTR(starts_locked, tmp) {
242 if (in_tracker_list(starts_unlocked, tmp->name, tmp->owner,
243 tmp->sym))
244 smatch_msg("Locking inconsistency. We assume '%s' is "
245 "both locked and unlocked at the start.",
246 tmp->name);
247 } END_FOR_EACH_PTR(tmp);
249 FOR_EACH_PTR(starts_locked, tmp) {
250 check_returns_consistently(tmp, &locked);
251 } END_FOR_EACH_PTR(tmp);
253 FOR_EACH_PTR(starts_unlocked, tmp) {
254 check_returns_consistently(tmp, &unlocked);
255 } END_FOR_EACH_PTR(tmp);
257 clear_lists();
260 void register_locking(int id)
262 my_id = id;
263 add_hook(&match_call, FUNCTION_CALL_HOOK);
264 add_hook(&match_return, RETURN_HOOK);
265 add_hook(&check_consistency, END_FUNC_HOOK);