extra: handle merging NULL pointers better
[smatch.git] / check_rosenberg.c
blob5bcf85f4c2efd3a2e6114b8736e7e1d0caa672fc
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 smatch_state *state)
36 set_state(my_member_id, name, sym, state);
39 static void print_holey_warning(struct expression *data, const char *member)
41 char *name;
43 name = expr_to_str(data);
44 if (member) {
45 sm_msg("warn: check that '%s' doesn't leak information (struct has a hole after '%s')",
46 name, member);
47 } else {
48 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)",
49 name);
51 free_string(name);
54 static int check_struct(struct expression *expr, struct symbol *type)
56 struct symbol *tmp, *base_type;
57 const char *prev = NULL;
58 int align;
60 align = 0;
61 FOR_EACH_PTR(type->symbol_list, tmp) {
62 base_type = get_real_base_type(tmp);
63 if (base_type && base_type->type == SYM_STRUCT) {
64 if (check_struct(expr, base_type))
65 return 1;
67 if (type->ctype.attribute->is_packed)
68 continue;
70 if (!tmp->ctype.alignment) {
71 sm_msg("warn: cannot determine the alignment here\n");
72 } else if (align % tmp->ctype.alignment) {
73 print_holey_warning(expr, prev);
74 return 1;
77 if (base_type == &bool_ctype)
78 align += 1;
79 else if (type_bits(tmp) <= 0)
80 align = 0;
81 else
82 align += type_bytes(tmp);
84 if (tmp->ident)
85 prev = tmp->ident->name;
86 else
87 prev = NULL;
88 } END_FOR_EACH_PTR(tmp);
90 if (align % type->ctype.alignment) {
91 print_holey_warning(expr, prev);
92 return 1;
95 return 0;
98 static int warn_on_holey_struct(struct expression *expr)
100 struct symbol *type;
101 type = get_type(expr);
102 if (!type || type->type != SYM_STRUCT)
103 return 0;
105 return check_struct(expr, type);
108 static int has_global_scope(struct expression *expr)
110 struct symbol *sym;
112 if (expr->type != EXPR_SYMBOL)
113 return FALSE;
114 sym = expr->symbol;
115 return toplevel(sym->scope);
118 static int was_initialized(struct expression *expr)
120 struct symbol *sym;
121 char *name;
123 name = expr_to_var_sym(expr, &sym);
124 if (!name)
125 return 0;
126 if (sym->initializer)
127 return 1;
128 return 0;
131 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
133 struct expression *ptr;
134 int arg_no = PTR_INT(_arg_no);
136 ptr = get_argument_from_call_expr(expr->args, arg_no);
137 if (!ptr)
138 return;
139 if (ptr->type != EXPR_PREOP || ptr->op != '&')
140 return;
141 ptr = strip_expr(ptr->unop);
142 set_state_expr(my_whole_id, ptr, &cleared);
145 static int was_memset(struct expression *expr)
147 if (get_state_expr(my_whole_id, expr) == &cleared)
148 return 1;
149 return 0;
152 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
154 char buf[256];
155 struct symbol *base;
157 base = get_base_type(member);
158 if (!base || base->type != SYM_BASETYPE || !member->ident)
159 return FALSE;
161 if (pointer)
162 snprintf(buf, 256, "%s->%s", name, member->ident->name);
163 else
164 snprintf(buf, 256, "%s.%s", name, member->ident->name);
166 if (get_state(my_member_id, buf, outer))
167 return TRUE;
169 return FALSE;
172 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
174 char buf[256];
175 struct symbol *base;
176 struct sm_state *sm;
178 base = get_base_type(member);
179 if (!base || base->type != SYM_BASETYPE || !member->ident)
180 return FALSE;
182 if (pointer)
183 snprintf(buf, 256, "%s->%s", name, member->ident->name);
184 else
185 snprintf(buf, 256, "%s.%s", name, member->ident->name);
187 sm = get_sm_state(my_member_id, buf, outer);
188 if (sm && !slist_has_state(sm->possible, &undefined))
189 return FALSE;
191 sm_msg("warn: check that '%s' doesn't leak information", buf);
192 return TRUE;
195 static int check_members_initialized(struct expression *expr)
197 char *name;
198 struct symbol *outer;
199 struct symbol *sym;
200 struct symbol *tmp;
201 int pointer = 0;
202 int printed = 0;
204 sym = get_type(expr);
205 if (sym && sym->type == SYM_PTR) {
206 pointer = 1;
207 sym = get_real_base_type(sym);
209 if (!sym)
210 return 0;
211 if (sym->type != SYM_STRUCT)
212 return 0;
214 name = expr_to_var_sym(expr, &outer);
216 if (get_state(my_member_id, name, outer))
217 goto out;
220 * check that at least one member was set. If all of them were not set
221 * it's more likely a problem in the check than a problem in the kernel
222 * code.
224 FOR_EACH_PTR(sym->symbol_list, tmp) {
225 if (member_initialized(name, outer, tmp, pointer))
226 goto check;
227 } END_FOR_EACH_PTR(tmp);
228 goto out;
230 check:
231 FOR_EACH_PTR(sym->symbol_list, tmp) {
232 if (member_uninitialized(name, outer, tmp, pointer)) {
233 printed = 1;
234 goto out;
236 } END_FOR_EACH_PTR(tmp);
237 out:
238 free_string(name);
239 return printed;
242 static void check_was_initialized(struct expression *data)
244 data = strip_expr(data);
245 if (!data)
246 return;
247 if (data->type == EXPR_PREOP && data->op == '&')
248 data = strip_expr(data->unop);
249 if (data->type != EXPR_SYMBOL)
250 return;
252 if (has_global_scope(data))
253 return;
254 if (was_initialized(data))
255 return;
256 if (was_memset(data))
257 return;
258 if (warn_on_holey_struct(data))
259 return;
260 check_members_initialized(data);
263 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
265 int arg = PTR_INT(_arg);
266 struct expression *data;
268 data = get_argument_from_call_expr(expr->args, arg);
269 data = strip_expr(data);
270 if (!data)
271 return;
272 if (data->type != EXPR_PREOP || data->op != '&')
273 return;
274 check_was_initialized(data);
277 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
279 while (expr->type == EXPR_ASSIGNMENT)
280 expr = strip_expr(expr->right);
281 if (expr->type != EXPR_CALL)
282 return;
284 match_clear(NULL, expr, INT_PTR(param));
287 static void match_assign(struct expression *expr)
289 struct symbol *type;
291 type = get_type(expr->left);
292 if (!type || type->type != SYM_STRUCT)
293 return;
294 set_state_expr(my_whole_id, expr->left, &cleared);
297 static void register_clears_argument(void)
299 struct token *token;
300 const char *func;
301 int arg;
303 token = get_tokens_file("kernel.clears_argument");
304 if (!token)
305 return;
306 if (token_type(token) != TOKEN_STREAMBEGIN)
307 return;
308 token = token->next;
309 while (token_type(token) != TOKEN_STREAMEND) {
310 if (token_type(token) != TOKEN_IDENT)
311 return;
312 func = show_ident(token->ident);
313 token = token->next;
314 if (token_type(token) != TOKEN_NUMBER)
315 return;
316 arg = atoi(token->number);
318 add_function_hook(func, &match_clear, INT_PTR(arg));
319 token = token->next;
321 clear_token_alloc();
324 static void register_copy_funcs_from_file(void)
326 struct token *token;
327 const char *func;
328 int arg;
330 token = get_tokens_file("kernel.rosenberg_funcs");
331 if (!token)
332 return;
333 if (token_type(token) != TOKEN_STREAMBEGIN)
334 return;
335 token = token->next;
336 while (token_type(token) != TOKEN_STREAMEND) {
337 if (token_type(token) != TOKEN_IDENT)
338 return;
339 func = show_ident(token->ident);
340 token = token->next;
341 if (token_type(token) != TOKEN_NUMBER)
342 return;
343 arg = atoi(token->number);
344 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
345 token = token->next;
347 clear_token_alloc();
350 void check_rosenberg(int id)
352 if (option_project != PROJ_KERNEL)
353 return;
354 my_whole_id = id;
356 add_function_hook("memset", &match_clear, INT_PTR(0));
357 add_function_hook("memcpy", &match_clear, INT_PTR(0));
358 add_function_hook("__memzero", &match_clear, INT_PTR(0));
359 add_function_hook("memzero", &match_clear, INT_PTR(0));
360 add_hook(&match_assign, ASSIGNMENT_HOOK);
361 register_clears_argument();
362 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
364 register_copy_funcs_from_file();
367 void check_rosenberg2(int id)
369 if (option_project != PROJ_KERNEL)
370 return;
372 my_member_id = id;
373 add_extra_mod_hook(&extra_mod_hook);