math: silence hard max false positives
[smatch.git] / check_rosenberg.c
blob2a9e3114132fcf3e0b27562bad2c23aceb4aec41
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 align = 0;
67 FOR_EACH_PTR(type->symbol_list, tmp) {
68 base_type = get_real_base_type(tmp);
69 if (base_type && base_type->type == SYM_STRUCT) {
70 if (check_struct(expr, base_type))
71 return 1;
73 if (type->ctype.attribute->is_packed)
74 continue;
76 if (!tmp->ctype.alignment) {
77 sm_msg("warn: cannot determine the alignment here\n");
78 } else if (align % tmp->ctype.alignment) {
79 print_holey_warning(expr, prev);
80 return 1;
83 if (base_type == &bool_ctype)
84 align += 1;
85 else if (type_bits(tmp) <= 0)
86 align = 0;
87 else
88 align += type_bytes(tmp);
90 if (tmp->ident)
91 prev = tmp->ident->name;
92 else
93 prev = NULL;
94 } END_FOR_EACH_PTR(tmp);
96 if (align % type->ctype.alignment) {
97 print_holey_warning(expr, prev);
98 return 1;
101 return 0;
104 static int warn_on_holey_struct(struct expression *expr)
106 struct symbol *type;
107 type = get_type(expr);
108 if (!type || type->type != SYM_STRUCT)
109 return 0;
111 return check_struct(expr, type);
114 static int has_global_scope(struct expression *expr)
116 struct symbol *sym;
118 if (expr->type != EXPR_SYMBOL)
119 return FALSE;
120 sym = expr->symbol;
121 if (!sym)
122 return FALSE;
123 return toplevel(sym->scope);
126 static int was_initialized(struct expression *expr)
128 struct symbol *sym;
129 char *name;
131 name = expr_to_var_sym(expr, &sym);
132 if (!name)
133 return 0;
134 if (sym->initializer)
135 return 1;
136 return 0;
139 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
141 struct expression *ptr;
142 int arg_no = PTR_INT(_arg_no);
144 ptr = get_argument_from_call_expr(expr->args, arg_no);
145 if (!ptr)
146 return;
147 ptr = strip_expr(ptr);
148 if (ptr->type != EXPR_PREOP || ptr->op != '&')
149 return;
150 ptr = strip_expr(ptr->unop);
151 set_state_expr(my_whole_id, ptr, &cleared);
154 static int was_memset(struct expression *expr)
156 if (get_state_expr(my_whole_id, expr) == &cleared)
157 return 1;
158 return 0;
161 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
163 char buf[256];
164 struct symbol *base;
166 base = get_base_type(member);
167 if (!base || base->type != SYM_BASETYPE || !member->ident)
168 return FALSE;
170 if (pointer)
171 snprintf(buf, 256, "%s->%s", name, member->ident->name);
172 else
173 snprintf(buf, 256, "%s.%s", name, member->ident->name);
175 if (get_state(my_member_id, buf, outer))
176 return TRUE;
178 return FALSE;
181 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
183 char buf[256];
184 struct symbol *base;
185 struct sm_state *sm;
187 base = get_base_type(member);
188 if (!base || base->type != SYM_BASETYPE || !member->ident)
189 return FALSE;
191 if (pointer)
192 snprintf(buf, 256, "%s->%s", name, member->ident->name);
193 else
194 snprintf(buf, 256, "%s.%s", name, member->ident->name);
196 sm = get_sm_state(my_member_id, buf, outer);
197 if (sm && !slist_has_state(sm->possible, &undefined))
198 return FALSE;
200 sm_msg("warn: check that '%s' doesn't leak information", buf);
201 return TRUE;
204 static int check_members_initialized(struct expression *expr)
206 char *name;
207 struct symbol *outer;
208 struct symbol *sym;
209 struct symbol *tmp;
210 int pointer = 0;
211 int printed = 0;
213 sym = get_type(expr);
214 if (sym && sym->type == SYM_PTR) {
215 pointer = 1;
216 sym = get_real_base_type(sym);
218 if (!sym)
219 return 0;
220 if (sym->type != SYM_STRUCT)
221 return 0;
223 name = expr_to_var_sym(expr, &outer);
226 * check that at least one member was set. If all of them were not set
227 * it's more likely a problem in the check than a problem in the kernel
228 * code.
230 FOR_EACH_PTR(sym->symbol_list, tmp) {
231 if (member_initialized(name, outer, tmp, pointer))
232 goto check;
233 } END_FOR_EACH_PTR(tmp);
234 goto out;
236 check:
237 FOR_EACH_PTR(sym->symbol_list, tmp) {
238 if (member_uninitialized(name, outer, tmp, pointer)) {
239 printed = 1;
240 goto out;
242 } END_FOR_EACH_PTR(tmp);
243 out:
244 free_string(name);
245 return printed;
248 static void check_was_initialized(struct expression *data)
250 data = strip_expr(data);
251 if (!data)
252 return;
253 if (data->type == EXPR_PREOP && data->op == '&')
254 data = strip_expr(data->unop);
255 if (data->type != EXPR_SYMBOL)
256 return;
258 if (has_global_scope(data))
259 return;
260 if (was_initialized(data))
261 return;
262 if (was_memset(data))
263 return;
264 if (warn_on_holey_struct(data))
265 return;
266 check_members_initialized(data);
269 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
271 int arg = PTR_INT(_arg);
272 struct expression *data;
274 data = get_argument_from_call_expr(expr->args, arg);
275 data = strip_expr(data);
276 if (!data)
277 return;
278 if (data->type != EXPR_PREOP || data->op != '&')
279 return;
280 check_was_initialized(data);
283 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
285 while (expr->type == EXPR_ASSIGNMENT)
286 expr = strip_expr(expr->right);
287 if (expr->type != EXPR_CALL)
288 return;
290 match_clear(NULL, expr, INT_PTR(param));
293 static void match_assign(struct expression *expr)
295 struct symbol *type;
297 type = get_type(expr->left);
298 if (!type || type->type != SYM_STRUCT)
299 return;
300 set_state_expr(my_whole_id, expr->left, &cleared);
303 static void register_clears_argument(void)
305 struct token *token;
306 const char *func;
307 int arg;
309 token = get_tokens_file("kernel.clears_argument");
310 if (!token)
311 return;
312 if (token_type(token) != TOKEN_STREAMBEGIN)
313 return;
314 token = token->next;
315 while (token_type(token) != TOKEN_STREAMEND) {
316 if (token_type(token) != TOKEN_IDENT)
317 return;
318 func = show_ident(token->ident);
319 token = token->next;
320 if (token_type(token) != TOKEN_NUMBER)
321 return;
322 arg = atoi(token->number);
324 add_function_hook(func, &match_clear, INT_PTR(arg));
325 token = token->next;
327 clear_token_alloc();
330 static void register_copy_funcs_from_file(void)
332 struct token *token;
333 const char *func;
334 int arg;
336 token = get_tokens_file("kernel.rosenberg_funcs");
337 if (!token)
338 return;
339 if (token_type(token) != TOKEN_STREAMBEGIN)
340 return;
341 token = token->next;
342 while (token_type(token) != TOKEN_STREAMEND) {
343 if (token_type(token) != TOKEN_IDENT)
344 return;
345 func = show_ident(token->ident);
346 token = token->next;
347 if (token_type(token) != TOKEN_NUMBER)
348 return;
349 arg = atoi(token->number);
350 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
351 token = token->next;
353 clear_token_alloc();
356 void check_rosenberg(int id)
358 if (option_project != PROJ_KERNEL)
359 return;
360 my_whole_id = id;
362 add_function_hook("memset", &match_clear, INT_PTR(0));
363 add_function_hook("memcpy", &match_clear, INT_PTR(0));
364 add_function_hook("memzero", &match_clear, INT_PTR(0));
365 add_function_hook("__memset", &match_clear, INT_PTR(0));
366 add_function_hook("__memcpy", &match_clear, INT_PTR(0));
367 add_function_hook("__memzero", &match_clear, INT_PTR(0));
368 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
369 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
371 add_hook(&match_assign, ASSIGNMENT_HOOK);
372 register_clears_argument();
373 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
375 register_copy_funcs_from_file();
378 void check_rosenberg2(int id)
380 if (option_project != PROJ_KERNEL)
381 return;
383 my_member_id = id;
384 add_extra_mod_hook(&extra_mod_hook);