Documentation: make me less confused
[smatch.git] / check_overflow.c
blob864c0b99d4876fe085858d6a25a1d778cde12d0d
1 /*
2 * Copyright (C) 2010 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 #include <stdlib.h>
19 #include "parse.h"
20 #include "smatch.h"
21 #include "smatch_slist.h"
22 #include "smatch_extra.h"
24 struct bound {
25 int param;
26 int size;
30 * This check has two smatch IDs.
31 * my_used_id - keeps a record of array offsets that have been used.
32 * If the code checks that they are within bounds later on,
33 * we complain about using an array offset before checking
34 * that it is within bounds.
36 static int my_used_id;
38 static struct symbol *this_func;
40 static void match_function_def(struct symbol *sym)
42 this_func = sym;
45 struct limiter {
46 int buf_arg;
47 int limit_arg;
49 static struct limiter b0_l2 = {0, 2};
50 static struct limiter b1_l2 = {1, 2};
52 static void delete(struct sm_state *sm, struct expression *mod_expr)
54 set_state(my_used_id, sm->name, sm->sym, &undefined);
57 static int definitely_just_used_as_limiter(struct expression *array, struct expression *offset)
59 sval_t sval;
60 struct expression *tmp;
61 int step = 0;
62 int dot_ops = 0;
64 if (!get_implied_value(offset, &sval))
65 return 0;
66 if (get_array_size(array) != sval.value)
67 return 0;
69 FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
70 if (step == 0) {
71 step = 1;
72 continue;
74 if (tmp->type == EXPR_PREOP && tmp->op == '(')
75 continue;
76 if (tmp->op == '.' && !dot_ops++)
77 continue;
78 if (step == 1 && tmp->op == '&') {
79 step = 2;
80 continue;
82 if (step == 2 && tmp->type == EXPR_COMPARE)
83 return 1;
84 if (step == 2 && tmp->type == EXPR_ASSIGNMENT)
85 return 1;
86 return 0;
87 } END_FOR_EACH_PTR_REVERSE(tmp);
88 return 0;
91 static int get_the_max(struct expression *expr, sval_t *sval)
93 if (get_hard_max(expr, sval))
94 return 1;
95 if (!option_spammy)
96 return 0;
97 if (get_fuzzy_max(expr, sval))
98 return 1;
99 if (is_user_data(expr))
100 return get_absolute_max(expr, sval);
101 return 0;
104 static int common_false_positives(char *name)
106 if (!name)
107 return 0;
109 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
110 * so it prints an error every time you compare to a string
111 * literal array with 4 or less chars.
113 if (strcmp(name, "__s1") == 0 || strcmp(name, "__s2") == 0)
114 return 1;
117 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
118 * how it's used so it causes a bunch of false positives.
120 if (option_project == PROJ_KERNEL &&
121 strcmp(name, "__per_cpu_offset") == 0)
122 return 1;
123 return 0;
126 static void array_check(struct expression *expr)
128 struct expression *array_expr;
129 int array_size;
130 struct expression *offset;
131 sval_t max;
132 char *name;
134 expr = strip_expr(expr);
135 if (!is_array(expr))
136 return;
138 array_expr = get_array_base(expr);
139 array_size = get_array_size(array_expr);
140 if (!array_size || array_size == 1)
141 return;
143 offset = get_array_offset(expr);
144 if (!get_the_max(offset, &max)) {
145 if (getting_address())
146 return;
147 if (is_capped(offset))
148 return;
149 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
150 } else if (array_size <= max.value) {
151 const char *level = "error";
153 if (getting_address())
154 level = "warn";
156 if (definitely_just_used_as_limiter(array_expr, offset))
157 return;
159 name = expr_to_str(array_expr);
160 if (!common_false_positives(name)) {
161 sm_msg("%s: buffer overflow '%s' %d <= %s",
162 level, name, array_size, sval_to_str(max));
164 free_string(name);
168 static void match_condition(struct expression *expr)
170 int left;
171 sval_t sval;
172 struct state_list *slist;
173 struct sm_state *tmp;
174 int boundary;
176 if (!expr || expr->type != EXPR_COMPARE)
177 return;
178 if (get_macro_name(expr->pos))
179 return;
180 if (get_implied_value(expr->left, &sval))
181 left = 1;
182 else if (get_implied_value(expr->right, &sval))
183 left = 0;
184 else
185 return;
187 if (left)
188 slist = get_possible_states_expr(my_used_id, expr->right);
189 else
190 slist = get_possible_states_expr(my_used_id, expr->left);
191 if (!slist)
192 return;
193 FOR_EACH_PTR(slist, tmp) {
194 if (tmp->state == &merged || tmp->state == &undefined)
195 continue;
196 boundary = PTR_INT(tmp->state->data);
197 boundary -= sval.value;
198 if (boundary < 1 && boundary > -1) {
199 char *name;
201 name = expr_to_var(left ? expr->right : expr->left);
202 sm_msg("error: testing array offset '%s' after use.", name);
203 return;
205 } END_FOR_EACH_PTR(tmp);
208 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
210 struct expression *dest;
211 struct expression *data;
212 char *dest_name = NULL;
213 char *data_name = NULL;
214 int dest_size;
215 int data_size;
217 dest = get_argument_from_call_expr(expr->args, 0);
218 data = get_argument_from_call_expr(expr->args, 1);
219 dest_size = get_array_size_bytes(dest);
220 data_size = get_array_size_bytes(data);
222 if (!dest_size)
223 return;
225 /* If the size of both arrays is known and the destination
226 * buffer is larger than the source buffer, we're okay.
228 if (data_size && dest_size >= data_size)
229 return;
231 dest_name = expr_to_str(dest);
232 data_name = expr_to_str(data);
234 if (data_size)
235 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
236 fn, data_name, dest_name, data_size, dest_size);
237 else if (option_spammy)
238 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
239 fn, data_name, dest_name);
241 free_string(dest_name);
242 free_string(data_name);
245 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
247 struct expression *dest;
248 struct expression *dest_size_expr;
249 struct expression *format_string;
250 struct expression *data;
251 char *data_name = NULL;
252 int dest_size;
253 sval_t limit_size;
254 char *format;
255 int data_size;
257 dest = get_argument_from_call_expr(expr->args, 0);
258 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
259 format_string = get_argument_from_call_expr(expr->args, 2);
260 data = get_argument_from_call_expr(expr->args, 3);
262 dest_size = get_array_size_bytes(dest);
263 if (!get_implied_value(dest_size_expr, &limit_size))
264 return;
265 if (dest_size && dest_size < limit_size.value)
266 sm_msg("error: snprintf() is printing too much %s vs %d",
267 sval_to_str(limit_size), dest_size);
268 format = expr_to_var(format_string);
269 if (!format)
270 return;
271 if (strcmp(format, "\"%s\""))
272 goto free;
273 data_name = expr_to_str(data);
274 data_size = get_array_size_bytes(data);
275 if (limit_size.value < data_size)
276 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %s",
277 data_name, data_size, sval_to_str(limit_size));
278 free:
279 free_string(data_name);
280 free_string(format);
283 static void match_sprintf(const char *fn, struct expression *expr, void *unused)
285 struct expression *dest;
286 struct expression *format_string;
287 struct expression *data;
288 char *data_name = NULL;
289 int dest_size;
290 char *format;
291 int data_size;
293 dest = get_argument_from_call_expr(expr->args, 0);
294 format_string = get_argument_from_call_expr(expr->args, 1);
295 data = get_argument_from_call_expr(expr->args, 2);
297 dest_size = get_array_size_bytes(dest);
298 if (!dest_size)
299 return;
300 format = expr_to_var(format_string);
301 if (!format)
302 return;
303 if (strcmp(format, "\"%s\""))
304 goto free;
305 data_name = expr_to_str(data);
306 data_size = get_array_size_bytes(data);
307 if (dest_size < data_size)
308 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
309 data_name, data_size, dest_size);
310 free:
311 free_string(data_name);
312 free_string(format);
315 static void match_limited(const char *fn, struct expression *expr, void *_limiter)
317 struct limiter *limiter = (struct limiter *)_limiter;
318 struct expression *dest;
319 struct expression *data;
320 char *dest_name = NULL;
321 sval_t needed;
322 int has;
324 dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
325 data = get_argument_from_call_expr(expr->args, limiter->limit_arg);
326 if (!get_the_max(data, &needed))
327 return;
328 has = get_array_size_bytes_max(dest);
329 if (!has)
330 return;
331 if (has >= needed.value)
332 return;
334 dest_name = expr_to_str(dest);
335 sm_msg("error: %s() '%s' too small (%d vs %s)", fn, dest_name, has, sval_to_str(needed));
336 free_string(dest_name);
339 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
341 struct expression *call;
342 struct symbol *left_type, *right_type;
343 int bytes;
344 sval_t sval;
346 if (expr->type != EXPR_ASSIGNMENT)
347 return;
348 right_type = get_pointer_type(expr->right);
349 if (!right_type || type_bits(right_type) != -1)
350 return;
352 call = strip_expr(expr->right);
353 left_type = get_pointer_type(expr->left);
355 if (!parse_call_math(call, math, &sval) || sval.value == 0)
356 return;
357 if (!left_type)
358 return;
359 bytes = type_bytes(left_type);
360 if (bytes <= 0)
361 return;
362 if (sval.uvalue >= bytes)
363 return;
364 sm_msg("error: not allocating enough data %d vs %s", bytes, sval_to_str(sval));
367 static void register_funcs_from_file(void)
369 char name[256];
370 struct token *token;
371 const char *func;
372 int size, buf;
373 struct limiter *limiter;
375 snprintf(name, 256, "%s.sizeof_param", option_project_str);
376 name[255] = '\0';
377 token = get_tokens_file(name);
378 if (!token)
379 return;
380 if (token_type(token) != TOKEN_STREAMBEGIN)
381 return;
382 token = token->next;
383 while (token_type(token) != TOKEN_STREAMEND) {
384 if (token_type(token) != TOKEN_IDENT)
385 return;
386 func = show_ident(token->ident);
388 token = token->next;
389 if (token_type(token) != TOKEN_NUMBER)
390 return;
391 size = atoi(token->number);
393 token = token->next;
394 if (token_type(token) != TOKEN_NUMBER)
395 return;
396 buf = atoi(token->number);
398 limiter = malloc(sizeof(*limiter));
399 limiter->limit_arg = size;
400 limiter->buf_arg = buf;
402 add_function_hook(func, &match_limited, limiter);
404 token = token->next;
406 clear_token_alloc();
409 void check_overflow(int id)
411 my_used_id = id;
412 register_funcs_from_file();
413 add_hook(&match_function_def, FUNC_DEF_HOOK);
414 add_hook(&array_check, OP_HOOK);
415 add_hook(&match_condition, CONDITION_HOOK);
416 add_function_hook("strcpy", &match_strcpy, NULL);
417 add_function_hook("snprintf", &match_snprintf, NULL);
418 add_function_hook("sprintf", &match_sprintf, NULL);
419 add_function_hook("memcmp", &match_limited, &b0_l2);
420 add_function_hook("memcmp", &match_limited, &b1_l2);
421 select_return_states_hook(BUF_SIZE, &db_returns_buf_size);
422 add_modification_hook(my_used_id, &delete);
423 if (option_project == PROJ_KERNEL) {
424 add_function_hook("copy_to_user", &match_limited, &b1_l2);
425 add_function_hook("_copy_to_user", &match_limited, &b1_l2);
426 add_function_hook("__copy_to_user", &match_limited, &b1_l2);
427 add_function_hook("copy_from_user", &match_limited, &b0_l2);
428 add_function_hook("_copy_from_user", &match_limited, &b0_l2);
429 add_function_hook("__copy_from_user", &match_limited, &b0_l2);