dereference: fix a crash
[smatch.git] / check_rosenberg.c
blob22df8a3e5b64b9e1f172ddd7c3f33084464a1b3b
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 if (!cur_func_sym)
44 return;
46 if (name && strstr(name, "->"))
47 return;
49 set_state(my_member_id, name, sym, state);
52 static void print_holey_warning(struct expression *data, const char *member)
54 char *name;
56 name = expr_to_str(data);
57 if (member) {
58 sm_warning("check that '%s' doesn't leak information (struct has a hole after '%s')",
59 name, member);
60 } else {
61 sm_warning("check that '%s' doesn't leak information (struct has holes)",
62 name);
64 free_string(name);
67 static int check_struct(struct expression *expr, struct symbol *type)
69 struct symbol *base_type, *prev_type;
70 struct symbol *tmp, *prev;
71 int align;
73 if (type->ctype.alignment == 1)
74 return 0;
76 align = 0;
77 prev = NULL;
78 prev_type = NULL;
79 FOR_EACH_PTR(type->symbol_list, tmp) {
80 base_type = get_real_base_type(tmp);
81 if (base_type && base_type->type == SYM_STRUCT) {
82 if (check_struct(expr, base_type))
83 return 1;
85 if (base_type && base_type->type == SYM_BITFIELD &&
86 prev_type && prev_type->type == SYM_BITFIELD)
87 goto next;
89 if (!tmp->ctype.alignment) {
90 sm_perror("cannot determine the alignment here");
91 } else if (align % tmp->ctype.alignment) {
93 print_holey_warning(expr, prev->ident ? prev->ident->name : "<unknown>");
94 return 1;
97 next:
98 if (base_type == &bool_ctype)
99 align += 1;
100 else if (type_bits(tmp) <= 0)
101 align = 0;
102 else
103 align += type_bytes(tmp);
105 prev = tmp;
106 prev_type = base_type;
107 } END_FOR_EACH_PTR(tmp);
109 // FIXME: this isn't the correct fix. See sbni_siocdevprivate().
110 if (prev_type && prev_type->type == SYM_BITFIELD)
111 return 0;
112 if (align % type->ctype.alignment) {
113 sm_msg("%s: tmp='%s' align=%d ctype.align=%ld type='%s'", __func__,
114 tmp->ident ? tmp->ident->name : "<unknown>",
115 align, tmp->ctype.alignment,
116 type_to_str(get_real_base_type(tmp)));
118 print_holey_warning(expr, (prev && prev->ident) ? prev->ident->name : "<unknown>");
119 return 1;
122 return 0;
125 static int warn_on_holey_struct(struct expression *expr)
127 struct symbol *type;
128 type = get_type(expr);
129 if (!type || type->type != SYM_STRUCT)
130 return 0;
132 return check_struct(expr, type);
135 static int has_global_scope(struct expression *expr)
137 struct symbol *sym;
139 if (expr->type != EXPR_SYMBOL)
140 return FALSE;
141 sym = expr->symbol;
142 if (!sym)
143 return FALSE;
144 return toplevel(sym->scope);
147 static int was_initialized(struct expression *expr)
149 struct symbol *sym, *type;
151 sym = expr_to_sym(expr);
152 if (!sym)
153 return 0;
154 if (!sym->initializer)
155 return 0;
157 type = get_real_base_type(sym);
158 if (!type)
159 return 0;
160 if (type->type != SYM_STRUCT)
161 return 1;
163 /* Fully initializing a struct does not clear the holes */
164 if (sym->initializer->type != EXPR_INITIALIZER)
165 return 0;
166 if (ptr_list_size((struct ptr_list *)sym->initializer->expr_list) >=
167 ptr_list_size((struct ptr_list *)type->symbol_list))
168 return 0;
170 return 1;
173 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
175 struct expression *ptr, *tmp;
176 int arg_no = PTR_INT(_arg_no);
178 ptr = get_argument_from_call_expr(expr->args, arg_no);
179 if (!ptr)
180 return;
181 tmp = get_assigned_expr(ptr);
182 if (tmp)
183 ptr = tmp;
184 ptr = strip_expr(ptr);
185 if (ptr->type != EXPR_PREOP || ptr->op != '&')
186 return;
187 ptr = strip_expr(ptr->unop);
188 set_state_expr(my_whole_id, ptr, &cleared);
191 static int was_memset(struct expression *expr)
193 if (get_state_expr(my_whole_id, expr) == &cleared)
194 return 1;
195 return 0;
198 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
200 char buf[256];
201 struct symbol *base;
203 base = get_base_type(member);
204 if (!base || base->type != SYM_BASETYPE || !member->ident)
205 return FALSE;
207 if (pointer)
208 snprintf(buf, 256, "%s->%s", name, member->ident->name);
209 else
210 snprintf(buf, 256, "%s.%s", name, member->ident->name);
212 if (get_state(my_member_id, buf, outer))
213 return TRUE;
215 return FALSE;
218 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
220 char buf[256];
221 struct symbol *base;
222 struct sm_state *sm;
224 base = get_base_type(member);
225 if (!base || base->type != SYM_BASETYPE || !member->ident)
226 return FALSE;
228 if (pointer)
229 snprintf(buf, 256, "%s->%s", name, member->ident->name);
230 else
231 snprintf(buf, 256, "%s.%s", name, member->ident->name);
233 sm = get_sm_state(my_member_id, buf, outer);
234 if (sm && !slist_has_state(sm->possible, &undefined))
235 return FALSE;
237 sm_warning("check that '%s' doesn't leak information", buf);
238 return TRUE;
241 static int check_members_initialized(struct expression *expr)
243 char *name;
244 struct symbol *outer;
245 struct symbol *sym;
246 struct symbol *tmp;
247 int pointer = 0;
248 int printed = 0;
250 sym = get_type(expr);
251 if (sym && sym->type == SYM_PTR) {
252 pointer = 1;
253 sym = get_real_base_type(sym);
255 if (!sym)
256 return 0;
257 if (sym->type != SYM_STRUCT)
258 return 0;
260 name = expr_to_var_sym(expr, &outer);
263 * check that at least one member was set. If all of them were not set
264 * it's more likely a problem in the check than a problem in the kernel
265 * code.
267 FOR_EACH_PTR(sym->symbol_list, tmp) {
268 if (member_initialized(name, outer, tmp, pointer))
269 goto check;
270 } END_FOR_EACH_PTR(tmp);
271 goto out;
273 check:
274 FOR_EACH_PTR(sym->symbol_list, tmp) {
275 if (member_uninitialized(name, outer, tmp, pointer)) {
276 printed = 1;
277 goto out;
279 } END_FOR_EACH_PTR(tmp);
280 out:
281 free_string(name);
282 return printed;
285 static void check_was_initialized(struct expression *data)
287 data = strip_expr(data);
288 if (!data)
289 return;
290 if (data->type == EXPR_PREOP && data->op == '&')
291 data = strip_expr(data->unop);
292 if (data->type != EXPR_SYMBOL)
293 return;
295 if (has_global_scope(data))
296 return;
297 if (was_initialized(data))
298 return;
299 if (was_memset(data))
300 return;
301 if (warn_on_holey_struct(data))
302 return;
303 check_members_initialized(data);
306 static void check_skb_put(struct expression *data)
308 data = strip_expr(data);
309 if (!data)
310 return;
311 if (data->type == EXPR_PREOP && data->op == '&')
312 data = strip_expr(data->unop);
314 if (was_memset(data))
315 return;
316 if (warn_on_holey_struct(data))
317 return;
318 check_members_initialized(data);
321 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
323 int arg = PTR_INT(_arg);
324 struct expression *data;
326 data = get_argument_from_call_expr(expr->args, arg);
327 data = strip_expr(data);
328 if (!data)
329 return;
330 if (data->type != EXPR_PREOP || data->op != '&')
331 return;
332 check_was_initialized(data);
335 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
337 while (expr->type == EXPR_ASSIGNMENT)
338 expr = strip_expr(expr->right);
339 if (expr->type != EXPR_CALL)
340 return;
342 match_clear(NULL, expr, INT_PTR(param));
345 static struct smatch_state *alloc_expr_state(struct expression *expr)
347 struct smatch_state *state;
348 char *name;
350 name = expr_to_str(expr);
351 if (!name)
352 return NULL;
354 state = __alloc_smatch_state(0);
355 expr = strip_expr(expr);
356 state->name = alloc_sname(name);
357 free_string(name);
358 state->data = expr;
359 return state;
362 static void match_skb_put(const char *fn, struct expression *expr, void *unused)
364 struct symbol *type;
365 struct smatch_state *state;
367 type = get_type(expr->left);
368 type = get_real_base_type(type);
369 if (!type || type->type != SYM_STRUCT)
370 return;
371 state = alloc_expr_state(expr->left);
372 set_state_expr(skb_put_id, expr->left, state);
375 static void match_return_skb_put(struct expression *expr)
377 struct sm_state *sm;
378 struct stree *stree;
380 if (is_error_return(expr))
381 return;
383 stree = __get_cur_stree();
385 FOR_EACH_MY_SM(skb_put_id, stree, sm) {
386 check_skb_put(sm->state->data);
387 } END_FOR_EACH_SM(sm);
390 static void register_clears_argument(void)
392 struct token *token;
393 const char *func;
394 int arg;
396 token = get_tokens_file("kernel.clears_argument");
397 if (!token)
398 return;
399 if (token_type(token) != TOKEN_STREAMBEGIN)
400 return;
401 token = token->next;
402 while (token_type(token) != TOKEN_STREAMEND) {
403 if (token_type(token) != TOKEN_IDENT)
404 return;
405 func = show_ident(token->ident);
406 token = token->next;
407 if (token_type(token) != TOKEN_NUMBER)
408 return;
409 arg = atoi(token->number);
411 add_function_hook(func, &match_clear, INT_PTR(arg));
412 token = token->next;
414 clear_token_alloc();
417 static void register_copy_funcs_from_file(void)
419 struct token *token;
420 const char *func;
421 int arg;
423 token = get_tokens_file("kernel.rosenberg_funcs");
424 if (!token)
425 return;
426 if (token_type(token) != TOKEN_STREAMBEGIN)
427 return;
428 token = token->next;
429 while (token_type(token) != TOKEN_STREAMEND) {
430 if (token_type(token) != TOKEN_IDENT)
431 return;
432 func = show_ident(token->ident);
433 token = token->next;
434 if (token_type(token) != TOKEN_NUMBER)
435 return;
436 arg = atoi(token->number);
437 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
438 token = token->next;
440 clear_token_alloc();
443 void check_rosenberg(int id)
445 if (option_project != PROJ_KERNEL)
446 return;
447 my_whole_id = id;
449 add_function_hook("memset", &match_clear, INT_PTR(0));
450 add_function_hook("memcpy", &match_clear, INT_PTR(0));
451 add_function_hook("memzero", &match_clear, INT_PTR(0));
452 add_function_hook("__memset", &match_clear, INT_PTR(0));
453 add_function_hook("__memcpy", &match_clear, INT_PTR(0));
454 add_function_hook("__memzero", &match_clear, INT_PTR(0));
455 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
456 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
458 register_clears_argument();
459 select_return_states_hook(BUF_CLEARED, &db_param_cleared);
461 register_copy_funcs_from_file();
464 void check_rosenberg2(int id)
466 if (option_project != PROJ_KERNEL)
467 return;
469 my_member_id = id;
470 set_dynamic_states(my_member_id);
471 add_extra_mod_hook(&extra_mod_hook);
474 void check_rosenberg3(int id)
476 if (option_project != PROJ_KERNEL)
477 return;
479 skb_put_id = id;
480 set_dynamic_states(skb_put_id);
481 add_function_assign_hook("skb_put", &match_skb_put, NULL);
482 add_hook(&match_return_skb_put, RETURN_HOOK);