Print this page
11506 smatch resync
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/smatch_strlen.c
+++ new/usr/src/tools/smatch/src/smatch_strlen.c
1 1 /*
2 2 * Copyright (C) 2013 Oracle.
3 3 *
4 4 * This program is free software; you can redistribute it and/or
5 5 * modify it under the terms of the GNU General Public License
6 6 * as published by the Free Software Foundation; either version 2
7 7 * of the License, or (at your option) any later version.
8 8 *
9 9 * This program is distributed in the hope that it will be useful,
10 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 * GNU General Public License for more details.
13 13 *
14 14 * You should have received a copy of the GNU General Public License
15 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16 16 */
17 17
18 18 #include <stdlib.h>
19 19 #include <errno.h>
20 20 #include "parse.h"
21 21 #include "smatch.h"
22 22 #include "smatch_slist.h"
23 23 #include "smatch_extra.h"
24 24
25 25 #define UNKNOWN_SIZE (-1)
26 26
27 27 static int my_strlen_id;
28 28 /*
29 29 * The trick with the my_equiv_id is that if we have:
30 30 * foo = strlen(bar);
31 31 * We don't know at that point what the strlen() is but we know it's equivalent
32 32 * to "foo" so maybe we can find the value of "foo" later.
33 33 */
34 34 static int my_equiv_id;
35 35
36 36 static struct smatch_state *size_to_estate(int size)
37 37 {
38 38 sval_t sval;
39 39
40 40 sval.type = &int_ctype;
41 41 sval.value = size;
42 42
43 43 return alloc_estate_sval(sval);
44 44 }
45 45
46 46 static struct smatch_state *unmatched_strlen_state(struct sm_state *sm)
47 47 {
48 48 return size_to_estate(UNKNOWN_SIZE);
49 49 }
50 50
51 51 static void set_strlen_undefined(struct sm_state *sm, struct expression *mod_expr)
52 52 {
53 53 set_state(sm->owner, sm->name, sm->sym, size_to_estate(UNKNOWN_SIZE));
54 54 }
55 55
56 56 static void set_strlen_equiv_undefined(struct sm_state *sm, struct expression *mod_expr)
57 57 {
58 58 set_state(sm->owner, sm->name, sm->sym, &undefined);
59 59 }
60 60
61 61 static void match_string_assignment(struct expression *expr)
62 62 {
63 63 struct range_list *rl;
64 64
65 65 if (expr->op != '=')
66 66 return;
67 67 if (!get_implied_strlen(expr->right, &rl))
68 68 return;
69 69 set_state_expr(my_strlen_id, expr->left, alloc_estate_rl(clone_rl(rl)));
70 70 }
71 71
72 72 static void match_strlen(const char *fn, struct expression *expr, void *unused)
73 73 {
74 74 struct expression *right;
75 75 struct expression *str;
76 76 struct expression *len_expr;
77 77 char *len_name;
78 78 struct smatch_state *state;
79 79
80 80 right = strip_expr(expr->right);
81 81 str = get_argument_from_call_expr(right->args, 0);
82 82 len_expr = strip_expr(expr->left);
83 83
84 84 len_name = expr_to_var(len_expr);
85 85 if (!len_name)
86 86 return;
87 87
88 88 state = __alloc_smatch_state(0);
89 89 state->name = len_name;
90 90 state->data = len_expr;
91 91
92 92 set_state_expr(my_equiv_id, str, state);
93 93 }
94 94
95 95 static void match_strlen_condition(struct expression *expr)
96 96 {
97 97 struct expression *left;
98 98 struct expression *right;
99 99 struct expression *str = NULL;
100 100 int strlen_left = 0;
101 101 int strlen_right = 0;
102 102 sval_t sval;
103 103 struct smatch_state *true_state = NULL;
104 104 struct smatch_state *false_state = NULL;
105 105 int op;
106 106
107 107 if (expr->type != EXPR_COMPARE)
108 108 return;
109 109
110 110 left = strip_expr(expr->left);
111 111 right = strip_expr(expr->right);
112 112
113 113 if (left->type == EXPR_CALL && sym_name_is("strlen", left->fn)) {
114 114 str = get_argument_from_call_expr(left->args, 0);
115 115 strlen_left = 1;
116 116 }
117 117 if (right->type == EXPR_CALL && sym_name_is("strlen", right->fn)) {
118 118 str = get_argument_from_call_expr(right->args, 0);
119 119 strlen_right = 1;
120 120 }
121 121
122 122 if (!strlen_left && !strlen_right)
123 123 return;
124 124 if (strlen_left && strlen_right)
125 125 return;
126 126
127 127 op = expr->op;
128 128 if (strlen_left) {
129 129 if (!get_value(right, &sval))
130 130 return;
131 131 } else {
132 132 op = flip_comparison(op);
133 133 if (!get_value(left, &sval))
134 134 return;
135 135 }
136 136
137 137 switch (op) {
138 138 case '<':
139 139 case SPECIAL_UNSIGNED_LT:
140 140 true_state = size_to_estate(sval.value - 1);
141 141 break;
142 142 case SPECIAL_LTE:
143 143 case SPECIAL_UNSIGNED_LTE:
144 144 true_state = size_to_estate(sval.value);
145 145 break;
146 146 case SPECIAL_EQUAL:
147 147 true_state = size_to_estate(sval.value);
148 148 break;
149 149 case SPECIAL_NOTEQUAL:
150 150 false_state = size_to_estate(sval.value);
151 151 break;
152 152 case SPECIAL_GTE:
153 153 case SPECIAL_UNSIGNED_GTE:
154 154 false_state = size_to_estate(sval.value - 1);
155 155 break;
156 156 case '>':
157 157 case SPECIAL_UNSIGNED_GT:
158 158 false_state = size_to_estate(sval.value);
159 159 break;
160 160 }
161 161
162 162 set_true_false_states_expr(my_strlen_id, str, true_state, false_state);
163 163 }
164 164
165 165 static void match_snprintf(const char *fn, struct expression *expr, void *unused)
166 166 {
167 167 struct expression *dest;
168 168 struct expression *dest_size_expr;
169 169 sval_t limit_size;
170 170
171 171 dest = get_argument_from_call_expr(expr->args, 0);
172 172 dest_size_expr = get_argument_from_call_expr(expr->args, 1);
173 173
174 174 if (!get_implied_value(dest_size_expr, &limit_size))
175 175 return;
176 176
177 177 if (limit_size.value <= 0)
178 178 return;
179 179
180 180 set_state_expr(my_strlen_id, dest, size_to_estate(limit_size.value - 1));
181 181 }
182 182
183 183 static void match_strlcpycat(const char *fn, struct expression *expr, void *unused)
184 184 {
185 185 struct expression *dest;
186 186 struct expression *src;
187 187 struct expression *limit_expr;
188 188 int src_len;
189 189 sval_t limit;
190 190
191 191 dest = get_argument_from_call_expr(expr->args, 0);
192 192 src = get_argument_from_call_expr(expr->args, 1);
193 193 limit_expr = get_argument_from_call_expr(expr->args, 2);
194 194
195 195 src_len = get_size_from_strlen(src);
196 196
197 197 if (!get_implied_max(limit_expr, &limit))
198 198 return;
199 199 if (limit.value < 0 || limit.value > INT_MAX)
200 200 return;
201 201 if (src_len != 0 && strcmp(fn, "strcpy") == 0 && src_len < limit.value)
202 202 limit.value = src_len;
203 203
204 204 set_state_expr(my_strlen_id, dest, size_to_estate(limit.value - 1));
205 205 }
206 206
207 207 static void match_strcpy(const char *fn, struct expression *expr, void *unused)
208 208 {
209 209 struct expression *dest;
210 210 struct expression *src;
211 211 int src_len;
212 212
213 213 dest = get_argument_from_call_expr(expr->args, 0);
214 214 src = get_argument_from_call_expr(expr->args, 1);
215 215
216 216 src_len = get_size_from_strlen(src);
217 217 if (src_len == 0)
218 218 return;
219 219
220 220 set_state_expr(my_strlen_id, dest, size_to_estate(src_len - 1));
221 221 }
222 222
223 223 static int get_strlen_from_string(struct expression *expr, struct range_list **rl)
224 224 {
225 225 sval_t sval;
226 226 int len;
227 227
228 228 len = expr->string->length;
229 229 sval = sval_type_val(&int_ctype, len - 1);
230 230 *rl = alloc_rl(sval, sval);
231 231 return 1;
232 232 }
233 233
234 234
235 235 static int get_strlen_from_state(struct expression *expr, struct range_list **rl)
236 236 {
237 237 struct smatch_state *state;
238 238
239 239 state = get_state_expr(my_strlen_id, expr);
240 240 if (!state)
241 241 return 0;
242 242 *rl = estate_rl(state);
243 243 return 1;
244 244 }
245 245
246 246 static int get_strlen_from_equiv(struct expression *expr, struct range_list **rl)
247 247 {
248 248 struct smatch_state *state;
249 249
250 250 state = get_state_expr(my_equiv_id, expr);
251 251 if (!state || !state->data)
252 252 return 0;
253 253 if (!get_implied_rl((struct expression *)state->data, rl))
254 254 return 0;
255 255 return 1;
256 256 }
257 257
258 258 /*
259 259 * This returns the strlen() without the NUL char.
260 260 */
261 261 int get_implied_strlen(struct expression *expr, struct range_list **rl)
262 262 {
263 263
264 264 *rl = NULL;
265 265
266 266 expr = strip_expr(expr);
267 267 if (expr->type == EXPR_STRING)
268 268 return get_strlen_from_string(expr, rl);
269 269
270 270 if (get_strlen_from_state(expr, rl))
271 271 return 1;
272 272 if (get_strlen_from_equiv(expr, rl))
273 273 return 1;
274 274 return 0;
275 275 }
276 276
277 277 int get_size_from_strlen(struct expression *expr)
278 278 {
279 279 struct range_list *rl;
280 280 sval_t max;
281 281
282 282 if (!get_implied_strlen(expr, &rl))
283 283 return 0;
284 284 max = rl_max(rl);
285 285 if (sval_is_negative(max) || sval_is_max(max))
286 286 return 0;
287 287
288 288 return max.value + 1; /* add one because strlen doesn't include the NULL */
289 289 }
290 290
291 291 void set_param_strlen(const char *name, struct symbol *sym, char *key, char *value)
292 292 {
293 293 struct range_list *rl = NULL;
294 294 struct smatch_state *state;
295 295 char fullname[256];
296 296
297 297 if (strncmp(key, "$", 1) != 0)
298 298 return;
299 299
300 300 snprintf(fullname, 256, "%s%s", name, key + 1);
301 301
302 302 str_to_rl(&int_ctype, value, &rl);
303 303 if (!rl || is_whole_rl(rl))
304 304 return;
305 305 state = alloc_estate_rl(rl);
306 306 set_state(my_strlen_id, fullname, sym, state);
307 307 }
308 308
309 309 static void match_call(struct expression *expr)
310 310 {
311 311 struct expression *arg;
312 312 struct range_list *rl;
313 313 int i;
314 314
315 315 i = 0;
316 316 FOR_EACH_PTR(expr->args, arg) {
317 317 if (!get_implied_strlen(arg, &rl))
318 318 continue;
319 319 if (!is_whole_rl(rl))
320 320 sql_insert_caller_info(expr, STR_LEN, i, "$", show_rl(rl));
321 321 i++;
322 322 } END_FOR_EACH_PTR(arg);
323 323 }
324 324
325 325 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
↓ open down ↓ |
325 lines elided |
↑ open up ↑ |
326 326 {
327 327 if (sm->state == &merged)
328 328 return;
329 329 sql_insert_caller_info(call, STR_LEN, param, printed_name, sm->state->name);
330 330 }
331 331
332 332 void register_strlen(int id)
333 333 {
334 334 my_strlen_id = id;
335 335
336 + set_dynamic_states(my_strlen_id);
337 +
336 338 add_unmatched_state_hook(my_strlen_id, &unmatched_strlen_state);
337 339
338 340 select_caller_info_hook(set_param_strlen, STR_LEN);
339 341 add_hook(&match_string_assignment, ASSIGNMENT_HOOK);
340 342
341 343 add_modification_hook(my_strlen_id, &set_strlen_undefined);
342 344 add_merge_hook(my_strlen_id, &merge_estates);
343 345 add_hook(&match_call, FUNCTION_CALL_HOOK);
344 346 add_member_info_callback(my_strlen_id, struct_member_callback);
345 347 add_hook(&match_strlen_condition, CONDITION_HOOK);
346 348
↓ open down ↓ |
1 lines elided |
↑ open up ↑ |
347 349 add_function_hook("snprintf", &match_snprintf, NULL);
348 350
349 351 add_function_hook("strlcpy", &match_strlcpycat, NULL);
350 352 add_function_hook("strlcat", &match_strlcpycat, NULL);
351 353 add_function_hook("strcpy", &match_strcpy, NULL);
352 354 }
353 355
354 356 void register_strlen_equiv(int id)
355 357 {
356 358 my_equiv_id = id;
359 + set_dynamic_states(my_equiv_id);
357 360 add_function_assign_hook("strlen", &match_strlen, NULL);
358 361 add_modification_hook(my_equiv_id, &set_strlen_equiv_undefined);
359 362 }
360 363
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX