dev_queue_xmit: turn on by default
[smatch.git] / check_overflow.c
blob50f4e88e2c9f57d757485c895d4352033e0c4e1b
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 long long val;
52 struct expression *tmp;
53 int step = 0;
54 int dot_ops = 0;
56 if (!get_implied_value(offset, &val))
57 return 0;
58 if (get_array_size(array) != val)
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 void array_check(struct expression *expr)
85 struct expression *array_expr;
86 int array_size;
87 struct expression *offset;
88 long long max;
89 char *name;
91 expr = strip_expr(expr);
92 if (!is_array(expr))
93 return;
95 array_expr = strip_parens(expr->unop->left);
96 array_size = get_array_size(array_expr);
97 if (!array_size || array_size == 1)
98 return;
100 offset = get_array_offset(expr);
101 if (!get_fuzzy_max(offset, &max)) {
102 if (getting_address())
103 return;
104 set_state_expr(my_used_id, offset, alloc_state_num(array_size));
105 } else if (array_size <= max) {
106 const char *level = "error";
108 if (getting_address())
109 level = "warn";
111 if (definitely_just_used_as_limiter(array_expr, offset))
112 return;
114 if (!option_spammy) {
115 struct smatch_state *state;
117 state = get_state_expr(SMATCH_EXTRA, offset);
118 if (state && is_whole_range(state))
119 return;
122 name = get_variable_from_expr_complex(array_expr, NULL);
123 /* Blast. Smatch can't figure out glibc's strcmp __strcmp_cg()
124 * so it prints an error every time you compare to a string
125 * literal array with 4 or less chars.
127 if (name && strcmp(name, "__s1") && strcmp(name, "__s2")) {
128 sm_msg("%s: buffer overflow '%s' %d <= %lld",
129 level, name, array_size, max);
131 free_string(name);
135 static void match_condition(struct expression *expr)
137 int left;
138 long long val;
139 struct state_list *slist;
140 struct sm_state *tmp;
141 int boundary;
143 if (!expr || expr->type != EXPR_COMPARE)
144 return;
145 if (get_macro_name(expr->pos))
146 return;
147 if (get_implied_value(expr->left, &val))
148 left = 1;
149 else if (get_implied_value(expr->right, &val))
150 left = 0;
151 else
152 return;
154 if (left)
155 slist = get_possible_states_expr(my_used_id, expr->right);
156 else
157 slist = get_possible_states_expr(my_used_id, expr->left);
158 if (!slist)
159 return;
160 FOR_EACH_PTR(slist, tmp) {
161 if (tmp->state == &merged || tmp->state == &undefined)
162 continue;
163 boundary = PTR_INT(tmp->state->data);
164 boundary -= val;
165 if (boundary < 1 && boundary > -1) {
166 char *name;
168 name = get_variable_from_expr((left ? expr->right : expr->left), NULL);
169 sm_msg("error: testing array offset '%s' after use.", name);
170 return;
172 } END_FOR_EACH_PTR(tmp);
175 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
177 struct expression *dest;
178 struct expression *data;
179 char *dest_name = NULL;
180 char *data_name = NULL;
181 int dest_size;
182 int data_size;
184 dest = get_argument_from_call_expr(expr->args, 0);
185 data = get_argument_from_call_expr(expr->args, 1);
186 dest_size = get_array_size_bytes(dest);
187 data_size = get_array_size_bytes(data);
189 if (!dest_size)
190 return;
192 /* If the size of both arrays is known and the destination
193 * buffer is larger than the source buffer, we're okay.
195 if (data_size && dest_size >= data_size)
196 return;
198 dest_name = get_variable_from_expr_complex(dest, NULL);
199 data_name = get_variable_from_expr_complex(data, NULL);
201 if (data_size)
202 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
203 fn, data_name, dest_name, data_size, dest_size);
204 else if (option_spammy)
205 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
206 fn, data_name, dest_name);
208 free_string(dest_name);
209 free_string(data_name);
212 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
214 struct expression *dest;
215 struct expression *dest_size_expr;
216 struct expression *format_string;
217 struct expression *data;
218 char *data_name = NULL;
219 int dest_size;
220 long long limit_size;
221 char *format;
222 int data_size;
224 dest = get_argument_from_call_expr(expr->args, 0);
225 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
226 format_string = get_argument_from_call_expr(expr->args, 2);
227 data = get_argument_from_call_expr(expr->args, 3);
229 dest_size = get_array_size_bytes(dest);
230 if (!get_implied_value(dest_size_expr, &limit_size))
231 return;
232 if (dest_size && dest_size < limit_size)
233 sm_msg("error: snprintf() is printing too much %lld vs %d", limit_size, dest_size);
234 format = get_variable_from_expr(format_string, NULL);
235 if (!format)
236 return;
237 if (strcmp(format, "\"%s\""))
238 goto free;
239 data_name = get_variable_from_expr_complex(data, NULL);
240 data_size = get_array_size_bytes(data);
241 if (limit_size < data_size)
242 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %lld",
243 data_name, data_size, limit_size);
244 free:
245 free_string(data_name);
246 free_string(format);
249 static void match_sprintf(const char *fn, struct expression *expr, void *unused)
251 struct expression *dest;
252 struct expression *format_string;
253 struct expression *data;
254 char *data_name = NULL;
255 int dest_size;
256 char *format;
257 int data_size;
259 dest = get_argument_from_call_expr(expr->args, 0);
260 format_string = get_argument_from_call_expr(expr->args, 1);
261 data = get_argument_from_call_expr(expr->args, 2);
263 dest_size = get_array_size_bytes(dest);
264 if (!dest_size)
265 return;
266 format = get_variable_from_expr(format_string, NULL);
267 if (!format)
268 return;
269 if (strcmp(format, "\"%s\""))
270 goto free;
271 data_name = get_variable_from_expr_complex(data, NULL);
272 data_size = get_array_size_bytes(data);
273 if (dest_size < data_size)
274 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
275 data_name, data_size, dest_size);
276 free:
277 free_string(data_name);
278 free_string(format);
281 static void match_limited(const char *fn, struct expression *expr, void *_limiter)
283 struct limiter *limiter = (struct limiter *)_limiter;
284 struct expression *dest;
285 struct expression *data;
286 char *dest_name = NULL;
287 long long needed;
288 int has;
290 dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
291 data = get_argument_from_call_expr(expr->args, limiter->limit_arg);
292 if (!get_fuzzy_max(data, &needed))
293 return;
294 has = get_array_size_bytes(dest);
295 if (!has)
296 return;
297 if (has >= needed)
298 return;
300 dest_name = get_variable_from_expr_complex(dest, NULL);
301 sm_msg("error: %s() '%s' too small (%d vs %lld)", fn, dest_name, has, needed);
302 free_string(dest_name);
305 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
307 struct expression *call;
308 struct symbol *type;
309 int bytes;
310 long long val;
312 if (expr->type != EXPR_ASSIGNMENT)
313 return;
314 call = strip_expr(expr->right);
315 type = get_pointer_type(expr->left);
317 if (!parse_call_math(call, math, &val) || val == 0)
318 return;
319 if (!type)
320 return;
321 bytes = bits_to_bytes(type->bit_size);
322 if (!bytes)
323 return;
324 if (val >= bytes)
325 return;
326 sm_msg("error: not allocating enough data %d vs %lld", bytes, val);
329 static void register_funcs_from_file(void)
331 char name[256];
332 struct token *token;
333 const char *func;
334 int size, buf;
335 struct limiter *limiter;
337 snprintf(name, 256, "%s.sizeof_param", option_project_str);
338 name[255] = '\0';
339 token = get_tokens_file(name);
340 if (!token)
341 return;
342 if (token_type(token) != TOKEN_STREAMBEGIN)
343 return;
344 token = token->next;
345 while (token_type(token) != TOKEN_STREAMEND) {
346 if (token_type(token) != TOKEN_IDENT)
347 return;
348 func = show_ident(token->ident);
350 token = token->next;
351 if (token_type(token) != TOKEN_NUMBER)
352 return;
353 size = atoi(token->number);
355 token = token->next;
356 if (token_type(token) != TOKEN_NUMBER)
357 return;
358 buf = atoi(token->number);
360 limiter = malloc(sizeof(*limiter));
361 limiter->limit_arg = size;
362 limiter->buf_arg = buf;
364 add_function_hook(func, &match_limited, limiter);
366 token = token->next;
368 clear_token_alloc();
371 void check_overflow(int id)
373 my_used_id = id;
374 register_funcs_from_file();
375 add_hook(&match_function_def, FUNC_DEF_HOOK);
376 add_hook(&array_check, OP_HOOK);
377 add_hook(&match_condition, CONDITION_HOOK);
378 add_function_hook("strcpy", &match_strcpy, NULL);
379 add_function_hook("snprintf", &match_snprintf, NULL);
380 add_function_hook("sprintf", &match_sprintf, NULL);
381 add_function_hook("memcmp", &match_limited, &b0_l2);
382 add_function_hook("memcmp", &match_limited, &b1_l2);
383 add_db_return_states_callback(BUF_SIZE, &db_returns_buf_size);
384 add_modification_hook(my_used_id, &delete);
385 if (option_project == PROJ_KERNEL) {
386 add_function_hook("copy_to_user", &match_limited, &b0_l2);
387 add_function_hook("copy_to_user", &match_limited, &b1_l2);
388 add_function_hook("_copy_to_user", &match_limited, &b0_l2);
389 add_function_hook("_copy_to_user", &match_limited, &b1_l2);
390 add_function_hook("__copy_to_user", &match_limited, &b0_l2);
391 add_function_hook("__copy_to_user", &match_limited, &b1_l2);
392 add_function_hook("copy_from_user", &match_limited, &b0_l2);
393 add_function_hook("copy_from_user", &match_limited, &b1_l2);
394 add_function_hook("_copy_from_user", &match_limited, &b0_l2);
395 add_function_hook("_copy_from_user", &match_limited, &b1_l2);
396 add_function_hook("__copy_from_user", &match_limited, &b0_l2);
397 add_function_hook("__copy_from_user", &match_limited, &b1_l2);