flow: don't print duplicate "unreachable code" warnings
[smatch.git] / check_rosenberg.c
blob55f77e0b69a8eee8ec53ad921529dfbdc679e4d1
1 /*
2 * sparse/check_rosenberg.c
4 * Copyright (C) 2011 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 /* Does a search for Dan Rosenberg style info leaks */
12 /* fixme: struct includes a struct with a hole in it */
13 /* function is called that clears the struct */
15 #include "scope.h"
16 #include "smatch.h"
17 #include "smatch_function_hashtable.h"
18 #include "smatch_slist.h"
19 #include "smatch_extra.h"
21 static int my_whole_id;
22 static int my_member_id;
24 STATE(cleared);
26 static void extra_mod_hook(const char *name, struct symbol *sym, struct smatch_state *state)
28 set_state(my_member_id, name, sym, state);
31 static void print_holey_warning(struct expression *data, const char *member)
33 char *name;
35 name = expr_to_str(data);
36 if (member) {
37 sm_msg("warn: check that '%s' doesn't leak information (struct has a hole after '%s')",
38 name, member);
39 } else {
40 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)",
41 name);
43 free_string(name);
46 static int check_struct(struct expression *expr, struct symbol *type)
48 struct symbol *tmp, *base_type;
49 const char *prev = NULL;
50 int align;
52 align = 0;
53 FOR_EACH_PTR(type->symbol_list, tmp) {
54 base_type = get_real_base_type(tmp);
55 if (base_type && base_type->type == SYM_STRUCT) {
56 if (check_struct(expr, base_type))
57 return 1;
59 if (type->ctype.attribute->is_packed)
60 continue;
62 if (!tmp->ctype.alignment) {
63 sm_msg("warn: cannot determine the alignment here\n");
64 } else if (align % tmp->ctype.alignment) {
65 print_holey_warning(expr, prev);
66 return 1;
69 if (base_type == &bool_ctype)
70 align += 1;
71 else if (tmp->bit_size <= 0)
72 align = 0;
73 else
74 align += bits_to_bytes(tmp->bit_size);
76 if (tmp->ident)
77 prev = tmp->ident->name;
78 else
79 prev = NULL;
80 } END_FOR_EACH_PTR(tmp);
82 return 0;
85 static int warn_on_holey_struct(struct expression *expr)
87 struct symbol *type;
88 type = get_type(expr);
89 if (!type || type->type != SYM_STRUCT)
90 return 0;
92 return check_struct(expr, type);
95 static int has_global_scope(struct expression *expr)
97 struct symbol *sym;
99 if (expr->type != EXPR_SYMBOL)
100 return FALSE;
101 sym = expr->symbol;
102 return toplevel(sym->scope);
105 static int was_initialized(struct expression *expr)
107 struct symbol *sym;
108 char *name;
110 name = expr_to_var_sym(expr, &sym);
111 if (!name)
112 return 0;
113 if (sym->initializer)
114 return 1;
115 return 0;
118 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
120 struct expression *ptr;
121 int arg_no = PTR_INT(_arg_no);
123 ptr = get_argument_from_call_expr(expr->args, arg_no);
124 if (!ptr)
125 return;
126 if (ptr->type != EXPR_PREOP || ptr->op != '&')
127 return;
128 ptr = strip_expr(ptr->unop);
129 set_state_expr(my_whole_id, ptr, &cleared);
132 static int was_memset(struct expression *expr)
134 if (get_state_expr(my_whole_id, expr) == &cleared)
135 return 1;
136 return 0;
139 static int member_initialized(char *name, struct symbol *outer, struct symbol *member)
141 char buf[256];
142 struct symbol *base;
144 base = get_base_type(member);
145 if (!base || base->type != SYM_BASETYPE || !member->ident)
146 return FALSE;
148 snprintf(buf, 256, "%s.%s", name, member->ident->name);
149 if (get_state(my_member_id, buf, outer))
150 return TRUE;
152 return FALSE;
155 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member)
157 char buf[256];
158 struct symbol *base;
159 struct sm_state *sm;
161 base = get_base_type(member);
162 if (!base || base->type != SYM_BASETYPE || !member->ident)
163 return FALSE;
165 snprintf(buf, 256, "%s.%s", name, member->ident->name);
166 sm = get_sm_state(my_member_id, buf, outer);
167 if (sm && !slist_has_state(sm->possible, &undefined))
168 return FALSE;
170 sm_msg("warn: check that '%s' doesn't leak information", buf);
171 return TRUE;
174 static void check_members_initialized(struct expression *expr)
176 char *name;
177 struct symbol *outer;
178 struct symbol *sym;
179 struct symbol *tmp;
181 sym = get_type(expr);
182 if (!sym || sym->type != SYM_STRUCT)
183 return;
185 name = expr_to_var_sym(expr, &outer);
187 if (get_state(my_member_id, name, outer))
188 goto out;
191 * check that at least one member was set. If all of them were not set
192 * it's more likely a problem in the check than a problem in the kernel
193 * code.
195 FOR_EACH_PTR(sym->symbol_list, tmp) {
196 if (member_initialized(name, outer, tmp))
197 goto check;
198 } END_FOR_EACH_PTR(tmp);
199 goto out;
201 check:
202 FOR_EACH_PTR(sym->symbol_list, tmp) {
203 if (member_uninitialized(name, outer, tmp))
204 goto out;
205 } END_FOR_EACH_PTR(tmp);
206 out:
207 free_string(name);
210 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
212 int arg = PTR_INT(_arg);
213 struct expression *data;
215 data = get_argument_from_call_expr(expr->args, arg);
216 if (!data)
217 return;
218 if (data->type != EXPR_PREOP || data->op != '&')
219 return;
221 data = strip_expr(data->unop);
222 if (data->type != EXPR_SYMBOL)
223 return;
225 if (has_global_scope(data))
226 return;
227 if (was_initialized(data))
228 return;
229 if (was_memset(data))
230 return;
231 if (warn_on_holey_struct(data))
232 return;
233 check_members_initialized(data);
236 static void register_clears_argument(void)
238 struct token *token;
239 const char *func;
240 int arg;
242 token = get_tokens_file("kernel.clears_argument");
243 if (!token)
244 return;
245 if (token_type(token) != TOKEN_STREAMBEGIN)
246 return;
247 token = token->next;
248 while (token_type(token) != TOKEN_STREAMEND) {
249 if (token_type(token) != TOKEN_IDENT)
250 return;
251 func = show_ident(token->ident);
252 token = token->next;
253 if (token_type(token) != TOKEN_NUMBER)
254 return;
255 arg = atoi(token->number);
257 add_function_hook(func, &match_clear, INT_PTR(arg));
258 token = token->next;
260 clear_token_alloc();
263 void check_rosenberg(int id)
265 if (option_project != PROJ_KERNEL)
266 return;
267 my_whole_id = id;
269 add_function_hook("memset", &match_clear, INT_PTR(0));
270 add_function_hook("memcpy", &match_clear, INT_PTR(0));
271 register_clears_argument();
273 add_function_hook("copy_to_user", &match_copy_to_user, INT_PTR(1));
274 add_function_hook("nla_put", &match_copy_to_user, INT_PTR(3));
277 void check_rosenberg2(int id)
279 my_member_id = id;
280 add_extra_mod_hook(&extra_mod_hook);