unwind: add release_and_free_resource() and release_resource()
[smatch.git] / check_rosenberg.c
blobe7c3530c75d57e42b4e3babd65a1f0a13d31f997
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 *base_type, *prev_type;
64 struct symbol *tmp, *prev;
65 int align;
67 if (type->ctype.alignment == 1)
68 return 0;
70 align = 0;
71 prev = NULL;
72 prev_type = NULL;
73 FOR_EACH_PTR(type->symbol_list, tmp) {
74 base_type = get_real_base_type(tmp);
75 if (base_type && base_type->type == SYM_STRUCT) {
76 if (check_struct(expr, base_type))
77 return 1;
79 if (base_type && base_type->type == SYM_BITFIELD &&
80 prev_type && prev_type->type == SYM_BITFIELD)
81 goto next;
83 if (!tmp->ctype.alignment) {
84 sm_perror("cannot determine the alignment here");
85 } else if (align % tmp->ctype.alignment) {
87 print_holey_warning(expr, prev->ident ? prev->ident->name : "<unknown>");
88 return 1;
91 next:
92 if (base_type == &bool_ctype)
93 align += 1;
94 else if (type_bits(tmp) <= 0)
95 align = 0;
96 else
97 align += type_bytes(tmp);
99 prev = tmp;
100 prev_type = base_type;
101 } END_FOR_EACH_PTR(tmp);
103 // FIXME: this isn't the correct fix. See sbni_siocdevprivate().
104 if (prev_type && prev_type->type == SYM_BITFIELD)
105 return 0;
106 if (align % type->ctype.alignment) {
107 sm_msg("%s: tmp='%s' align=%d ctype.align=%ld type='%s'", __func__,
108 tmp->ident ? tmp->ident->name : "<unknown>",
109 align, tmp->ctype.alignment,
110 type_to_str(get_real_base_type(tmp)));
112 print_holey_warning(expr, (prev && prev->ident) ? prev->ident->name : "<unknown>");
113 return 1;
116 return 0;
119 static int warn_on_holey_struct(struct expression *expr)
121 struct symbol *type;
122 type = get_type(expr);
123 if (!type || type->type != SYM_STRUCT)
124 return 0;
126 return check_struct(expr, type);
129 static int has_global_scope(struct expression *expr)
131 struct symbol *sym;
133 if (expr->type != EXPR_SYMBOL)
134 return FALSE;
135 sym = expr->symbol;
136 if (!sym)
137 return FALSE;
138 return toplevel(sym->scope);
141 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
143 struct expression *ptr, *tmp;
144 int arg_no = PTR_INT(_arg_no);
146 ptr = get_argument_from_call_expr(expr->args, arg_no);
147 if (!ptr)
148 return;
149 tmp = get_assigned_expr(ptr);
150 if (tmp)
151 ptr = tmp;
152 ptr = strip_expr(ptr);
153 if (ptr->type != EXPR_PREOP || ptr->op != '&')
154 return;
155 ptr = strip_expr(ptr->unop);
156 set_state_expr(my_whole_id, ptr, &cleared);
159 static int was_memset(struct expression *expr)
161 if (get_state_expr(my_whole_id, expr) == &cleared)
162 return 1;
163 return 0;
166 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
168 char buf[256];
169 struct symbol *base;
171 base = get_base_type(member);
172 if (!base || base->type != SYM_BASETYPE || !member->ident)
173 return FALSE;
175 if (pointer)
176 snprintf(buf, 256, "%s->%s", name, member->ident->name);
177 else
178 snprintf(buf, 256, "%s.%s", name, member->ident->name);
180 if (get_state(my_member_id, buf, outer))
181 return TRUE;
183 return FALSE;
186 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
188 char buf[256];
189 struct symbol *base;
190 struct sm_state *sm;
192 base = get_base_type(member);
193 if (!base || base->type != SYM_BASETYPE || !member->ident)
194 return FALSE;
196 if (pointer)
197 snprintf(buf, 256, "%s->%s", name, member->ident->name);
198 else
199 snprintf(buf, 256, "%s.%s", name, member->ident->name);
201 sm = get_sm_state(my_member_id, buf, outer);
202 if (sm && !slist_has_state(sm->possible, &undefined))
203 return FALSE;
205 sm_warning("check that '%s' doesn't leak information", buf);
206 return TRUE;
209 static int check_members_initialized(struct expression *expr)
211 char *name;
212 struct symbol *outer;
213 struct symbol *sym;
214 struct symbol *tmp;
215 int pointer = 0;
216 int printed = 0;
218 sym = get_type(expr);
219 if (sym && sym->type == SYM_PTR) {
220 pointer = 1;
221 sym = get_real_base_type(sym);
223 if (!sym)
224 return 0;
225 if (sym->type != SYM_STRUCT)
226 return 0;
228 name = expr_to_var_sym(expr, &outer);
231 * check that at least one member was set. If all of them were not set
232 * it's more likely a problem in the check than a problem in the kernel
233 * code.
235 FOR_EACH_PTR(sym->symbol_list, tmp) {
236 if (member_initialized(name, outer, tmp, pointer))
237 goto check;
238 } END_FOR_EACH_PTR(tmp);
239 goto out;
241 check:
242 FOR_EACH_PTR(sym->symbol_list, tmp) {
243 if (member_uninitialized(name, outer, tmp, pointer)) {
244 printed = 1;
245 goto out;
247 } END_FOR_EACH_PTR(tmp);
248 out:
249 free_string(name);
250 return printed;
253 static void check_was_initialized(struct expression *data)
255 data = strip_expr(data);
256 if (!data)
257 return;
258 if (data->type == EXPR_PREOP && data->op == '&')
259 data = strip_expr(data->unop);
260 if (data->type != EXPR_SYMBOL)
261 return;
263 if (has_global_scope(data))
264 return;
265 if (was_memset(data))
266 return;
267 if (warn_on_holey_struct(data))
268 return;
269 check_members_initialized(data);
272 static void check_skb_put(struct expression *data)
274 data = strip_expr(data);
275 if (!data)
276 return;
277 if (data->type == EXPR_PREOP && data->op == '&')
278 data = strip_expr(data->unop);
280 if (was_memset(data))
281 return;
282 if (warn_on_holey_struct(data))
283 return;
284 check_members_initialized(data);
287 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
289 int arg = PTR_INT(_arg);
290 struct expression *data;
292 data = get_argument_from_call_expr(expr->args, arg);
293 data = strip_expr(data);
294 if (!data)
295 return;
296 if (data->type != EXPR_PREOP || data->op != '&')
297 return;
298 check_was_initialized(data);
301 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
303 while (expr->type == EXPR_ASSIGNMENT)
304 expr = strip_expr(expr->right);
305 if (expr->type != EXPR_CALL)
306 return;
308 match_clear(NULL, expr, INT_PTR(param));
311 static struct smatch_state *alloc_expr_state(struct expression *expr)
313 struct smatch_state *state;
314 char *name;
316 name = expr_to_str(expr);
317 if (!name)
318 return NULL;
320 state = __alloc_smatch_state(0);
321 expr = strip_expr(expr);
322 state->name = alloc_sname(name);
323 free_string(name);
324 state->data = expr;
325 return state;
328 static void match_skb_put(const char *fn, struct expression *expr, void *unused)
330 struct symbol *type;
331 struct smatch_state *state;
333 type = get_type(expr->left);
334 type = get_real_base_type(type);
335 if (!type || type->type != SYM_STRUCT)
336 return;
337 state = alloc_expr_state(expr->left);
338 set_state_expr(skb_put_id, expr->left, state);
341 static void match_return_skb_put(struct expression *expr)
343 struct sm_state *sm;
344 struct stree *stree;
346 if (is_error_return(expr))
347 return;
349 stree = __get_cur_stree();
351 FOR_EACH_MY_SM(skb_put_id, stree, sm) {
352 check_skb_put(sm->state->data);
353 } END_FOR_EACH_SM(sm);
356 static void register_clears_argument(void)
358 struct token *token;
359 const char *func;
360 int arg;
362 token = get_tokens_file("kernel.clears_argument");
363 if (!token)
364 return;
365 if (token_type(token) != TOKEN_STREAMBEGIN)
366 return;
367 token = token->next;
368 while (token_type(token) != TOKEN_STREAMEND) {
369 if (token_type(token) != TOKEN_IDENT)
370 return;
371 func = show_ident(token->ident);
372 token = token->next;
373 if (token_type(token) != TOKEN_NUMBER)
374 return;
375 arg = atoi(token->number);
377 add_function_hook(func, &match_clear, INT_PTR(arg));
378 token = token->next;
380 clear_token_alloc();
383 static void register_copy_funcs_from_file(void)
385 struct token *token;
386 const char *func;
387 int arg;
389 token = get_tokens_file("kernel.rosenberg_funcs");
390 if (!token)
391 return;
392 if (token_type(token) != TOKEN_STREAMBEGIN)
393 return;
394 token = token->next;
395 while (token_type(token) != TOKEN_STREAMEND) {
396 if (token_type(token) != TOKEN_IDENT)
397 return;
398 func = show_ident(token->ident);
399 token = token->next;
400 if (token_type(token) != TOKEN_NUMBER)
401 return;
402 arg = atoi(token->number);
403 add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
404 token = token->next;
406 clear_token_alloc();
409 void check_rosenberg(int id)
411 if (option_project != PROJ_KERNEL)
412 return;
413 my_whole_id = id;
415 add_function_hook("memset", &match_clear, INT_PTR(0));
416 add_function_hook("memcpy", &match_clear, INT_PTR(0));
417 add_function_hook("memzero", &match_clear, INT_PTR(0));
418 add_function_hook("__memset", &match_clear, INT_PTR(0));
419 add_function_hook("__memcpy", &match_clear, INT_PTR(0));
420 add_function_hook("__memzero", &match_clear, INT_PTR(0));
421 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
422 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
424 register_clears_argument();
425 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
427 register_copy_funcs_from_file();
430 void check_rosenberg2(int id)
432 if (option_project != PROJ_KERNEL)
433 return;
435 my_member_id = id;
436 set_dynamic_states(my_member_id);
437 add_extra_mod_hook(&extra_mod_hook);
440 void check_rosenberg3(int id)
442 if (option_project != PROJ_KERNEL)
443 return;
445 skb_put_id = id;
446 set_dynamic_states(skb_put_id);
447 add_function_assign_hook("skb_put", &match_skb_put, NULL);
448 add_hook(&match_return_skb_put, RETURN_HOOK);