extra: ignore self assignments "foo = foo;"
[smatch.git] / check_rosenberg.c
blob7284faf0c6ecc65a1d215e98e6c6fe2ea06741a3
1 /*
2 * Copyright (C) 2011 Dan Carpenter.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
18 /* Does a search for Dan Rosenberg style info leaks */
20 /* fixme: struct includes a struct with a hole in it */
21 /* function is called that clears the struct */
23 #include "scope.h"
24 #include "smatch.h"
25 #include "smatch_function_hashtable.h"
26 #include "smatch_slist.h"
27 #include "smatch_extra.h"
29 static int my_whole_id;
30 static int my_member_id;
31 static int skb_put_id;
33 STATE(cleared);
35 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
37 struct symbol *type;
39 type = get_real_base_type(sym);
40 if (!type || type->type != SYM_STRUCT)
41 return;
43 set_state(my_member_id, name, sym, state);
46 static void print_holey_warning(struct expression *data, const char *member)
48 char *name;
50 name = expr_to_str(data);
51 if (member) {
52 sm_warning("check that '%s' doesn't leak information (struct has a hole after '%s')",
53 name, member);
54 } else {
55 sm_warning("check that '%s' doesn't leak information (struct has holes)",
56 name);
58 free_string(name);
61 static int check_struct(struct expression *expr, struct symbol *type)
63 struct symbol *tmp, *base_type;
64 const char *prev = NULL;
65 int align;
67 if (type->ctype.alignment == 1)
68 return 0;
70 align = 0;
71 FOR_EACH_PTR(type->symbol_list, tmp) {
72 base_type = get_real_base_type(tmp);
73 if (base_type && base_type->type == SYM_STRUCT) {
74 if (check_struct(expr, base_type))
75 return 1;
78 if (!tmp->ctype.alignment) {
79 sm_perror("cannot determine the alignment here");
80 } else if (align % tmp->ctype.alignment) {
81 print_holey_warning(expr, prev);
82 return 1;
85 if (base_type == &bool_ctype)
86 align += 1;
87 else if (type_bits(tmp) <= 0)
88 align = 0;
89 else
90 align += type_bytes(tmp);
92 if (tmp->ident)
93 prev = tmp->ident->name;
94 else
95 prev = NULL;
96 } END_FOR_EACH_PTR(tmp);
98 if (align % type->ctype.alignment) {
99 print_holey_warning(expr, prev);
100 return 1;
103 return 0;
106 static int warn_on_holey_struct(struct expression *expr)
108 struct symbol *type;
109 type = get_type(expr);
110 if (!type || type->type != SYM_STRUCT)
111 return 0;
113 return check_struct(expr, type);
116 static int has_global_scope(struct expression *expr)
118 struct symbol *sym;
120 if (expr->type != EXPR_SYMBOL)
121 return FALSE;
122 sym = expr->symbol;
123 if (!sym)
124 return FALSE;
125 return toplevel(sym->scope);
128 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
130 struct expression *ptr, *tmp;
131 int arg_no = PTR_INT(_arg_no);
133 ptr = get_argument_from_call_expr(expr->args, arg_no);
134 if (!ptr)
135 return;
136 tmp = get_assigned_expr(ptr);
137 if (tmp)
138 ptr = tmp;
139 ptr = strip_expr(ptr);
140 if (ptr->type != EXPR_PREOP || ptr->op != '&')
141 return;
142 ptr = strip_expr(ptr->unop);
143 set_state_expr(my_whole_id, ptr, &cleared);
146 static int was_memset(struct expression *expr)
148 if (get_state_expr(my_whole_id, expr) == &cleared)
149 return 1;
150 return 0;
153 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
155 char buf[256];
156 struct symbol *base;
158 base = get_base_type(member);
159 if (!base || base->type != SYM_BASETYPE || !member->ident)
160 return FALSE;
162 if (pointer)
163 snprintf(buf, 256, "%s->%s", name, member->ident->name);
164 else
165 snprintf(buf, 256, "%s.%s", name, member->ident->name);
167 if (get_state(my_member_id, buf, outer))
168 return TRUE;
170 return FALSE;
173 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
175 char buf[256];
176 struct symbol *base;
177 struct sm_state *sm;
179 base = get_base_type(member);
180 if (!base || base->type != SYM_BASETYPE || !member->ident)
181 return FALSE;
183 if (pointer)
184 snprintf(buf, 256, "%s->%s", name, member->ident->name);
185 else
186 snprintf(buf, 256, "%s.%s", name, member->ident->name);
188 sm = get_sm_state(my_member_id, buf, outer);
189 if (sm && !slist_has_state(sm->possible, &undefined))
190 return FALSE;
192 sm_warning("check that '%s' doesn't leak information", buf);
193 return TRUE;
196 static int check_members_initialized(struct expression *expr)
198 char *name;
199 struct symbol *outer;
200 struct symbol *sym;
201 struct symbol *tmp;
202 int pointer = 0;
203 int printed = 0;
205 sym = get_type(expr);
206 if (sym && sym->type == SYM_PTR) {
207 pointer = 1;
208 sym = get_real_base_type(sym);
210 if (!sym)
211 return 0;
212 if (sym->type != SYM_STRUCT)
213 return 0;
215 name = expr_to_var_sym(expr, &outer);
218 * check that at least one member was set. If all of them were not set
219 * it's more likely a problem in the check than a problem in the kernel
220 * code.
222 FOR_EACH_PTR(sym->symbol_list, tmp) {
223 if (member_initialized(name, outer, tmp, pointer))
224 goto check;
225 } END_FOR_EACH_PTR(tmp);
226 goto out;
228 check:
229 FOR_EACH_PTR(sym->symbol_list, tmp) {
230 if (member_uninitialized(name, outer, tmp, pointer)) {
231 printed = 1;
232 goto out;
234 } END_FOR_EACH_PTR(tmp);
235 out:
236 free_string(name);
237 return printed;
240 static void check_was_initialized(struct expression *data)
242 data = strip_expr(data);
243 if (!data)
244 return;
245 if (data->type == EXPR_PREOP && data->op == '&')
246 data = strip_expr(data->unop);
247 if (data->type != EXPR_SYMBOL)
248 return;
250 if (has_global_scope(data))
251 return;
252 if (was_memset(data))
253 return;
254 if (warn_on_holey_struct(data))
255 return;
256 check_members_initialized(data);
259 static void check_skb_put(struct expression *data)
261 data = strip_expr(data);
262 if (!data)
263 return;
264 if (data->type == EXPR_PREOP && data->op == '&')
265 data = strip_expr(data->unop);
267 if (was_memset(data))
268 return;
269 if (warn_on_holey_struct(data))
270 return;
271 check_members_initialized(data);
274 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
276 int arg = PTR_INT(_arg);
277 struct expression *data;
279 data = get_argument_from_call_expr(expr->args, arg);
280 data = strip_expr(data);
281 if (!data)
282 return;
283 if (data->type != EXPR_PREOP || data->op != '&')
284 return;
285 check_was_initialized(data);
288 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
290 while (expr->type == EXPR_ASSIGNMENT)
291 expr = strip_expr(expr->right);
292 if (expr->type != EXPR_CALL)
293 return;
295 match_clear(NULL, expr, INT_PTR(param));
298 static struct smatch_state *alloc_expr_state(struct expression *expr)
300 struct smatch_state *state;
301 char *name;
303 name = expr_to_str(expr);
304 if (!name)
305 return NULL;
307 state = __alloc_smatch_state(0);
308 expr = strip_expr(expr);
309 state->name = alloc_sname(name);
310 free_string(name);
311 state->data = expr;
312 return state;
315 static void match_skb_put(const char *fn, struct expression *expr, void *unused)
317 struct symbol *type;
318 struct smatch_state *state;
320 type = get_type(expr->left);
321 type = get_real_base_type(type);
322 if (!type || type->type != SYM_STRUCT)
323 return;
324 state = alloc_expr_state(expr->left);
325 set_state_expr(skb_put_id, expr->left, state);
328 static void match_return_skb_put(struct expression *expr)
330 struct sm_state *sm;
331 struct stree *stree;
333 if (is_error_return(expr))
334 return;
336 stree = __get_cur_stree();
338 FOR_EACH_MY_SM(skb_put_id, stree, sm) {
339 check_skb_put(sm->state->data);
340 } END_FOR_EACH_SM(sm);
343 static void register_clears_argument(void)
345 struct token *token;
346 const char *func;
347 int arg;
349 token = get_tokens_file("kernel.clears_argument");
350 if (!token)
351 return;
352 if (token_type(token) != TOKEN_STREAMBEGIN)
353 return;
354 token = token->next;
355 while (token_type(token) != TOKEN_STREAMEND) {
356 if (token_type(token) != TOKEN_IDENT)
357 return;
358 func = show_ident(token->ident);
359 token = token->next;
360 if (token_type(token) != TOKEN_NUMBER)
361 return;
362 arg = atoi(token->number);
364 add_function_hook(func, &match_clear, INT_PTR(arg));
365 token = token->next;
367 clear_token_alloc();
370 static void register_copy_funcs_from_file(void)
372 struct token *token;
373 const char *func;
374 int arg;
376 token = get_tokens_file("kernel.rosenberg_funcs");
377 if (!token)
378 return;
379 if (token_type(token) != TOKEN_STREAMBEGIN)
380 return;
381 token = token->next;
382 while (token_type(token) != TOKEN_STREAMEND) {
383 if (token_type(token) != TOKEN_IDENT)
384 return;
385 func = show_ident(token->ident);
386 token = token->next;
387 if (token_type(token) != TOKEN_NUMBER)
388 return;
389 arg = atoi(token->number);
390 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
391 token = token->next;
393 clear_token_alloc();
396 void check_rosenberg(int id)
398 if (option_project != PROJ_KERNEL)
399 return;
400 my_whole_id = id;
402 add_function_hook("memset", &match_clear, INT_PTR(0));
403 add_function_hook("memcpy", &match_clear, INT_PTR(0));
404 add_function_hook("memzero", &match_clear, INT_PTR(0));
405 add_function_hook("__memset", &match_clear, INT_PTR(0));
406 add_function_hook("__memcpy", &match_clear, INT_PTR(0));
407 add_function_hook("__memzero", &match_clear, INT_PTR(0));
408 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
409 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
411 register_clears_argument();
412 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
414 register_copy_funcs_from_file();
417 void check_rosenberg2(int id)
419 if (option_project != PROJ_KERNEL)
420 return;
422 my_member_id = id;
423 set_dynamic_states(my_member_id);
424 add_extra_mod_hook(&extra_mod_hook);
427 void check_rosenberg3(int id)
429 if (option_project != PROJ_KERNEL)
430 return;
432 skb_put_id = id;
433 set_dynamic_states(skb_put_id);
434 add_function_assign_hook("skb_put", &match_skb_put, NULL);
435 add_hook(&match_return_skb_put, RETURN_HOOK);