param_set: handle when the stack variable was modified better
[smatch.git] / check_rosenberg.c
blob01544cd15db113c8a50a87efaf791765765e25a8
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;
32 STATE(cleared);
34 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
36 struct symbol *type;
38 type = get_real_base_type(sym);
39 if (!type || type->type != SYM_STRUCT)
40 return;
42 set_state(my_member_id, name, sym, state);
45 static void print_holey_warning(struct expression *data, const char *member)
47 char *name;
49 name = expr_to_str(data);
50 if (member) {
51 sm_msg("warn: check that '%s' doesn't leak information (struct has a hole after '%s')",
52 name, member);
53 } else {
54 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)",
55 name);
57 free_string(name);
60 static int check_struct(struct expression *expr, struct symbol *type)
62 struct symbol *tmp, *base_type;
63 const char *prev = NULL;
64 int align;
66 if (type->ctype.alignment == 1)
67 return 0;
69 align = 0;
70 FOR_EACH_PTR(type->symbol_list, tmp) {
71 base_type = get_real_base_type(tmp);
72 if (base_type && base_type->type == SYM_STRUCT) {
73 if (check_struct(expr, base_type))
74 return 1;
77 if (!tmp->ctype.alignment) {
78 sm_msg("warn: cannot determine the alignment here\n");
79 } else if (align % tmp->ctype.alignment) {
80 print_holey_warning(expr, prev);
81 return 1;
84 if (base_type == &bool_ctype)
85 align += 1;
86 else if (type_bits(tmp) <= 0)
87 align = 0;
88 else
89 align += type_bytes(tmp);
91 if (tmp->ident)
92 prev = tmp->ident->name;
93 else
94 prev = NULL;
95 } END_FOR_EACH_PTR(tmp);
97 if (align % type->ctype.alignment) {
98 print_holey_warning(expr, prev);
99 return 1;
102 return 0;
105 static int warn_on_holey_struct(struct expression *expr)
107 struct symbol *type;
108 type = get_type(expr);
109 if (!type || type->type != SYM_STRUCT)
110 return 0;
112 return check_struct(expr, type);
115 static int has_global_scope(struct expression *expr)
117 struct symbol *sym;
119 if (expr->type != EXPR_SYMBOL)
120 return FALSE;
121 sym = expr->symbol;
122 if (!sym)
123 return FALSE;
124 return toplevel(sym->scope);
127 static int was_initialized(struct expression *expr)
129 struct symbol *sym;
130 char *name;
132 name = expr_to_var_sym(expr, &sym);
133 if (!name)
134 return 0;
135 if (sym->initializer)
136 return 1;
137 return 0;
140 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
142 struct expression *ptr;
143 int arg_no = PTR_INT(_arg_no);
145 ptr = get_argument_from_call_expr(expr->args, arg_no);
146 if (!ptr)
147 return;
148 ptr = strip_expr(ptr);
149 if (ptr->type != EXPR_PREOP || ptr->op != '&')
150 return;
151 ptr = strip_expr(ptr->unop);
152 set_state_expr(my_whole_id, ptr, &cleared);
155 static int was_memset(struct expression *expr)
157 if (get_state_expr(my_whole_id, expr) == &cleared)
158 return 1;
159 return 0;
162 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
164 char buf[256];
165 struct symbol *base;
167 base = get_base_type(member);
168 if (!base || base->type != SYM_BASETYPE || !member->ident)
169 return FALSE;
171 if (pointer)
172 snprintf(buf, 256, "%s->%s", name, member->ident->name);
173 else
174 snprintf(buf, 256, "%s.%s", name, member->ident->name);
176 if (get_state(my_member_id, buf, outer))
177 return TRUE;
179 return FALSE;
182 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
184 char buf[256];
185 struct symbol *base;
186 struct sm_state *sm;
188 base = get_base_type(member);
189 if (!base || base->type != SYM_BASETYPE || !member->ident)
190 return FALSE;
192 if (pointer)
193 snprintf(buf, 256, "%s->%s", name, member->ident->name);
194 else
195 snprintf(buf, 256, "%s.%s", name, member->ident->name);
197 sm = get_sm_state(my_member_id, buf, outer);
198 if (sm && !slist_has_state(sm->possible, &undefined))
199 return FALSE;
201 sm_msg("warn: check that '%s' doesn't leak information", buf);
202 return TRUE;
205 static int check_members_initialized(struct expression *expr)
207 char *name;
208 struct symbol *outer;
209 struct symbol *sym;
210 struct symbol *tmp;
211 int pointer = 0;
212 int printed = 0;
214 sym = get_type(expr);
215 if (sym && sym->type == SYM_PTR) {
216 pointer = 1;
217 sym = get_real_base_type(sym);
219 if (!sym)
220 return 0;
221 if (sym->type != SYM_STRUCT)
222 return 0;
224 name = expr_to_var_sym(expr, &outer);
227 * check that at least one member was set. If all of them were not set
228 * it's more likely a problem in the check than a problem in the kernel
229 * code.
231 FOR_EACH_PTR(sym->symbol_list, tmp) {
232 if (member_initialized(name, outer, tmp, pointer))
233 goto check;
234 } END_FOR_EACH_PTR(tmp);
235 goto out;
237 check:
238 FOR_EACH_PTR(sym->symbol_list, tmp) {
239 if (member_uninitialized(name, outer, tmp, pointer)) {
240 printed = 1;
241 goto out;
243 } END_FOR_EACH_PTR(tmp);
244 out:
245 free_string(name);
246 return printed;
249 static void check_was_initialized(struct expression *data)
251 data = strip_expr(data);
252 if (!data)
253 return;
254 if (data->type == EXPR_PREOP && data->op == '&')
255 data = strip_expr(data->unop);
256 if (data->type != EXPR_SYMBOL)
257 return;
259 if (has_global_scope(data))
260 return;
261 if (was_initialized(data))
262 return;
263 if (was_memset(data))
264 return;
265 if (warn_on_holey_struct(data))
266 return;
267 check_members_initialized(data);
270 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
272 int arg = PTR_INT(_arg);
273 struct expression *data;
275 data = get_argument_from_call_expr(expr->args, arg);
276 data = strip_expr(data);
277 if (!data)
278 return;
279 if (data->type != EXPR_PREOP || data->op != '&')
280 return;
281 check_was_initialized(data);
284 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
286 while (expr->type == EXPR_ASSIGNMENT)
287 expr = strip_expr(expr->right);
288 if (expr->type != EXPR_CALL)
289 return;
291 match_clear(NULL, expr, INT_PTR(param));
294 static void match_assign(struct expression *expr)
296 struct symbol *type;
298 type = get_type(expr->left);
299 if (!type || type->type != SYM_STRUCT)
300 return;
301 set_state_expr(my_whole_id, expr->left, &cleared);
304 static void register_clears_argument(void)
306 struct token *token;
307 const char *func;
308 int arg;
310 token = get_tokens_file("kernel.clears_argument");
311 if (!token)
312 return;
313 if (token_type(token) != TOKEN_STREAMBEGIN)
314 return;
315 token = token->next;
316 while (token_type(token) != TOKEN_STREAMEND) {
317 if (token_type(token) != TOKEN_IDENT)
318 return;
319 func = show_ident(token->ident);
320 token = token->next;
321 if (token_type(token) != TOKEN_NUMBER)
322 return;
323 arg = atoi(token->number);
325 add_function_hook(func, &match_clear, INT_PTR(arg));
326 token = token->next;
328 clear_token_alloc();
331 static void register_copy_funcs_from_file(void)
333 struct token *token;
334 const char *func;
335 int arg;
337 token = get_tokens_file("kernel.rosenberg_funcs");
338 if (!token)
339 return;
340 if (token_type(token) != TOKEN_STREAMBEGIN)
341 return;
342 token = token->next;
343 while (token_type(token) != TOKEN_STREAMEND) {
344 if (token_type(token) != TOKEN_IDENT)
345 return;
346 func = show_ident(token->ident);
347 token = token->next;
348 if (token_type(token) != TOKEN_NUMBER)
349 return;
350 arg = atoi(token->number);
351 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
352 token = token->next;
354 clear_token_alloc();
357 void check_rosenberg(int id)
359 if (option_project != PROJ_KERNEL)
360 return;
361 my_whole_id = id;
363 add_function_hook("memset", &match_clear, INT_PTR(0));
364 add_function_hook("memcpy", &match_clear, INT_PTR(0));
365 add_function_hook("memzero", &match_clear, INT_PTR(0));
366 add_function_hook("__memset", &match_clear, INT_PTR(0));
367 add_function_hook("__memcpy", &match_clear, INT_PTR(0));
368 add_function_hook("__memzero", &match_clear, INT_PTR(0));
369 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
370 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
372 add_hook(&match_assign, ASSIGNMENT_HOOK);
373 register_clears_argument();
374 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
376 register_copy_funcs_from_file();
379 void check_rosenberg2(int id)
381 if (option_project != PROJ_KERNEL)
382 return;
384 my_member_id = id;
385 add_extra_mod_hook(&extra_mod_hook);