*new* min_t() casting that truncates the values
[smatch.git] / check_overflow.c
blob9dd030da29f089561013496dd39d69853baa92b9
1 /*
2 * smatch/check_overflow.c
4 * Copyright (C) 2010 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;
22 * This check has two smatch IDs.
23 * my_used_id - keeps a record of array offsets that have been used.
24 * If the code checks that they are within bounds later on,
25 * we complain about using an array offset before checking
26 * that it is within bounds.
28 static int my_used_id;
30 static struct symbol *this_func;
32 static void match_function_def(struct symbol *sym)
34 this_func = sym;
37 struct limiter {
38 int buf_arg;
39 int limit_arg;
41 static struct limiter b0_l2 = {0, 2};
42 static struct limiter b1_l2 = {1, 2};
44 static void print_args(struct expression *expr, int size)
46 struct symbol *sym;
47 char *name;
48 struct symbol *arg;
49 const char *arg_name;
50 int i;
52 if (!option_info)
53 return;
55 name = get_variable_from_expr(expr, &sym);
56 if (!name || !sym)
57 goto free;
59 i = 0;
60 FOR_EACH_PTR(this_func->ctype.base_type->arguments, arg) {
61 arg_name = (arg->ident?arg->ident->name:"-");
62 if (sym == arg && !strcmp(name, arg_name)) {
63 sm_info("param %d array index. size %d", i, size);
64 goto free;
66 i++;
67 } END_FOR_EACH_PTR(arg);
68 free:
69 free_string(name);
72 static void delete(const char *name, struct symbol *sym, struct expression *expr, void *unused)
74 delete_state(my_used_id, name, sym);
77 static int definitely_just_used_as_limiter(struct expression *array, struct expression *offset)
79 long long val;
80 struct expression *tmp;
81 int step = 0;
82 int dot_ops = 0;
84 if (!get_implied_value(offset, &val))
85 return 0;
86 if (get_array_size(array) != val)
87 return 0;
89 FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
90 if (step == 0) {
91 step = 1;
92 continue;
94 if (tmp->type == EXPR_PREOP && tmp->op == '(')
95 continue;
96 if (tmp->op == '.' && !dot_ops++)
97 continue;
98 if (step == 1 && tmp->op == '&') {
99 step = 2;
100 continue;
102 if (step == 2 && tmp->type == EXPR_COMPARE)
103 return 1;
104 return 0;
105 } END_FOR_EACH_PTR_REVERSE(tmp);
106 return 0;
109 static void array_check(struct expression *expr)
111 struct expression *array_expr;
112 int array_size;
113 struct expression *offset;
114 long long max;
115 char *name;
117 expr = strip_expr(expr);
118 if (!is_array(expr))
119 return;
121 array_expr = strip_parens(expr->unop->left);
122 array_size = get_array_size(array_expr);
123 if (!array_size || array_size == 1)
124 return;
126 offset = get_array_offset(expr);
127 if (!get_fuzzy_max(offset, &max)) {
128 if (getting_address())
129 return;
130 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
131 add_modification_hook_expr(my_used_id, offset, &delete, NULL);
132 print_args(offset, array_size);
133 } else if (array_size <= max) {
134 const char *level = "error";
136 if (getting_address())
137 level = "warn";
139 if (definitely_just_used_as_limiter(array_expr, offset))
140 return;
142 if (!option_spammy) {
143 struct smatch_state *state;
145 state = get_state_expr(SMATCH_EXTRA, offset);
146 if (state && is_whole_range(state))
147 return;
150 name = get_variable_from_expr_complex(array_expr, NULL);
151 /* Blast. Smatch can't figure out glibc's strcmp __strcmp_cg()
152 * so it prints an error every time you compare to a string
153 * literal array with 4 or less chars.
155 if (name && strcmp(name, "__s1") && strcmp(name, "__s2")) {
156 sm_msg("%s: buffer overflow '%s' %d <= %lld",
157 level, name, array_size, max);
159 free_string(name);
163 static void match_condition(struct expression *expr)
165 int left;
166 long long val;
167 struct state_list *slist;
168 struct sm_state *tmp;
169 int boundary;
171 if (!expr || expr->type != EXPR_COMPARE)
172 return;
173 if (get_implied_value(expr->left, &val))
174 left = 1;
175 else if (get_implied_value(expr->right, &val))
176 left = 0;
177 else
178 return;
180 if (left)
181 slist = get_possible_states_expr(my_used_id, expr->right);
182 else
183 slist = get_possible_states_expr(my_used_id, expr->left);
184 if (!slist)
185 return;
186 FOR_EACH_PTR(slist, tmp) {
187 if (tmp->state == &merged)
188 continue;
189 boundary = PTR_INT(tmp->state->data);
190 boundary -= val;
191 if (boundary < 1 && boundary > -1) {
192 char *name;
194 name = get_variable_from_expr((left ? expr->right : expr->left), NULL);
195 sm_msg("error: testing array offset '%s' after use.", name);
196 return;
198 } END_FOR_EACH_PTR(tmp);
201 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
203 struct expression *dest;
204 struct expression *data;
205 char *dest_name = NULL;
206 char *data_name = NULL;
207 int dest_size;
208 int data_size;
210 dest = get_argument_from_call_expr(expr->args, 0);
211 data = get_argument_from_call_expr(expr->args, 1);
212 dest_size = get_array_size_bytes(dest);
213 data_size = get_array_size_bytes(data);
215 if (!dest_size)
216 return;
218 /* If the size of both arrays is known and the destination
219 * buffer is larger than the source buffer, we're okay.
221 if (data_size && dest_size >= data_size)
222 return;
224 dest_name = get_variable_from_expr_complex(dest, NULL);
225 data_name = get_variable_from_expr_complex(data, NULL);
227 if (data_size)
228 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
229 fn, data_name, dest_name, data_size, dest_size);
230 else if (option_spammy)
231 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
232 fn, data_name, dest_name);
234 free_string(dest_name);
235 free_string(data_name);
238 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
240 struct expression *dest;
241 struct expression *dest_size_expr;
242 struct expression *format_string;
243 struct expression *data;
244 char *data_name = NULL;
245 int dest_size;
246 long long limit_size;
247 char *format;
248 int data_size;
250 dest = get_argument_from_call_expr(expr->args, 0);
251 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
252 format_string = get_argument_from_call_expr(expr->args, 2);
253 data = get_argument_from_call_expr(expr->args, 3);
255 dest_size = get_array_size_bytes(dest);
256 if (!get_implied_value(dest_size_expr, &limit_size))
257 return;
258 if (dest_size && dest_size < limit_size)
259 sm_msg("error: snprintf() is printing too much %lld vs %d", limit_size, dest_size);
260 format = get_variable_from_expr(format_string, NULL);
261 if (!format)
262 return;
263 if (strcmp(format, "\"%s\""))
264 goto free;
265 data_name = get_variable_from_expr_complex(data, NULL);
266 data_size = get_array_size_bytes(data);
267 if (limit_size < data_size)
268 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %lld",
269 data_name, data_size, limit_size);
270 free:
271 free_string(data_name);
272 free_string(format);
275 static void match_sprintf(const char *fn, struct expression *expr, void *unused)
277 struct expression *dest;
278 struct expression *format_string;
279 struct expression *data;
280 char *data_name = NULL;
281 int dest_size;
282 char *format;
283 int data_size;
285 dest = get_argument_from_call_expr(expr->args, 0);
286 format_string = get_argument_from_call_expr(expr->args, 1);
287 data = get_argument_from_call_expr(expr->args, 2);
289 dest_size = get_array_size_bytes(dest);
290 if (!dest_size)
291 return;
292 format = get_variable_from_expr(format_string, NULL);
293 if (!format)
294 return;
295 if (strcmp(format, "\"%s\""))
296 goto free;
297 data_name = get_variable_from_expr_complex(data, NULL);
298 data_size = get_array_size_bytes(data);
299 if (dest_size < data_size)
300 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
301 data_name, data_size, dest_size);
302 free:
303 free_string(data_name);
304 free_string(format);
307 static void match_limited(const char *fn, struct expression *expr, void *_limiter)
309 struct limiter *limiter = (struct limiter *)_limiter;
310 struct expression *dest;
311 struct expression *data;
312 char *dest_name = NULL;
313 long long needed;
314 int has;
316 dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
317 data = get_argument_from_call_expr(expr->args, limiter->limit_arg);
318 if (!get_fuzzy_max(data, &needed))
319 return;
320 has = get_array_size_bytes(dest);
321 if (!has)
322 return;
323 if (has >= needed)
324 return;
326 dest_name = get_variable_from_expr_complex(dest, NULL);
327 sm_msg("error: %s() '%s' too small (%d vs %lld)", fn, dest_name, has, needed);
328 free_string(dest_name);
331 static void match_array_func(const char *fn, struct expression *expr, void *info)
333 struct bound *bound_info = (struct bound *)info;
334 struct expression *arg;
335 long long offset;
337 arg = get_argument_from_call_expr(expr->args, bound_info->param);
338 if (!get_implied_max(arg, &offset))
339 return;
340 if (offset >= bound_info->size)
341 sm_msg("error: buffer overflow calling %s. param %d. %lld >= %d",
342 fn, bound_info->param, offset, bound_info->size);
345 static void register_array_funcs(void)
347 struct token *token;
348 const char *func;
349 struct bound *bound_info = NULL;
350 char name[256];
352 snprintf(name, 256, "%s.array_bounds", option_project_str);
353 token = get_tokens_file(name);
354 if (!token)
355 return;
356 if (token_type(token) != TOKEN_STREAMBEGIN)
357 return;
358 token = token->next;
359 while (token_type(token) != TOKEN_STREAMEND) {
360 bound_info = malloc(sizeof(*bound_info));
361 if (token_type(token) != TOKEN_IDENT)
362 break;
363 func = show_ident(token->ident);
364 token = token->next;
365 if (token_type(token) != TOKEN_NUMBER)
366 break;
367 bound_info->param = atoi(token->number);
368 token = token->next;
369 if (token_type(token) != TOKEN_NUMBER)
370 break;
371 bound_info->size = atoi(token->number);
372 add_function_hook(func, &match_array_func, bound_info);
373 token = token->next;
375 if (token_type(token) != TOKEN_STREAMEND) {
376 printf("failed to load %s (line %d)", name, token->pos.line);
377 free(bound_info);
379 clear_token_alloc();
382 void check_overflow(int id)
384 my_used_id = id;
385 add_hook(&match_function_def, FUNC_DEF_HOOK);
386 add_hook(&array_check, OP_HOOK);
387 add_hook(&match_condition, CONDITION_HOOK);
388 add_function_hook("strcpy", &match_strcpy, NULL);
389 add_function_hook("snprintf", &match_snprintf, NULL);
390 add_function_hook("sprintf", &match_sprintf, NULL);
391 if (option_project == PROJ_KERNEL) {
392 add_function_hook("copy_to_user", &match_limited, &b0_l2);
393 add_function_hook("copy_to_user", &match_limited, &b1_l2);
394 add_function_hook("_copy_to_user", &match_limited, &b0_l2);
395 add_function_hook("_copy_to_user", &match_limited, &b1_l2);
396 add_function_hook("__copy_to_user", &match_limited, &b0_l2);
397 add_function_hook("__copy_to_user", &match_limited, &b1_l2);
398 add_function_hook("copy_from_user", &match_limited, &b0_l2);
399 add_function_hook("copy_from_user", &match_limited, &b1_l2);
400 add_function_hook("_copy_from_user", &match_limited, &b0_l2);
401 add_function_hook("_copy_from_user", &match_limited, &b1_l2);
402 add_function_hook("__copy_from_user", &match_limited, &b0_l2);
403 add_function_hook("__copy_from_user", &match_limited, &b1_l2);
405 if (option_spammy)
406 register_array_funcs();