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"
28 static int definitely_just_used_as_limiter(struct expression
*array
, struct expression
*offset
)
31 struct expression
*tmp
;
33 if (!get_implied_value(offset
, &sval
))
35 if (get_array_size(array
) != sval
.value
)
39 while ((tmp
= expr_get_parent_expr(tmp
))) {
40 if (tmp
->type
== EXPR_PREOP
&& tmp
->op
== '&')
47 static int fake_get_hard_max(struct expression
*expr
, sval_t
*sval
)
49 struct range_list
*implied_rl
;
51 if (!get_hard_max(expr
, sval
))
55 * The problem is that hard_max doesn't care about minimums
56 * properly. So if you give it thing like:
58 * __smatch_hard_max(-err);
60 * Then it returns s32max instead of 10.
63 if (get_implied_rl(expr
, &implied_rl
) &&
64 sval_cmp(rl_max(implied_rl
), *sval
) < 0)
65 *sval
= rl_max(implied_rl
);
69 static int get_the_max(struct expression
*expr
, sval_t
*sval
)
71 struct range_list
*rl
;
73 if (get_hard_max(expr
, sval
)) {
74 struct range_list
*implied_rl
;
77 * The problem is that hard_max doesn't care about minimums
78 * properly. So if you give it thing like:
80 * __smatch_hard_max(-err);
82 * Then it returns s32max instead of 10.
85 if (get_implied_rl(expr
, &implied_rl
) &&
86 sval_cmp(rl_max(implied_rl
), *sval
) < 0)
87 *sval
= rl_max(implied_rl
);
93 /* Fixme: use fuzzy max */
95 if (!get_user_rl(expr
, &rl
))
97 if (rl_max(rl
).uvalue
> sval_type_max(rl_type(rl
)).uvalue
- 4 &&
105 static int common_false_positives(struct expression
*array
, sval_t max
)
110 name
= expr_to_str(array
);
112 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
113 * so it prints an error every time you compare to a string
114 * literal array with 4 or less chars.
117 (strcmp(name
, "__s1") == 0 || strcmp(name
, "__s2") == 0)) {
122 /* Ugh... People are saying that Smatch still barfs on glibc strcmp()
128 /* why is this again??? */
129 if (array
->type
== EXPR_STRING
&&
130 max
.value
== array
->string
->length
) {
135 macro
= get_macro_name(array
->pos
);
136 if (macro
&& max
.uvalue
< 4 &&
137 (strcmp(macro
, "strcmp") == 0 ||
138 strcmp(macro
, "strncmp") == 0 ||
139 strcmp(macro
, "streq") == 0 ||
140 strcmp(macro
, "strneq") == 0 ||
141 strcmp(macro
, "strsep") == 0)) {
148 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
149 * how it's used so it causes a bunch of false positives.
151 if (option_project
== PROJ_KERNEL
&& name
&&
152 strcmp(name
, "__per_cpu_offset") == 0) {
163 static int is_subtract(struct expression
*expr
)
165 struct expression
*tmp
;
168 expr
= strip_expr(expr
);
169 while ((tmp
= get_assigned_expr(expr
))) {
170 expr
= strip_expr(tmp
);
175 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '-')
180 static int constraint_met(struct expression
*array_expr
, struct expression
*offset
)
182 char *data_str
, *required
, *unmet
;
185 data_str
= get_constraint_str(array_expr
);
189 required
= get_required_constraint(data_str
);
193 unmet
= unmet_constraint(array_expr
, offset
);
197 free_string(required
);
200 free_string(data_str
);
204 static bool is_zero_size_memcpy(struct expression
*expr
, int size
, struct range_list
*rl
)
206 struct expression
*parent
;
209 * Often times we have code like this:
210 * memcpy(array[idx], src, size)
211 * In this example if "idx == ARRAY_SIZE()" then "size" is zero so
212 * nothing is copied and the code is fine and Smatch should not
213 * print a warning even though the idx is one element out of bounds.
215 * TODO: if we wanted to be very accurate we could find the length
216 * expression and assume() that offset == rl_max() and then test that
217 * the length expression is zero. But that seems like a lot of work.
221 if (rl_max(rl
).value
!= size
)
225 while ((parent
= expr_get_parent_expr(parent
))) {
226 if (parent
->type
== EXPR_PREOP
&&
227 (parent
->op
== '(' || parent
->op
== '&'))
229 if (parent
->type
== EXPR_CAST
)
233 if (!parent
|| parent
->type
!= EXPR_CALL
||
234 parent
->fn
->type
!= EXPR_SYMBOL
|| !parent
->fn
->symbol_name
)
237 if (strstr(parent
->fn
->symbol_name
->name
, "memcpy") ||
238 strstr(parent
->fn
->symbol_name
->name
, "memset"))
244 static int should_warn(struct expression
*expr
)
246 struct expression
*array_expr
;
247 struct range_list
*abs_rl
;
248 sval_t hard_max
= { .type
= &int_ctype
, };
249 sval_t fuzzy_max
= { .type
= &int_ctype
, };
251 struct expression
*offset
;
254 expr
= strip_expr(expr
);
258 if (is_impossible_path())
260 array_expr
= get_array_base(expr
);
261 array_size
= get_array_size(array_expr
);
262 if (!array_size
|| array_size
== 1)
265 offset
= get_array_offset(expr
);
266 get_absolute_rl(offset
, &abs_rl
);
267 fake_get_hard_max(offset
, &hard_max
);
268 get_fuzzy_max(offset
, &fuzzy_max
);
270 if (!get_the_max(offset
, &max
))
272 if (array_size
> max
.value
)
274 if (buf_comparison_index_ok(expr
))
276 if (constraint_met(array_expr
, offset
))
279 if (array_size
> rl_max(abs_rl
).uvalue
)
282 if (definitely_just_used_as_limiter(array_expr
, offset
))
285 array_expr
= strip_expr(array_expr
);
286 if (common_false_positives(array_expr
, max
))
289 if (impossibly_high_comparison(offset
))
292 if (is_zero_size_memcpy(expr
, array_size
, abs_rl
))
298 static int is_because_of_no_break(struct expression
*offset
)
300 if (get_state_expr(loop_id
, offset
) == &loop_end
)
305 static void array_check(struct expression
*expr
)
307 struct expression
*array_expr
;
308 struct range_list
*abs_rl
;
309 struct range_list
*user_rl
= NULL
;
310 sval_t hard_max
= { .type
= &int_ctype
, };
311 sval_t fuzzy_max
= { .type
= &int_ctype
, };
313 struct expression
*array_size_value
, *comparison
;
314 struct expression
*offset
;
319 if (!should_warn(expr
))
322 expr
= strip_expr(expr
);
323 array_expr
= get_array_base(expr
);
324 array_size
= get_array_size(array_expr
);
325 offset
= get_array_offset(expr
);
328 * Perhaps if the offset is out of bounds that means a constraint
329 * applies or maybe it means we are on an impossible path. So test
330 * again based on that assumption.
333 array_size_value
= value_expr(array_size
);
334 comparison
= compare_expression(offset
, SPECIAL_GTE
, array_size_value
);
335 if (assume(comparison
)) {
336 if (!should_warn(expr
)) {
340 no_break
= is_because_of_no_break(offset
);
344 get_absolute_rl(offset
, &abs_rl
);
345 get_user_rl(offset
, &user_rl
);
346 fake_get_hard_max(offset
, &hard_max
);
347 get_fuzzy_max(offset
, &fuzzy_max
);
349 array_expr
= strip_expr(array_expr
);
350 name
= expr_to_str(array_expr
);
353 max
= rl_max(user_rl
);
355 max
= rl_max(abs_rl
);
357 if (!option_spammy
&& is_subtract(offset
))
361 sm_error("buffer overflow '%s' %d <= %s (assuming for loop doesn't break)",
362 name
, array_size
, sval_to_str(max
));
363 } else if (user_rl
) {
364 sm_error("buffer overflow '%s' %d <= %s user_rl='%s'%s",
365 name
, array_size
, sval_to_str(max
), show_rl(user_rl
),
366 is_subtract(offset
) ? " subtract" : "");
368 sm_error("buffer overflow '%s' %d <= %s%s",
369 name
, array_size
, sval_to_str(max
),
370 is_subtract(offset
) ? " subtract" : "");
376 void check_index_overflow(int id
)
378 add_hook(&array_check
, OP_HOOK
);
381 static void match_condition(struct expression
*expr
)
383 struct statement
*stmt
;
385 if (expr
->type
!= EXPR_COMPARE
)
387 if (expr
->op
!= '<' && expr
->op
!= SPECIAL_UNSIGNED_LT
)
390 stmt
= expr_get_parent_stmt(expr
);
391 if (!stmt
|| stmt
->type
!= STMT_ITERATOR
)
394 set_true_false_states_expr(loop_id
, expr
->left
, NULL
, &loop_end
);
397 void check_index_overflow_loop_marker(int id
)
401 add_hook(&match_condition
, CONDITION_HOOK
);
402 add_modification_hook(loop_id
, &set_undefined
);