buf_size: allow strncmp("foo", bar, 100) where 100 is larger than "foo"
[smatch.git] / check_rosenberg.c
blob5c265f774e46dcf5e9bed7ef4de5f058d9cf0d20
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 (tmp->bit_size <= 0)
80 align = 0;
81 else
82 align += bits_to_bytes(tmp->bit_size);
84 if (tmp->ident)
85 prev = tmp->ident->name;
86 else
87 prev = NULL;
88 } END_FOR_EACH_PTR(tmp);
90 return 0;
93 static int warn_on_holey_struct(struct expression *expr)
95 struct symbol *type;
96 type = get_type(expr);
97 if (!type || type->type != SYM_STRUCT)
98 return 0;
100 return check_struct(expr, type);
103 static int has_global_scope(struct expression *expr)
105 struct symbol *sym;
107 if (expr->type != EXPR_SYMBOL)
108 return FALSE;
109 sym = expr->symbol;
110 return toplevel(sym->scope);
113 static int was_initialized(struct expression *expr)
115 struct symbol *sym;
116 char *name;
118 name = expr_to_var_sym(expr, &sym);
119 if (!name)
120 return 0;
121 if (sym->initializer)
122 return 1;
123 return 0;
126 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
128 struct expression *ptr;
129 int arg_no = PTR_INT(_arg_no);
131 ptr = get_argument_from_call_expr(expr->args, arg_no);
132 if (!ptr)
133 return;
134 if (ptr->type != EXPR_PREOP || ptr->op != '&')
135 return;
136 ptr = strip_expr(ptr->unop);
137 set_state_expr(my_whole_id, ptr, &cleared);
140 static int was_memset(struct expression *expr)
142 if (get_state_expr(my_whole_id, expr) == &cleared)
143 return 1;
144 return 0;
147 static int member_initialized(char *name, struct symbol *outer, struct symbol *member)
149 char buf[256];
150 struct symbol *base;
152 base = get_base_type(member);
153 if (!base || base->type != SYM_BASETYPE || !member->ident)
154 return FALSE;
156 snprintf(buf, 256, "%s.%s", name, member->ident->name);
157 if (get_state(my_member_id, buf, outer))
158 return TRUE;
160 return FALSE;
163 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member)
165 char buf[256];
166 struct symbol *base;
167 struct sm_state *sm;
169 base = get_base_type(member);
170 if (!base || base->type != SYM_BASETYPE || !member->ident)
171 return FALSE;
173 snprintf(buf, 256, "%s.%s", name, member->ident->name);
174 sm = get_sm_state(my_member_id, buf, outer);
175 if (sm && !slist_has_state(sm->possible, &undefined))
176 return FALSE;
178 sm_msg("warn: check that '%s' doesn't leak information", buf);
179 return TRUE;
182 static int check_members_initialized(struct expression *expr)
184 char *name;
185 struct symbol *outer;
186 struct symbol *sym;
187 struct symbol *tmp;
188 int printed = 0;
190 sym = get_type(expr);
191 if (!sym || sym->type != SYM_STRUCT)
192 return 0;
194 name = expr_to_var_sym(expr, &outer);
196 if (get_state(my_member_id, name, outer))
197 goto out;
200 * check that at least one member was set. If all of them were not set
201 * it's more likely a problem in the check than a problem in the kernel
202 * code.
204 FOR_EACH_PTR(sym->symbol_list, tmp) {
205 if (member_initialized(name, outer, tmp))
206 goto check;
207 } END_FOR_EACH_PTR(tmp);
208 goto out;
210 check:
211 FOR_EACH_PTR(sym->symbol_list, tmp) {
212 if (member_uninitialized(name, outer, tmp)) {
213 printed = 1;
214 goto out;
216 } END_FOR_EACH_PTR(tmp);
217 out:
218 free_string(name);
219 return printed;
222 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
224 int arg = PTR_INT(_arg);
225 struct expression *data;
227 data = get_argument_from_call_expr(expr->args, arg);
228 data = strip_expr(data);
229 if (!data)
230 return;
231 if (data->type == EXPR_PREOP && data->op == '&')
232 data = strip_expr(data->unop);
233 if (data->type != EXPR_SYMBOL)
234 return;
236 if (has_global_scope(data))
237 return;
238 if (was_initialized(data))
239 return;
240 if (was_memset(data))
241 return;
242 if (warn_on_holey_struct(data))
243 return;
244 check_members_initialized(data);
247 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
249 while (expr->type == EXPR_ASSIGNMENT)
250 expr = strip_expr(expr->right);
251 if (expr->type != EXPR_CALL)
252 return;
254 match_clear(NULL, expr, INT_PTR(param));
257 static void register_clears_argument(void)
259 struct token *token;
260 const char *func;
261 int arg;
263 token = get_tokens_file("kernel.clears_argument");
264 if (!token)
265 return;
266 if (token_type(token) != TOKEN_STREAMBEGIN)
267 return;
268 token = token->next;
269 while (token_type(token) != TOKEN_STREAMEND) {
270 if (token_type(token) != TOKEN_IDENT)
271 return;
272 func = show_ident(token->ident);
273 token = token->next;
274 if (token_type(token) != TOKEN_NUMBER)
275 return;
276 arg = atoi(token->number);
278 add_function_hook(func, &match_clear, INT_PTR(arg));
279 token = token->next;
281 clear_token_alloc();
284 void check_rosenberg(int id)
286 if (option_project != PROJ_KERNEL)
287 return;
288 my_whole_id = id;
290 add_function_hook("memset", &match_clear, INT_PTR(0));
291 add_function_hook("memcpy", &match_clear, INT_PTR(0));
292 register_clears_argument();
294 add_function_hook("copy_to_user", &match_copy_to_user, INT_PTR(1));
295 add_function_hook("__copy_to_user", &match_copy_to_user, INT_PTR(1));
296 add_function_hook("nla_put", &match_copy_to_user, INT_PTR(3));
297 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
300 void check_rosenberg2(int id)
302 my_member_id = id;
303 add_extra_mod_hook(&extra_mod_hook);