kernel data/: update to recent linux-next (3.11-rc1 ish)
[smatch.git] / check_overflow.c
blob2bfe076e55c2783cadbdea8ad1f6dfa03cb7ab10
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 delete(struct sm_state *sm, struct expression *mod_expr)
46 set_state(my_used_id, sm->name, sm->sym, &undefined);
49 static int definitely_just_used_as_limiter(struct expression *array, struct expression *offset)
51 sval_t sval;
52 struct expression *tmp;
53 int step = 0;
54 int dot_ops = 0;
56 if (!get_implied_value(offset, &sval))
57 return 0;
58 if (get_array_size(array) != sval.value)
59 return 0;
61 FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
62 if (step == 0) {
63 step = 1;
64 continue;
66 if (tmp->type == EXPR_PREOP && tmp->op == '(')
67 continue;
68 if (tmp->op == '.' && !dot_ops++)
69 continue;
70 if (step == 1 && tmp->op == '&') {
71 step = 2;
72 continue;
74 if (step == 2 && tmp->type == EXPR_COMPARE)
75 return 1;
76 if (step == 2 && tmp->type == EXPR_ASSIGNMENT)
77 return 1;
78 return 0;
79 } END_FOR_EACH_PTR_REVERSE(tmp);
80 return 0;
83 static int get_the_max(struct expression *expr, sval_t *sval)
85 if (get_hard_max(expr, sval))
86 return 1;
87 if (!option_spammy)
88 return 0;
89 if (is_user_data(expr))
90 return get_absolute_max(expr, sval);
91 /* FIXME: get_fuzzy_max() is not working well across function boundaries */
92 return 0;
95 static int common_false_positives(char *name)
97 if (!name)
98 return 0;
100 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
101 * so it prints an error every time you compare to a string
102 * literal array with 4 or less chars.
104 if (strcmp(name, "__s1") == 0 || strcmp(name, "__s2") == 0)
105 return 1;
108 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
109 * how it's used so it causes a bunch of false positives.
111 if (option_project == PROJ_KERNEL &&
112 strcmp(name, "__per_cpu_offset") == 0)
113 return 1;
114 return 0;
117 static void array_check(struct expression *expr)
119 struct expression *array_expr;
120 int array_size;
121 struct expression *offset;
122 sval_t max;
123 char *name;
125 expr = strip_expr(expr);
126 if (!is_array(expr))
127 return;
129 array_expr = strip_parens(expr->unop->left);
130 array_size = get_array_size(array_expr);
131 if (!array_size || array_size == 1)
132 return;
134 offset = get_array_offset(expr);
135 if (!get_the_max(offset, &max)) {
136 if (getting_address())
137 return;
138 if (is_capped(offset))
139 return;
140 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
141 } else if (array_size <= max.value) {
142 const char *level = "error";
144 if (getting_address())
145 level = "warn";
147 if (definitely_just_used_as_limiter(array_expr, offset))
148 return;
150 if (!option_spammy) {
151 struct smatch_state *state;
153 state = get_state_expr(SMATCH_EXTRA, offset);
154 if (state && estate_is_whole(state))
155 return;
158 name = expr_to_str(array_expr);
159 if (!common_false_positives(name)) {
160 sm_msg("%s: buffer overflow '%s' %d <= %s",
161 level, name, array_size, sval_to_str(max));
163 free_string(name);
167 static void match_condition(struct expression *expr)
169 int left;
170 sval_t sval;
171 struct state_list *slist;
172 struct sm_state *tmp;
173 int boundary;
175 if (!expr || expr->type != EXPR_COMPARE)
176 return;
177 if (get_macro_name(expr->pos))
178 return;
179 if (get_implied_value(expr->left, &sval))
180 left = 1;
181 else if (get_implied_value(expr->right, &sval))
182 left = 0;
183 else
184 return;
186 if (left)
187 slist = get_possible_states_expr(my_used_id, expr->right);
188 else
189 slist = get_possible_states_expr(my_used_id, expr->left);
190 if (!slist)
191 return;
192 FOR_EACH_PTR(slist, tmp) {
193 if (tmp->state == &merged || tmp->state == &undefined)
194 continue;
195 boundary = PTR_INT(tmp->state->data);
196 boundary -= sval.value;
197 if (boundary < 1 && boundary > -1) {
198 char *name;
200 name = expr_to_var(left ? expr->right : expr->left);
201 sm_msg("error: testing array offset '%s' after use.", name);
202 return;
204 } END_FOR_EACH_PTR(tmp);
207 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
209 struct expression *dest;
210 struct expression *data;
211 char *dest_name = NULL;
212 char *data_name = NULL;
213 int dest_size;
214 int data_size;
216 dest = get_argument_from_call_expr(expr->args, 0);
217 data = get_argument_from_call_expr(expr->args, 1);
218 dest_size = get_array_size_bytes(dest);
219 data_size = get_array_size_bytes(data);
221 if (!dest_size)
222 return;
224 /* If the size of both arrays is known and the destination
225 * buffer is larger than the source buffer, we're okay.
227 if (data_size && dest_size >= data_size)
228 return;
230 dest_name = expr_to_str(dest);
231 data_name = expr_to_str(data);
233 if (data_size)
234 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
235 fn, data_name, dest_name, data_size, dest_size);
236 else if (option_spammy)
237 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
238 fn, data_name, dest_name);
240 free_string(dest_name);
241 free_string(data_name);
244 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
246 struct expression *dest;
247 struct expression *dest_size_expr;
248 struct expression *format_string;
249 struct expression *data;
250 char *data_name = NULL;
251 int dest_size;
252 sval_t limit_size;
253 char *format;
254 int data_size;
256 dest = get_argument_from_call_expr(expr->args, 0);
257 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
258 format_string = get_argument_from_call_expr(expr->args, 2);
259 data = get_argument_from_call_expr(expr->args, 3);
261 dest_size = get_array_size_bytes(dest);
262 if (!get_implied_value(dest_size_expr, &limit_size))
263 return;
264 if (dest_size && dest_size < limit_size.value)
265 sm_msg("error: snprintf() is printing too much %s vs %d",
266 sval_to_str(limit_size), dest_size);
267 format = expr_to_var(format_string);
268 if (!format)
269 return;
270 if (strcmp(format, "\"%s\""))
271 goto free;
272 data_name = expr_to_str(data);
273 data_size = get_array_size_bytes(data);
274 if (limit_size.value < data_size)
275 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %s",
276 data_name, data_size, sval_to_str(limit_size));
277 free:
278 free_string(data_name);
279 free_string(format);
282 static void match_sprintf(const char *fn, struct expression *expr, void *unused)
284 struct expression *dest;
285 struct expression *format_string;
286 struct expression *data;
287 char *data_name = NULL;
288 int dest_size;
289 char *format;
290 int data_size;
292 dest = get_argument_from_call_expr(expr->args, 0);
293 format_string = get_argument_from_call_expr(expr->args, 1);
294 data = get_argument_from_call_expr(expr->args, 2);
296 dest_size = get_array_size_bytes(dest);
297 if (!dest_size)
298 return;
299 format = expr_to_var(format_string);
300 if (!format)
301 return;
302 if (strcmp(format, "\"%s\""))
303 goto free;
304 data_name = expr_to_str(data);
305 data_size = get_array_size_bytes(data);
306 if (dest_size < data_size)
307 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
308 data_name, data_size, dest_size);
309 free:
310 free_string(data_name);
311 free_string(format);
314 static void match_limited(const char *fn, struct expression *expr, void *_limiter)
316 struct limiter *limiter = (struct limiter *)_limiter;
317 struct expression *dest;
318 struct expression *data;
319 char *dest_name = NULL;
320 sval_t needed;
321 int has;
323 dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
324 data = get_argument_from_call_expr(expr->args, limiter->limit_arg);
325 if (!get_the_max(data, &needed))
326 return;
327 has = get_array_size_bytes(dest);
328 if (!has)
329 return;
330 if (has >= needed.value)
331 return;
333 dest_name = expr_to_str(dest);
334 sm_msg("error: %s() '%s' too small (%d vs %s)", fn, dest_name, has, sval_to_str(needed));
335 free_string(dest_name);
338 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
340 struct expression *call;
341 struct symbol *left_type, *right_type;
342 int bytes;
343 sval_t sval;
345 if (expr->type != EXPR_ASSIGNMENT)
346 return;
347 right_type = get_pointer_type(expr->right);
348 if (!right_type || right_type->bit_size != -1)
349 return;
351 call = strip_expr(expr->right);
352 left_type = get_pointer_type(expr->left);
354 if (!parse_call_math(call, math, &sval) || sval.value == 0)
355 return;
356 if (!left_type)
357 return;
358 bytes = bits_to_bytes(left_type->bit_size);
359 if (bytes <= 0)
360 return;
361 if (sval.uvalue >= bytes)
362 return;
363 sm_msg("error: not allocating enough data %d vs %s", bytes, sval_to_str(sval));
366 static void register_funcs_from_file(void)
368 char name[256];
369 struct token *token;
370 const char *func;
371 int size, buf;
372 struct limiter *limiter;
374 snprintf(name, 256, "%s.sizeof_param", option_project_str);
375 name[255] = '\0';
376 token = get_tokens_file(name);
377 if (!token)
378 return;
379 if (token_type(token) != TOKEN_STREAMBEGIN)
380 return;
381 token = token->next;
382 while (token_type(token) != TOKEN_STREAMEND) {
383 if (token_type(token) != TOKEN_IDENT)
384 return;
385 func = show_ident(token->ident);
387 token = token->next;
388 if (token_type(token) != TOKEN_NUMBER)
389 return;
390 size = atoi(token->number);
392 token = token->next;
393 if (token_type(token) != TOKEN_NUMBER)
394 return;
395 buf = atoi(token->number);
397 limiter = malloc(sizeof(*limiter));
398 limiter->limit_arg = size;
399 limiter->buf_arg = buf;
401 add_function_hook(func, &match_limited, limiter);
403 token = token->next;
405 clear_token_alloc();
408 void check_overflow(int id)
410 my_used_id = id;
411 register_funcs_from_file();
412 add_hook(&match_function_def, FUNC_DEF_HOOK);
413 add_hook(&array_check, OP_HOOK);
414 add_hook(&match_condition, CONDITION_HOOK);
415 add_function_hook("strcpy", &match_strcpy, NULL);
416 add_function_hook("snprintf", &match_snprintf, NULL);
417 add_function_hook("sprintf", &match_sprintf, NULL);
418 add_function_hook("memcmp", &match_limited, &b0_l2);
419 add_function_hook("memcmp", &match_limited, &b1_l2);
420 select_return_states_hook(BUF_SIZE, &db_returns_buf_size);
421 add_modification_hook(my_used_id, &delete);
422 if (option_project == PROJ_KERNEL) {
423 add_function_hook("copy_to_user", &match_limited, &b0_l2);
424 add_function_hook("copy_to_user", &match_limited, &b1_l2);
425 add_function_hook("_copy_to_user", &match_limited, &b0_l2);
426 add_function_hook("_copy_to_user", &match_limited, &b1_l2);
427 add_function_hook("__copy_to_user", &match_limited, &b0_l2);
428 add_function_hook("__copy_to_user", &match_limited, &b1_l2);
429 add_function_hook("copy_from_user", &match_limited, &b0_l2);
430 add_function_hook("copy_from_user", &match_limited, &b1_l2);
431 add_function_hook("_copy_from_user", &match_limited, &b0_l2);
432 add_function_hook("_copy_from_user", &match_limited, &b1_l2);
433 add_function_hook("__copy_from_user", &match_limited, &b0_l2);
434 add_function_hook("__copy_from_user", &match_limited, &b1_l2);