introduce: get_dinfo()
[smatch.git] / check_overflow.c
blob090819575d38ff81f0bcb20ab7cc0b68860de952
1 /*
2 * sparse/check_deference.c
4 * Copyright (C) 2006 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include <stdlib.h>
11 #include "parse.h"
12 #include "smatch.h"
13 #include "smatch_slist.h"
14 #include "smatch_extra.h"
16 struct bound {
17 int param;
18 int size;
21 static int my_decl_id;
22 static int my_used_id;
24 static struct symbol *this_func;
26 static void match_function_def(struct symbol *sym)
28 this_func = sym;
31 static void print_args(struct expression *expr, int size)
33 struct symbol *sym;
34 char *name;
35 struct symbol *arg;
36 const char *arg_name;
37 int i;
39 if (!option_spammy)
40 return;
42 name = get_variable_from_expr(expr, &sym);
43 if (!name || !sym)
44 goto free;
46 i = 0;
47 FOR_EACH_PTR(this_func->ctype.base_type->arguments, arg) {
48 arg_name = (arg->ident?arg->ident->name:"-");
49 if (sym == arg && !strcmp(name, arg_name)) {
50 sm_info("param %d array index. size %d", i, size);
51 goto free;
53 i++;
54 } END_FOR_EACH_PTR(arg);
55 free:
56 free_string(name);
59 static char *alloc_num(long long num)
61 static char buff[256];
63 if (num == whole_range.min) {
64 snprintf(buff, 255, "min");
65 } else if (num == whole_range.max) {
66 snprintf(buff, 255, "max");
67 } else if (num < 0) {
68 snprintf(buff, 255, "(%lld)", num);
69 } else {
70 snprintf(buff, 255, "%lld", num);
72 buff[255] = '\0';
73 return alloc_sname(buff);
76 static void delete(const char *name, struct symbol *sym, struct expression *expr, void *unused)
78 delete_state(my_used_id, name, sym);
81 static struct smatch_state *alloc_my_state(int val)
83 struct smatch_state *state;
85 state = malloc(sizeof(*state));
86 state->name = alloc_num(val);
87 state->data = malloc(sizeof(int));
88 *(int *)state->data = val;
89 return state;
92 static void match_declaration(struct symbol *sym)
94 struct symbol *base_type;
95 char *name;
96 int size;
98 if (!sym->ident)
99 return;
101 name = sym->ident->name;
102 base_type = get_base_type(sym);
104 if (base_type->type == SYM_ARRAY && base_type->bit_size > 0) {
105 set_state(my_decl_id, name, NULL, alloc_my_state(base_type->bit_size));
106 } else {
107 if (sym->initializer &&
108 sym->initializer->type == EXPR_STRING &&
109 sym->initializer->string) {
110 size = sym->initializer->string->length * 8;
111 set_state(my_decl_id, name, NULL, alloc_my_state(size));
116 static int is_last_struct_member(struct expression *expr)
118 struct ident *member;
119 struct symbol *struct_sym;
120 struct symbol *tmp;
122 if (!expr || expr->type != EXPR_DEREF)
123 return 0;
125 member = expr->member;
126 struct_sym = get_type(expr->deref);
127 if (!struct_sym)
128 return 0;
129 if (struct_sym->type == SYM_PTR)
130 struct_sym = get_base_type(struct_sym);
131 FOR_EACH_PTR_REVERSE(struct_sym->symbol_list, tmp) {
132 if (tmp->ident == member)
133 return 1;
134 return 0;
135 } END_FOR_EACH_PTR_REVERSE(tmp);
136 return 0;
140 static int get_array_size(struct expression *expr)
142 char *name;
143 struct symbol *tmp;
144 struct smatch_state *state;
145 int ret = 0;
147 tmp = get_type(expr);
148 if (!tmp)
149 return ret;
150 if (tmp->type == SYM_ARRAY) {
151 ret = get_expression_value(tmp->array_size);
152 if (ret == 1 && is_last_struct_member(expr))
153 return 0;
154 return ret;
156 name = get_variable_from_expr(expr, NULL);
157 if (!name)
158 return 0;
159 state = get_state(my_decl_id, name, NULL);
160 if (!state || !state->data)
161 goto free;
162 if (tmp->type == SYM_PTR)
163 tmp = get_base_type(tmp);
164 if (!tmp->ctype.alignment)
165 goto free;
166 ret = *(int *)state->data / 8 / tmp->ctype.alignment;
167 free:
168 free_string(name);
169 return ret;
172 extern int check_assigned_expr_id;
173 static void print_assigned_expr(struct expression *expr)
175 #if 0
176 struct state_list *slist;
177 struct sm_state *tmp;
178 char *name;
180 name = get_variable_from_expr(expr, NULL);
181 slist = get_possible_states_expr(check_assigned_expr_id, expr);
182 FOR_EACH_PTR(slist, tmp) {
183 if (tmp->state == &undefined || tmp->state == &merged)
184 continue;
185 smatch_msg("debug: unknown initializer %s = %s", name, show_state(tmp->state));
186 } END_FOR_EACH_PTR(tmp);
187 free_string(name);
188 #endif
191 static void array_check(struct expression *expr)
193 struct expression *dest;
194 int array_size;
195 struct expression *offset;
196 long long max;
197 char *name;
199 expr = strip_expr(expr);
200 if (!is_array(expr))
201 return;
203 dest = get_array_name(expr);
204 array_size = get_array_size(dest);
205 if (!array_size) {
206 name = get_variable_from_expr(dest, NULL);
207 if (!name)
208 return;
209 // smatch_msg("debug: array '%s' unknown size", name);
210 print_assigned_expr(dest);
211 return;
214 offset = get_array_offset(expr);
215 if (!get_fuzzy_max(offset, &max)) {
216 if (getting_address())
217 return;
218 name = get_variable_from_expr(offset, NULL);
219 if (!name)
220 return;
221 // smatch_msg("debug: offset '%s' unknown", name);
222 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
223 add_modification_hook(name, &delete, NULL);
224 print_args(offset, array_size);
225 free_string(name);
226 } else if (array_size <= max) {
227 const char *level = "error";
229 if (getting_address())
230 level = "warn";
232 name = get_variable_from_expr_complex(dest, NULL);
233 /*FIXME!!!!!!!!!!!
234 blast. smatch can't figure out glibc's strcmp __strcmp_cg()
235 so it prints an error every time you compare to a string
236 literal array with 4 or less chars. */
237 if (name && strcmp(name, "__s1") && strcmp(name, "__s2"))
238 sm_msg("%s: buffer overflow '%s' %d <= %lld", level, name, array_size, max);
239 free_string(name);
243 static void match_condition(struct expression *expr)
245 int left;
246 long long val;
247 struct state_list *slist;
248 struct sm_state *tmp;
249 int boundary;
251 if (!expr || expr->type != EXPR_COMPARE)
252 return;
253 if (get_implied_value(expr->left, &val))
254 left = 1;
255 else if (get_implied_value(expr->right, &val))
256 left = 0;
257 else
258 return;
260 if (left)
261 slist = get_possible_states_expr(my_used_id, expr->right);
262 else
263 slist = get_possible_states_expr(my_used_id, expr->left);
264 if (!slist)
265 return;
266 FOR_EACH_PTR(slist, tmp) {
267 if (tmp->state == &merged)
268 continue;
269 boundary = (int)tmp->state->data;
270 boundary -= val;
271 if (boundary < 1 && boundary > -1) {
272 char *name;
274 name = get_variable_from_expr(left?expr->right:expr->left, NULL);
275 sm_msg("error: testing array offset '%s' after use.", name);
276 return;
278 } END_FOR_EACH_PTR(tmp);
282 static void match_string_assignment(struct expression *expr)
284 struct expression *left;
285 struct expression *right;
286 char *name;
288 left = strip_expr(expr->left);
289 right = strip_expr(expr->right);
290 name = get_variable_from_expr(left, NULL);
291 if (!name)
292 return;
293 if (right->type != EXPR_STRING || !right->string)
294 goto free;
295 set_state(my_decl_id, name, NULL,
296 alloc_my_state(right->string->length * 8));
297 free:
298 free_string(name);
301 static void match_malloc(const char *fn, struct expression *expr, void *unused)
303 char *name;
304 struct expression *right;
305 struct expression *arg;
306 long long bytes;
308 name = get_variable_from_expr(expr->left, NULL);
309 if (!name)
310 return;
312 right = strip_expr(expr->right);
313 arg = get_argument_from_call_expr(right->args, 0);
314 if (!get_implied_value(arg, &bytes))
315 goto free;
316 set_state(my_decl_id, name, NULL, alloc_my_state(bytes * 8));
317 free:
318 free_string(name);
321 static void match_strcpy(const char *fn, struct expression *expr,
322 void *unused)
324 struct expression *dest;
325 struct expression *data;
326 char *dest_name = NULL;
327 char *data_name = NULL;
328 struct smatch_state *dest_state;
329 struct smatch_state *data_state;
330 int dest_size;
331 int data_size;
333 dest = get_argument_from_call_expr(expr->args, 0);
334 dest_name = get_variable_from_expr(dest, NULL);
336 data = get_argument_from_call_expr(expr->args, 1);
337 data_name = get_variable_from_expr(data, NULL);
339 dest_state = get_state(my_decl_id, dest_name, NULL);
340 if (!dest_state || !dest_state->data)
341 goto free;
343 data_state = get_state(my_decl_id, data_name, NULL);
344 if (!data_state || !data_state->data)
345 goto free;
346 dest_size = *(int *)dest_state->data / 8;
347 data_size = *(int *)data_state->data / 8;
348 if (dest_size < data_size)
349 sm_msg("error: %s (%d) too large for %s (%d)", data_name,
350 data_size, dest_name, dest_size);
351 free:
352 free_string(dest_name);
353 free_string(data_name);
356 static void match_limitted(const char *fn, struct expression *expr,
357 void *limit_arg)
359 struct expression *dest;
360 struct expression *data;
361 char *dest_name = NULL;
362 struct smatch_state *state;
363 long long needed;
364 int has;
366 dest = get_argument_from_call_expr(expr->args, 0);
367 dest_name = get_variable_from_expr(dest, NULL);
369 data = get_argument_from_call_expr(expr->args, PTR_INT(limit_arg));
370 if (!get_value(data, &needed))
371 goto free;
372 state = get_state(my_decl_id, dest_name, NULL);
373 if (!state || !state->data)
374 goto free;
375 has = *(int *)state->data / 8;
376 if (has < needed)
377 sm_msg("error: %s too small for %lld bytes.", dest_name,
378 needed);
379 free:
380 free_string(dest_name);
383 static void match_array_func(const char *fn, struct expression *expr, void *info)
385 struct bound *bound_info = (struct bound *)info;
386 struct expression *arg;
387 long long offset;
389 arg = get_argument_from_call_expr(expr->args, bound_info->param);
390 if (!get_implied_value(arg, &offset))
391 return;
392 if (offset >= bound_info->size)
393 sm_msg("error: buffer overflow calling %s. param %d. %lld >= %d", fn, bound_info->param, offset, bound_info->size);
396 static void register_array_funcs(void)
398 struct token *token;
399 const char *func;
400 struct bound *bound_info;
402 token = get_tokens_file("kernel.array_bounds");
403 if (!token)
404 return;
405 if (token_type(token) != TOKEN_STREAMBEGIN)
406 return;
407 token = token->next;
408 while (token_type(token) != TOKEN_STREAMEND) {
409 bound_info = malloc(sizeof(*bound_info));
410 if (token_type(token) != TOKEN_IDENT)
411 return;
412 func = show_ident(token->ident);
413 token = token->next;
414 if (token_type(token) != TOKEN_NUMBER)
415 return;
416 bound_info->param = atoi(token->number);
417 token = token->next;
418 if (token_type(token) != TOKEN_NUMBER)
419 return;
420 bound_info->size = atoi(token->number);
421 add_function_hook(func, &match_array_func, bound_info);
422 token = token->next;
424 clear_token_alloc();
427 void check_overflow(int id)
429 my_decl_id = id;
430 add_hook(&match_function_def, FUNC_DEF_HOOK);
431 add_hook(&match_declaration, DECLARATION_HOOK);
432 add_hook(&array_check, OP_HOOK);
433 add_hook(&match_string_assignment, ASSIGNMENT_HOOK);
434 add_hook(&match_condition, CONDITION_HOOK);
435 add_function_assign_hook("malloc", &match_malloc, NULL);
436 add_function_hook("strcpy", &match_strcpy, NULL);
437 add_function_hook("strncpy", &match_limitted, (void *)2);
438 if (option_project == PROJ_KERNEL) {
439 add_function_assign_hook("kmalloc", &match_malloc, NULL);
440 add_function_assign_hook("kzalloc", &match_malloc, NULL);
441 add_function_assign_hook("vmalloc", &match_malloc, NULL);
442 add_function_hook("copy_to_user", &match_limitted, (void *)2);
443 add_function_hook("copy_from_user", &match_limitted, (void *)2);
445 register_array_funcs();
448 void register_check_overflow_again(int id)
450 my_used_id = id;