Warn when we don't acquire locks when calling certain functions.
[smatch.git] / check_locking.c
bloba6baae5bb1bd10887091ce31831ba85817ee5688
1 /*
2 * sparse/check_locking.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
11 * For this test let's look for functions that return a negative value
12 * with a spinlock held.
14 * One short coming is that it assumes a function isn't supposed
15 * to return negative with a lock held. Perhaps the function was
16 * called with the lock held. A more complicated script could check that.
20 #include "parse.h"
21 #include "smatch.h"
22 #include "smatch_slist.h"
24 static const char *lock_funcs[] = {
25 "_spin_lock",
26 "down",
27 NULL,
30 static const char *unlock_funcs[] = {
31 "_spin_unlock",
32 "up",
33 NULL,
36 struct locked_call {
37 const char *function;
38 const char *lock;
41 static struct locked_call lock_needed[] = {
42 {"tty_ldisc_ref_wait", "tty_ldisc_lock"},
45 static int my_id;
47 STATE(locked);
48 STATE(unlocked);
51 * merge_func() can go away when we fix the core to just store all the possible
52 * states.
54 * The parameters are passed in alphabetical order with NULL at the beginning
55 * of the alphabet. (s2 is never NULL).
58 static struct smatch_state *merge_func(const char *name, struct symbol *sym,
59 struct smatch_state *s1,
60 struct smatch_state *s2)
62 if (s1 == NULL)
63 return s2;
64 return &undefined;
68 static char *match_lock_func(char *fn_name, struct expression_list *args)
70 struct expression *lock_expr;
71 int i;
73 for (i = 0; lock_funcs[i]; i++) {
74 if (!strcmp(fn_name, lock_funcs[i])) {
75 lock_expr = get_argument_from_call_expr(args, 0);
76 return get_variable_from_expr(lock_expr, NULL);
79 if (!strcmp(fn_name, "lock_kernel"))
80 return "kernel";
81 return NULL;
84 static char *match_unlock_func(char *fn_name, struct expression_list *args)
86 struct expression *lock_expr;
87 int i;
89 for (i = 0; unlock_funcs[i]; i++) {
90 if (!strcmp(fn_name, unlock_funcs[i])) {
91 lock_expr = get_argument_from_call_expr(args, 0);
92 return get_variable_from_expr(lock_expr, NULL);
95 if (!strcmp(fn_name, "unlock_kernel"))
96 return "kernel";
97 return NULL;
100 static void check_locks_needed(const char *fn_name)
102 struct smatch_state *state;
103 int i;
105 for (i = 0; i < sizeof(lock_needed)/sizeof(struct locked_call); i++) {
106 if (!strcmp(fn_name, lock_needed[i].function)) {
107 state = get_state(lock_needed[i].lock, my_id, NULL);
108 if (state != &locked) {
109 smatch_msg("%s called without holding %s lock",
110 lock_needed[i].function,
111 lock_needed[i].lock);
117 static void match_call(struct expression *expr)
119 char *fn_name;
120 char *lock_name;
122 fn_name = get_variable_from_expr(expr->fn, NULL);
123 if (!fn_name)
124 return;
126 if ((lock_name = match_lock_func(fn_name, expr->args)))
127 set_state(lock_name, my_id, NULL, &locked);
128 else if ((lock_name = match_unlock_func(fn_name, expr->args)))
129 set_state(lock_name, my_id, NULL, &unlocked);
130 else
131 check_locks_needed(fn_name);
132 free_string(fn_name);
133 return;
136 static void match_condition(struct expression *expr)
138 /* __raw_spin_is_locked */
141 static int possibly_negative(struct expression *expr)
143 char *name;
144 struct symbol *sym;
145 struct state_list *slist;
146 struct sm_state *tmp;
148 name = get_variable_from_expr(expr, &sym);
149 if (!name || !sym)
150 return 0;
151 slist = get_possible_states(name, SMATCH_EXTRA, sym);
152 FOR_EACH_PTR(slist, tmp) {
153 int value = 0;
155 if (tmp->state->data)
156 value = *(int *)tmp->state->data;
158 if (value < 0) {
159 return 1;
161 } END_FOR_EACH_PTR(tmp);
162 return 0;
165 static void match_return(struct statement *stmt)
167 int ret_val;
168 struct state_list *slist;
169 struct sm_state *tmp;
171 ret_val = get_value(stmt->ret_value);
172 if (ret_val >= 0) {
173 return;
175 if (ret_val == UNDEFINED) {
176 if (!possibly_negative(stmt->ret_value))
177 return;
180 slist = get_all_states(my_id);
181 FOR_EACH_PTR(slist, tmp) {
182 if (tmp->state != &unlocked)
183 smatch_msg("returned negative with %s lock held",
184 tmp->name);
185 } END_FOR_EACH_PTR(tmp);
188 void register_locking(int id)
190 my_id = id;
191 add_merge_hook(my_id, &merge_func);
192 add_hook(&match_call, FUNCTION_CALL_HOOK);
193 add_hook(&match_return, RETURN_HOOK);