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
21 #include "smatch_slist.h"
22 #include "smatch_extra.h"
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
)
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
)
60 struct expression
*tmp
;
64 if (!get_implied_value(offset
, &sval
))
66 if (get_array_size(array
) != sval
.value
)
69 FOR_EACH_PTR_REVERSE(big_expression_stack
, tmp
) {
74 if (tmp
->type
== EXPR_PREOP
&& tmp
->op
== '(')
76 if (tmp
->op
== '.' && !dot_ops
++)
78 if (step
== 1 && tmp
->op
== '&') {
82 if (step
== 2 && tmp
->type
== EXPR_COMPARE
)
84 if (step
== 2 && tmp
->type
== EXPR_ASSIGNMENT
)
87 } END_FOR_EACH_PTR_REVERSE(tmp
);
91 static int get_the_max(struct expression
*expr
, sval_t
*sval
)
93 if (get_hard_max(expr
, sval
))
97 if (get_fuzzy_max(expr
, sval
))
99 if (is_user_data(expr
))
100 return get_absolute_max(expr
, sval
);
104 static int common_false_positives(struct expression
*array
, char *name
, sval_t max
)
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)
116 /* Ugh... People are saying that Smatch still barfs on glibc strcmp()
119 if (array
&& array
->type
== EXPR_STRING
) {
122 if (max
.value
== array
->string
->length
)
125 macro
= get_macro_name(array
->pos
);
127 (strcmp(macro
, "strcmp") == 0 ||
128 strcmp(macro
, "strncmp") == 0))
133 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
134 * how it's used so it causes a bunch of false positives.
136 if (option_project
== PROJ_KERNEL
&&
137 strcmp(name
, "__per_cpu_offset") == 0)
142 static void array_check(struct expression
*expr
)
144 struct expression
*array_expr
;
146 struct expression
*offset
;
150 expr
= strip_expr(expr
);
154 array_expr
= get_array_base(expr
);
155 array_size
= get_array_size(array_expr
);
156 if (!array_size
|| array_size
== 1)
159 offset
= get_array_offset(expr
);
160 if (!get_the_max(offset
, &max
)) {
161 if (getting_address())
163 if (is_capped(offset
))
165 set_state_expr(my_used_id
, offset
, alloc_state_num(array_size
));
166 } else if (array_size
<= max
.value
) {
167 const char *level
= "error";
169 if (getting_address())
172 if (definitely_just_used_as_limiter(array_expr
, offset
))
175 array_expr
= strip_expr(array_expr
);
176 name
= expr_to_str(array_expr
);
177 if (!common_false_positives(array_expr
, name
, max
)) {
178 sm_msg("%s: buffer overflow '%s' %d <= %s",
179 level
, name
, array_size
, sval_to_str(max
));
185 static void match_condition(struct expression
*expr
)
189 struct state_list
*slist
;
190 struct sm_state
*tmp
;
193 if (!expr
|| expr
->type
!= EXPR_COMPARE
)
195 if (get_macro_name(expr
->pos
))
197 if (get_implied_value(expr
->left
, &sval
))
199 else if (get_implied_value(expr
->right
, &sval
))
205 slist
= get_possible_states_expr(my_used_id
, expr
->right
);
207 slist
= get_possible_states_expr(my_used_id
, expr
->left
);
210 FOR_EACH_PTR(slist
, tmp
) {
211 if (tmp
->state
== &merged
|| tmp
->state
== &undefined
)
213 boundary
= PTR_INT(tmp
->state
->data
);
214 boundary
-= sval
.value
;
215 if (boundary
< 1 && boundary
> -1) {
218 name
= expr_to_var(left
? expr
->right
: expr
->left
);
219 sm_msg("error: testing array offset '%s' after use.", name
);
222 } END_FOR_EACH_PTR(tmp
);
225 static void match_strcpy(const char *fn
, struct expression
*expr
, void *unused
)
227 struct expression
*dest
;
228 struct expression
*data
;
229 char *dest_name
= NULL
;
230 char *data_name
= NULL
;
234 dest
= get_argument_from_call_expr(expr
->args
, 0);
235 data
= get_argument_from_call_expr(expr
->args
, 1);
236 dest_size
= get_array_size_bytes(dest
);
240 data_size
= get_size_from_strlen(data
);
242 data_size
= get_array_size_bytes(data
);
244 /* If the size of both arrays is known and the destination
245 * buffer is larger than the source buffer, we're okay.
247 if (data_size
&& dest_size
>= data_size
)
250 dest_name
= expr_to_str(dest
);
251 data_name
= expr_to_str(data
);
254 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
255 fn
, data_name
, dest_name
, data_size
, dest_size
);
256 else if (option_spammy
)
257 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
258 fn
, data_name
, dest_name
);
260 free_string(dest_name
);
261 free_string(data_name
);
264 static void match_snprintf(const char *fn
, struct expression
*expr
, void *unused
)
266 struct expression
*dest
;
267 struct expression
*dest_size_expr
;
268 struct expression
*format_string
;
269 struct expression
*data
;
270 char *data_name
= NULL
;
276 dest
= get_argument_from_call_expr(expr
->args
, 0);
277 dest_size_expr
= get_argument_from_call_expr(expr
->args
, 1);
278 format_string
= get_argument_from_call_expr(expr
->args
, 2);
279 data
= get_argument_from_call_expr(expr
->args
, 3);
281 dest_size
= get_array_size_bytes(dest
);
282 if (!get_implied_value(dest_size_expr
, &limit_size
))
284 if (dest_size
&& dest_size
< limit_size
.value
)
285 sm_msg("error: snprintf() is printing too much %s vs %d",
286 sval_to_str(limit_size
), dest_size
);
287 format
= expr_to_var(format_string
);
290 if (strcmp(format
, "\"%s\""))
292 data_name
= expr_to_str(data
);
293 data_size
= get_size_from_strlen(data
);
295 data_size
= get_array_size_bytes(data
);
296 if (limit_size
.value
< data_size
)
297 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %s",
298 data_name
, data_size
, sval_to_str(limit_size
));
300 free_string(data_name
);
304 static void match_sprintf(const char *fn
, struct expression
*expr
, void *unused
)
306 struct expression
*dest
;
307 struct expression
*format_string
;
308 struct expression
*data
;
309 char *data_name
= NULL
;
314 dest
= get_argument_from_call_expr(expr
->args
, 0);
315 format_string
= get_argument_from_call_expr(expr
->args
, 1);
316 data
= get_argument_from_call_expr(expr
->args
, 2);
318 dest_size
= get_array_size_bytes(dest
);
321 format
= expr_to_var(format_string
);
324 if (strcmp(format
, "\"%s\""))
326 data_name
= expr_to_str(data
);
327 data_size
= get_size_from_strlen(data
);
329 data_size
= get_array_size_bytes(data
);
330 if (dest_size
< data_size
)
331 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
332 data_name
, data_size
, dest_size
);
334 free_string(data_name
);
338 static void match_limited(const char *fn
, struct expression
*expr
, void *_limiter
)
340 struct limiter
*limiter
= (struct limiter
*)_limiter
;
341 struct expression
*dest
;
342 struct expression
*data
;
343 char *dest_name
= NULL
;
347 dest
= get_argument_from_call_expr(expr
->args
, limiter
->buf_arg
);
348 data
= get_argument_from_call_expr(expr
->args
, limiter
->limit_arg
);
349 if (!get_the_max(data
, &needed
))
351 has
= get_array_size_bytes_max(dest
);
354 if (has
>= needed
.value
)
357 dest_name
= expr_to_str(dest
);
358 sm_msg("error: %s() '%s' too small (%d vs %s)", fn
, dest_name
, has
, sval_to_str(needed
));
359 free_string(dest_name
);
362 static void db_returns_buf_size(struct expression
*expr
, int param
, char *unused
, char *math
)
364 struct expression
*call
;
365 struct symbol
*left_type
, *right_type
;
369 if (expr
->type
!= EXPR_ASSIGNMENT
)
371 right_type
= get_pointer_type(expr
->right
);
372 if (!right_type
|| type_bits(right_type
) != -1)
375 call
= strip_expr(expr
->right
);
376 left_type
= get_pointer_type(expr
->left
);
378 if (!parse_call_math(call
, math
, &sval
) || sval
.value
== 0)
382 bytes
= type_bytes(left_type
);
385 if (sval
.uvalue
>= bytes
)
387 sm_msg("error: not allocating enough data %d vs %s", bytes
, sval_to_str(sval
));
390 static void register_funcs_from_file(void)
396 struct limiter
*limiter
;
398 snprintf(name
, 256, "%s.sizeof_param", option_project_str
);
400 token
= get_tokens_file(name
);
403 if (token_type(token
) != TOKEN_STREAMBEGIN
)
406 while (token_type(token
) != TOKEN_STREAMEND
) {
407 if (token_type(token
) != TOKEN_IDENT
)
409 func
= show_ident(token
->ident
);
412 if (token_type(token
) != TOKEN_NUMBER
)
414 size
= atoi(token
->number
);
417 if (token_type(token
) != TOKEN_NUMBER
)
419 buf
= atoi(token
->number
);
421 limiter
= malloc(sizeof(*limiter
));
422 limiter
->limit_arg
= size
;
423 limiter
->buf_arg
= buf
;
425 add_function_hook(func
, &match_limited
, limiter
);
432 void check_overflow(int id
)
435 register_funcs_from_file();
436 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
437 add_hook(&array_check
, OP_HOOK
);
438 add_hook(&match_condition
, CONDITION_HOOK
);
439 add_function_hook("strcpy", &match_strcpy
, NULL
);
440 add_function_hook("snprintf", &match_snprintf
, NULL
);
441 add_function_hook("sprintf", &match_sprintf
, NULL
);
442 add_function_hook("memcmp", &match_limited
, &b0_l2
);
443 add_function_hook("memcmp", &match_limited
, &b1_l2
);
444 select_return_states_hook(BUF_SIZE
, &db_returns_buf_size
);
445 add_modification_hook(my_used_id
, &delete);
446 if (option_project
== PROJ_KERNEL
) {
447 add_function_hook("copy_to_user", &match_limited
, &b1_l2
);
448 add_function_hook("_copy_to_user", &match_limited
, &b1_l2
);
449 add_function_hook("__copy_to_user", &match_limited
, &b1_l2
);
450 add_function_hook("copy_from_user", &match_limited
, &b0_l2
);
451 add_function_hook("_copy_from_user", &match_limited
, &b0_l2
);
452 add_function_hook("__copy_from_user", &match_limited
, &b0_l2
);