modification_hooks: handle PARAM_SET earlier
[smatch.git] / check_index_overflow.c
blob54202265d1ffa39860b976616da3e8877c1e8931
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 (!is_user_data(expr))
69 return 0;
70 if (!get_user_rl(expr, &rl))
71 return 0;
72 *sval = rl_max(rl);
73 return 1;
76 static int common_false_positives(struct expression *array, char *name, sval_t max)
78 if (!name)
79 return 0;
81 /* Smatch can't figure out glibc's strcmp __strcmp_cg()
82 * so it prints an error every time you compare to a string
83 * literal array with 4 or less chars.
85 if (strcmp(name, "__s1") == 0 || strcmp(name, "__s2") == 0)
86 return 1;
88 /* Ugh... People are saying that Smatch still barfs on glibc strcmp()
89 * functions.
91 if (array) {
92 char *macro;
94 /* why is this again??? */
95 if (array->type == EXPR_STRING &&
96 max.value == array->string->length)
97 return 1;
99 macro = get_macro_name(array->pos);
100 if (macro && max.uvalue < 4 &&
101 (strcmp(macro, "strcmp") == 0 ||
102 strcmp(macro, "strncmp") == 0 ||
103 strcmp(macro, "streq") == 0 ||
104 strcmp(macro, "strneq") == 0 ||
105 strcmp(macro, "strsep") == 0))
106 return 1;
110 * passing WORK_CPU_UNBOUND is idiomatic but Smatch doesn't understand
111 * how it's used so it causes a bunch of false positives.
113 if (option_project == PROJ_KERNEL &&
114 strcmp(name, "__per_cpu_offset") == 0)
115 return 1;
116 return 0;
119 static void array_check(struct expression *expr)
121 struct expression *array_expr;
122 const char *level = "error";
123 int array_size;
124 struct expression *offset;
125 sval_t max;
126 char *name;
128 expr = strip_expr(expr);
129 if (!is_array(expr))
130 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 sm_msg("%s: buffer overflow '%s' %d <= %s",
152 level, name, array_size, sval_to_str(max));
154 free_string(name);
157 void check_index_overflow(int id)
159 add_hook(&array_check, OP_HOOK);