*new* smatch_clear_buffer.c: handle memset() type functions
[smatch.git] / check_overflow.c
bloba8768e9eccf287d9f69ef21103ea83d61764aae5
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)
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 common_false_positives(char *name)
85 if (!name)
86 return 0;
88 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
89 * so it prints an error every time you compare to a string
90 * literal array with 4 or less chars.
92 if (strcmp(name, "__s1") == 0 || strcmp(name, "__s2") == 0)
93 return 1;
96 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
97 * how it's used so it causes a bunch of false positives.
99 if (option_project == PROJ_KERNEL &&
100 strcmp(name, "__per_cpu_offset") == 0)
101 return 1;
102 return 0;
105 static void array_check(struct expression *expr)
107 struct expression *array_expr;
108 int array_size;
109 struct expression *offset;
110 sval_t max;
111 char *name;
113 expr = strip_expr(expr);
114 if (!is_array(expr))
115 return;
117 array_expr = strip_parens(expr->unop->left);
118 array_size = get_array_size(array_expr);
119 if (!array_size || array_size == 1)
120 return;
122 offset = get_array_offset(expr);
123 if (!get_fuzzy_max(offset, &max)) {
124 if (getting_address())
125 return;
126 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
127 } else if (array_size <= max.value) {
128 const char *level = "error";
130 if (getting_address())
131 level = "warn";
133 if (definitely_just_used_as_limiter(array_expr, offset))
134 return;
136 if (!option_spammy) {
137 struct smatch_state *state;
139 state = get_state_expr(SMATCH_EXTRA, offset);
140 if (state && estate_is_whole(state))
141 return;
144 name = expr_to_str(array_expr);
145 if (!common_false_positives(name)) {
146 sm_msg("%s: buffer overflow '%s' %d <= %s",
147 level, name, array_size, sval_to_str(max));
149 free_string(name);
153 static void match_condition(struct expression *expr)
155 int left;
156 sval_t sval;
157 struct state_list *slist;
158 struct sm_state *tmp;
159 int boundary;
161 if (!expr || expr->type != EXPR_COMPARE)
162 return;
163 if (get_macro_name(expr->pos))
164 return;
165 if (get_implied_value(expr->left, &sval))
166 left = 1;
167 else if (get_implied_value(expr->right, &sval))
168 left = 0;
169 else
170 return;
172 if (left)
173 slist = get_possible_states_expr(my_used_id, expr->right);
174 else
175 slist = get_possible_states_expr(my_used_id, expr->left);
176 if (!slist)
177 return;
178 FOR_EACH_PTR(slist, tmp) {
179 if (tmp->state == &merged || tmp->state == &undefined)
180 continue;
181 boundary = PTR_INT(tmp->state->data);
182 boundary -= sval.value;
183 if (boundary < 1 && boundary > -1) {
184 char *name;
186 name = expr_to_var(left ? expr->right : expr->left);
187 sm_msg("error: testing array offset '%s' after use.", name);
188 return;
190 } END_FOR_EACH_PTR(tmp);
193 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
195 struct expression *dest;
196 struct expression *data;
197 char *dest_name = NULL;
198 char *data_name = NULL;
199 int dest_size;
200 int data_size;
202 dest = get_argument_from_call_expr(expr->args, 0);
203 data = get_argument_from_call_expr(expr->args, 1);
204 dest_size = get_array_size_bytes(dest);
205 data_size = get_array_size_bytes(data);
207 if (!dest_size)
208 return;
210 /* If the size of both arrays is known and the destination
211 * buffer is larger than the source buffer, we're okay.
213 if (data_size && dest_size >= data_size)
214 return;
216 dest_name = expr_to_str(dest);
217 data_name = expr_to_str(data);
219 if (data_size)
220 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
221 fn, data_name, dest_name, data_size, dest_size);
222 else if (option_spammy)
223 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
224 fn, data_name, dest_name);
226 free_string(dest_name);
227 free_string(data_name);
230 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
232 struct expression *dest;
233 struct expression *dest_size_expr;
234 struct expression *format_string;
235 struct expression *data;
236 char *data_name = NULL;
237 int dest_size;
238 sval_t limit_size;
239 char *format;
240 int data_size;
242 dest = get_argument_from_call_expr(expr->args, 0);
243 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
244 format_string = get_argument_from_call_expr(expr->args, 2);
245 data = get_argument_from_call_expr(expr->args, 3);
247 dest_size = get_array_size_bytes(dest);
248 if (!get_implied_value(dest_size_expr, &limit_size))
249 return;
250 if (dest_size && dest_size < limit_size.value)
251 sm_msg("error: snprintf() is printing too much %s vs %d",
252 sval_to_str(limit_size), dest_size);
253 format = expr_to_var(format_string);
254 if (!format)
255 return;
256 if (strcmp(format, "\"%s\""))
257 goto free;
258 data_name = expr_to_str(data);
259 data_size = get_array_size_bytes(data);
260 if (limit_size.value < data_size)
261 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %s",
262 data_name, data_size, sval_to_str(limit_size));
263 free:
264 free_string(data_name);
265 free_string(format);
268 static void match_sprintf(const char *fn, struct expression *expr, void *unused)
270 struct expression *dest;
271 struct expression *format_string;
272 struct expression *data;
273 char *data_name = NULL;
274 int dest_size;
275 char *format;
276 int data_size;
278 dest = get_argument_from_call_expr(expr->args, 0);
279 format_string = get_argument_from_call_expr(expr->args, 1);
280 data = get_argument_from_call_expr(expr->args, 2);
282 dest_size = get_array_size_bytes(dest);
283 if (!dest_size)
284 return;
285 format = expr_to_var(format_string);
286 if (!format)
287 return;
288 if (strcmp(format, "\"%s\""))
289 goto free;
290 data_name = expr_to_str(data);
291 data_size = get_array_size_bytes(data);
292 if (dest_size < data_size)
293 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
294 data_name, data_size, dest_size);
295 free:
296 free_string(data_name);
297 free_string(format);
300 static void match_limited(const char *fn, struct expression *expr, void *_limiter)
302 struct limiter *limiter = (struct limiter *)_limiter;
303 struct expression *dest;
304 struct expression *data;
305 char *dest_name = NULL;
306 sval_t needed;
307 int has;
309 dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
310 data = get_argument_from_call_expr(expr->args, limiter->limit_arg);
311 if (!get_fuzzy_max(data, &needed))
312 return;
313 has = get_array_size_bytes(dest);
314 if (!has)
315 return;
316 if (has >= needed.value)
317 return;
319 dest_name = expr_to_str(dest);
320 sm_msg("error: %s() '%s' too small (%d vs %s)", fn, dest_name, has, sval_to_str(needed));
321 free_string(dest_name);
324 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
326 struct expression *call;
327 struct symbol *type;
328 int bytes;
329 sval_t sval;
331 if (expr->type != EXPR_ASSIGNMENT)
332 return;
333 call = strip_expr(expr->right);
334 type = get_pointer_type(expr->left);
336 if (!parse_call_math(call, math, &sval) || sval.value == 0)
337 return;
338 if (!type)
339 return;
340 bytes = bits_to_bytes(type->bit_size);
341 if (bytes <= 0)
342 return;
343 if (sval.uvalue >= bytes)
344 return;
345 sm_msg("error: not allocating enough data %d vs %s", bytes, sval_to_str(sval));
348 static void register_funcs_from_file(void)
350 char name[256];
351 struct token *token;
352 const char *func;
353 int size, buf;
354 struct limiter *limiter;
356 snprintf(name, 256, "%s.sizeof_param", option_project_str);
357 name[255] = '\0';
358 token = get_tokens_file(name);
359 if (!token)
360 return;
361 if (token_type(token) != TOKEN_STREAMBEGIN)
362 return;
363 token = token->next;
364 while (token_type(token) != TOKEN_STREAMEND) {
365 if (token_type(token) != TOKEN_IDENT)
366 return;
367 func = show_ident(token->ident);
369 token = token->next;
370 if (token_type(token) != TOKEN_NUMBER)
371 return;
372 size = atoi(token->number);
374 token = token->next;
375 if (token_type(token) != TOKEN_NUMBER)
376 return;
377 buf = atoi(token->number);
379 limiter = malloc(sizeof(*limiter));
380 limiter->limit_arg = size;
381 limiter->buf_arg = buf;
383 add_function_hook(func, &match_limited, limiter);
385 token = token->next;
387 clear_token_alloc();
390 void check_overflow(int id)
392 my_used_id = id;
393 register_funcs_from_file();
394 add_hook(&match_function_def, FUNC_DEF_HOOK);
395 add_hook(&array_check, OP_HOOK);
396 add_hook(&match_condition, CONDITION_HOOK);
397 add_function_hook("strcpy", &match_strcpy, NULL);
398 add_function_hook("snprintf", &match_snprintf, NULL);
399 add_function_hook("sprintf", &match_sprintf, NULL);
400 add_function_hook("memcmp", &match_limited, &b0_l2);
401 add_function_hook("memcmp", &match_limited, &b1_l2);
402 add_db_return_states_callback(BUF_SIZE, &db_returns_buf_size);
403 add_modification_hook(my_used_id, &delete);
404 if (option_project == PROJ_KERNEL) {
405 add_function_hook("copy_to_user", &match_limited, &b0_l2);
406 add_function_hook("copy_to_user", &match_limited, &b1_l2);
407 add_function_hook("_copy_to_user", &match_limited, &b0_l2);
408 add_function_hook("_copy_to_user", &match_limited, &b1_l2);
409 add_function_hook("__copy_to_user", &match_limited, &b0_l2);
410 add_function_hook("__copy_to_user", &match_limited, &b1_l2);
411 add_function_hook("copy_from_user", &match_limited, &b0_l2);
412 add_function_hook("copy_from_user", &match_limited, &b1_l2);
413 add_function_hook("_copy_from_user", &match_limited, &b0_l2);
414 add_function_hook("_copy_from_user", &match_limited, &b1_l2);
415 add_function_hook("__copy_from_user", &match_limited, &b0_l2);
416 add_function_hook("__copy_from_user", &match_limited, &b1_l2);