2 * smatch/check_overflow.c
4 * Copyright (C) 2010 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
13 #include "smatch_slist.h"
14 #include "smatch_extra.h"
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
)
41 static struct limiter b0_l2
= {0, 2};
42 static struct limiter b1_l2
= {1, 2};
44 static void print_args(struct expression
*expr
, int size
)
55 name
= get_variable_from_expr(expr
, &sym
);
60 FOR_EACH_PTR(this_func
->ctype
.base_type
->arguments
, arg
) {
61 arg_name
= (arg
->ident
?arg
->ident
->name
:"-");
62 if (sym
== arg
&& !strcmp(name
, arg_name
)) {
63 sm_info("param %d array index. size %d", i
, size
);
67 } END_FOR_EACH_PTR(arg
);
72 static void delete(const char *name
, struct symbol
*sym
, struct expression
*expr
, void *unused
)
74 delete_state(my_used_id
, name
, sym
);
77 static int definitely_just_used_as_limiter(struct expression
*array
, struct expression
*offset
)
80 struct expression
*tmp
;
84 if (!get_implied_value(offset
, &val
))
86 if (get_array_size(array
) != val
)
89 FOR_EACH_PTR_REVERSE(big_expression_stack
, tmp
) {
94 if (tmp
->type
== EXPR_PREOP
&& tmp
->op
== '(')
96 if (tmp
->op
== '.' && !dot_ops
++)
98 if (step
== 1 && tmp
->op
== '&') {
102 if (step
== 2 && tmp
->type
== EXPR_COMPARE
)
105 } END_FOR_EACH_PTR_REVERSE(tmp
);
109 static void array_check(struct expression
*expr
)
111 struct expression
*array_expr
;
113 struct expression
*offset
;
117 expr
= strip_expr(expr
);
121 array_expr
= strip_parens(expr
->unop
->left
);
122 array_size
= get_array_size(array_expr
);
123 if (!array_size
|| array_size
== 1)
126 offset
= get_array_offset(expr
);
127 if (!get_fuzzy_max(offset
, &max
)) {
128 if (getting_address())
130 set_state_expr(my_used_id
, offset
, alloc_state_num(array_size
));
131 add_modification_hook_expr(my_used_id
, offset
, &delete, NULL
);
132 print_args(offset
, array_size
);
133 } else if (array_size
<= max
) {
134 const char *level
= "error";
136 if (getting_address())
139 if (definitely_just_used_as_limiter(array_expr
, offset
))
142 if (!option_spammy
) {
143 struct smatch_state
*state
;
145 state
= get_state_expr(SMATCH_EXTRA
, offset
);
146 if (state
&& is_whole_range(state
))
150 name
= get_variable_from_expr_complex(array_expr
, NULL
);
151 /* Blast. Smatch can't figure out glibc's strcmp __strcmp_cg()
152 * so it prints an error every time you compare to a string
153 * literal array with 4 or less chars.
155 if (name
&& strcmp(name
, "__s1") && strcmp(name
, "__s2")) {
156 sm_msg("%s: buffer overflow '%s' %d <= %lld",
157 level
, name
, array_size
, max
);
163 static void match_condition(struct expression
*expr
)
167 struct state_list
*slist
;
168 struct sm_state
*tmp
;
171 if (!expr
|| expr
->type
!= EXPR_COMPARE
)
173 if (get_implied_value(expr
->left
, &val
))
175 else if (get_implied_value(expr
->right
, &val
))
181 slist
= get_possible_states_expr(my_used_id
, expr
->right
);
183 slist
= get_possible_states_expr(my_used_id
, expr
->left
);
186 FOR_EACH_PTR(slist
, tmp
) {
187 if (tmp
->state
== &merged
)
189 boundary
= PTR_INT(tmp
->state
->data
);
191 if (boundary
< 1 && boundary
> -1) {
194 name
= get_variable_from_expr((left
? expr
->right
: expr
->left
), NULL
);
195 sm_msg("error: testing array offset '%s' after use.", name
);
198 } END_FOR_EACH_PTR(tmp
);
201 static void match_strcpy(const char *fn
, struct expression
*expr
, void *unused
)
203 struct expression
*dest
;
204 struct expression
*data
;
205 char *dest_name
= NULL
;
206 char *data_name
= NULL
;
210 dest
= get_argument_from_call_expr(expr
->args
, 0);
211 data
= get_argument_from_call_expr(expr
->args
, 1);
212 dest_size
= get_array_size_bytes(dest
);
213 data_size
= get_array_size_bytes(data
);
218 /* If the size of both arrays is known and the destination
219 * buffer is larger than the source buffer, we're okay.
221 if (data_size
&& dest_size
>= data_size
)
224 dest_name
= get_variable_from_expr_complex(dest
, NULL
);
225 data_name
= get_variable_from_expr_complex(data
, NULL
);
228 sm_msg("error: %s() '%s' too large for '%s' (%d vs %d)",
229 fn
, data_name
, dest_name
, data_size
, dest_size
);
230 else if (option_spammy
)
231 sm_msg("warn: %s() '%s' of unknown size might be too large for '%s'",
232 fn
, data_name
, dest_name
);
234 free_string(dest_name
);
235 free_string(data_name
);
238 static void match_snprintf(const char *fn
, struct expression
*expr
, void *unused
)
240 struct expression
*dest
;
241 struct expression
*dest_size_expr
;
242 struct expression
*format_string
;
243 struct expression
*data
;
244 char *data_name
= NULL
;
246 long long limit_size
;
250 dest
= get_argument_from_call_expr(expr
->args
, 0);
251 dest_size_expr
= get_argument_from_call_expr(expr
->args
, 1);
252 format_string
= get_argument_from_call_expr(expr
->args
, 2);
253 data
= get_argument_from_call_expr(expr
->args
, 3);
255 dest_size
= get_array_size_bytes(dest
);
256 if (!get_implied_value(dest_size_expr
, &limit_size
))
258 if (dest_size
&& dest_size
< limit_size
)
259 sm_msg("error: snprintf() is printing too much %lld vs %d", limit_size
, dest_size
);
260 format
= get_variable_from_expr(format_string
, NULL
);
263 if (strcmp(format
, "\"%s\""))
265 data_name
= get_variable_from_expr_complex(data
, NULL
);
266 data_size
= get_array_size_bytes(data
);
267 if (limit_size
< data_size
)
268 sm_msg("error: snprintf() chops off the last chars of '%s': %d vs %lld",
269 data_name
, data_size
, limit_size
);
271 free_string(data_name
);
275 static void match_sprintf(const char *fn
, struct expression
*expr
, void *unused
)
277 struct expression
*dest
;
278 struct expression
*format_string
;
279 struct expression
*data
;
280 char *data_name
= NULL
;
285 dest
= get_argument_from_call_expr(expr
->args
, 0);
286 format_string
= get_argument_from_call_expr(expr
->args
, 1);
287 data
= get_argument_from_call_expr(expr
->args
, 2);
289 dest_size
= get_array_size_bytes(dest
);
292 format
= get_variable_from_expr(format_string
, NULL
);
295 if (strcmp(format
, "\"%s\""))
297 data_name
= get_variable_from_expr_complex(data
, NULL
);
298 data_size
= get_array_size_bytes(data
);
299 if (dest_size
< data_size
)
300 sm_msg("error: sprintf() copies too much data from '%s': %d vs %d",
301 data_name
, data_size
, dest_size
);
303 free_string(data_name
);
307 static void match_limited(const char *fn
, struct expression
*expr
, void *_limiter
)
309 struct limiter
*limiter
= (struct limiter
*)_limiter
;
310 struct expression
*dest
;
311 struct expression
*data
;
312 char *dest_name
= NULL
;
316 dest
= get_argument_from_call_expr(expr
->args
, limiter
->buf_arg
);
317 data
= get_argument_from_call_expr(expr
->args
, limiter
->limit_arg
);
318 if (!get_fuzzy_max(data
, &needed
))
320 has
= get_array_size_bytes(dest
);
326 dest_name
= get_variable_from_expr_complex(dest
, NULL
);
327 sm_msg("error: %s() '%s' too small (%d vs %lld)", fn
, dest_name
, has
, needed
);
328 free_string(dest_name
);
331 static void match_array_func(const char *fn
, struct expression
*expr
, void *info
)
333 struct bound
*bound_info
= (struct bound
*)info
;
334 struct expression
*arg
;
337 arg
= get_argument_from_call_expr(expr
->args
, bound_info
->param
);
338 if (!get_implied_max(arg
, &offset
))
340 if (offset
>= bound_info
->size
)
341 sm_msg("error: buffer overflow calling %s. param %d. %lld >= %d",
342 fn
, bound_info
->param
, offset
, bound_info
->size
);
345 static void register_array_funcs(void)
349 struct bound
*bound_info
= NULL
;
352 snprintf(name
, 256, "%s.array_bounds", option_project_str
);
353 token
= get_tokens_file(name
);
356 if (token_type(token
) != TOKEN_STREAMBEGIN
)
359 while (token_type(token
) != TOKEN_STREAMEND
) {
360 bound_info
= malloc(sizeof(*bound_info
));
361 if (token_type(token
) != TOKEN_IDENT
)
363 func
= show_ident(token
->ident
);
365 if (token_type(token
) != TOKEN_NUMBER
)
367 bound_info
->param
= atoi(token
->number
);
369 if (token_type(token
) != TOKEN_NUMBER
)
371 bound_info
->size
= atoi(token
->number
);
372 add_function_hook(func
, &match_array_func
, bound_info
);
375 if (token_type(token
) != TOKEN_STREAMEND
) {
376 printf("failed to load %s (line %d)", name
, token
->pos
.line
);
382 void check_overflow(int id
)
385 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
386 add_hook(&array_check
, OP_HOOK
);
387 add_hook(&match_condition
, CONDITION_HOOK
);
388 add_function_hook("strcpy", &match_strcpy
, NULL
);
389 add_function_hook("snprintf", &match_snprintf
, NULL
);
390 add_function_hook("sprintf", &match_sprintf
, NULL
);
391 if (option_project
== PROJ_KERNEL
) {
392 add_function_hook("copy_to_user", &match_limited
, &b0_l2
);
393 add_function_hook("copy_to_user", &match_limited
, &b1_l2
);
394 add_function_hook("_copy_to_user", &match_limited
, &b0_l2
);
395 add_function_hook("_copy_to_user", &match_limited
, &b1_l2
);
396 add_function_hook("__copy_to_user", &match_limited
, &b0_l2
);
397 add_function_hook("__copy_to_user", &match_limited
, &b1_l2
);
398 add_function_hook("copy_from_user", &match_limited
, &b0_l2
);
399 add_function_hook("copy_from_user", &match_limited
, &b1_l2
);
400 add_function_hook("_copy_from_user", &match_limited
, &b0_l2
);
401 add_function_hook("_copy_from_user", &match_limited
, &b1_l2
);
402 add_function_hook("__copy_from_user", &match_limited
, &b0_l2
);
403 add_function_hook("__copy_from_user", &match_limited
, &b1_l2
);
406 register_array_funcs();