index_overflow: small cleanup
[smatch.git] / check_index_overflow.c
blobd2655ecad7e000f0bd861d637ef434e5c93d9b58
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 static int definitely_just_used_as_limiter(struct expression *array, struct expression *offset)
26 sval_t sval;
27 struct expression *tmp;
28 int step = 0;
29 int dot_ops = 0;
31 if (!get_implied_value(offset, &sval))
32 return 0;
33 if (get_array_size(array) != sval.value)
34 return 0;
36 FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
37 if (step == 0) {
38 step = 1;
39 continue;
41 if (tmp->type == EXPR_PREOP && tmp->op == '(')
42 continue;
43 if (tmp->op == '.' && !dot_ops++)
44 continue;
45 if (step == 1 && tmp->op == '&') {
46 step = 2;
47 continue;
49 if (step == 2 && tmp->type == EXPR_COMPARE)
50 return 1;
51 if (step == 2 && tmp->type == EXPR_ASSIGNMENT)
52 return 1;
53 return 0;
54 } END_FOR_EACH_PTR_REVERSE(tmp);
55 return 0;
58 static int get_the_max(struct expression *expr, sval_t *sval)
60 struct range_list *rl;
62 if (get_hard_max(expr, sval))
63 return 1;
64 if (!option_spammy)
65 return 0;
66 if (get_fuzzy_max(expr, sval))
67 return 1;
68 if (!get_user_rl(expr, &rl))
69 return 0;
70 *sval = rl_max(rl);
71 return 1;
74 static int common_false_positives(struct expression *array, char *name, sval_t max)
76 if (!name)
77 return 0;
79 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
80 * so it prints an error every time you compare to a string
81 * literal array with 4 or less chars.
83 if (strcmp(name, "__s1") == 0 || strcmp(name, "__s2") == 0)
84 return 1;
86 /* Ugh... People are saying that Smatch still barfs on glibc strcmp()
87 * functions.
89 if (array) {
90 char *macro;
92 /* why is this again??? */
93 if (array->type == EXPR_STRING &&
94 max.value == array->string->length)
95 return 1;
97 macro = get_macro_name(array->pos);
98 if (macro && max.uvalue < 4 &&
99 (strcmp(macro, "strcmp") == 0 ||
100 strcmp(macro, "strncmp") == 0 ||
101 strcmp(macro, "streq") == 0 ||
102 strcmp(macro, "strneq") == 0 ||
103 strcmp(macro, "strsep") == 0))
104 return 1;
108 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
109 * how it's used so it causes a bunch of false positives.
111 if (option_project == PROJ_KERNEL &&
112 strcmp(name, "__per_cpu_offset") == 0)
113 return 1;
114 return 0;
117 static void array_check(struct expression *expr)
119 struct expression *array_expr;
120 const char *level = "error";
121 int array_size;
122 struct expression *offset;
123 sval_t max;
124 char *name;
126 expr = strip_expr(expr);
127 if (!is_array(expr))
128 return;
130 if (is_impossible_path())
131 return;
132 array_expr = get_array_base(expr);
133 array_size = get_array_size(array_expr);
134 if (!array_size || array_size == 1)
135 return;
137 offset = get_array_offset(expr);
138 if (!get_the_max(offset, &max))
139 return;
140 if (array_size > max.value)
141 return;
142 if (getting_address())
143 level = "warn";
145 if (definitely_just_used_as_limiter(array_expr, offset))
146 return;
148 array_expr = strip_expr(array_expr);
149 name = expr_to_str(array_expr);
150 if (common_false_positives(array_expr, name, max))
151 goto free;
153 sm_msg("%s: buffer overflow '%s' %d <= %s",
154 level, name, array_size, sval_to_str(max));
156 free:
157 free_string(name);
160 void check_index_overflow(int id)
162 add_hook(&array_check, OP_HOOK);