param_key: fix container of when no struct member is referenced
[smatch.git] / smatch_param_cleared.c
blob80a05096488a08bfbb6c63740ec98016848aae38
1 /*
2 * Copyright (C) 2012 Oracle.
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
19 * This works together with smatch_clear_buffer.c. This one is only for
20 * tracking the information and smatch_clear_buffer.c changes SMATCH_EXTRA.
22 * This tracks functions like memset() which clear out a chunk of memory.
23 * It fills in a gap that smatch_param_set.c can't handle. It only handles
24 * void pointers because smatch_param_set.c should handle the rest. Oh. And
25 * also it handles arrays because Smatch sucks at handling arrays.
28 #include "scope.h"
29 #include "smatch.h"
30 #include "smatch_slist.h"
31 #include "smatch_extra.h"
33 static int my_id;
35 STATE(cleared);
36 STATE(zeroed);
38 struct func_info {
39 const char *name;
40 int type;
41 int param;
42 const char *key;
43 const char *value;
44 const sval_t *implies_start, *implies_end;
45 param_key_hook *call_back;
48 static struct func_info func_table[] = {
49 { "memset", BUF_CLEARED, 0, "*$", "0"},
50 { "memzero", BUF_CLEARED, 0, "*$", "0" },
51 { "__memset", BUF_CLEARED, 0, "*$", "0"},
52 { "__memzero", BUF_CLEARED, 0, "*$", "0" },
54 { "memcpy", BUF_CLEARED, 0, "*$" },
55 { "memmove", BUF_CLEARED, 0, "*$" },
56 { "__memcpy", BUF_CLEARED, 0, "*$" },
57 { "__memmove", BUF_CLEARED, 0, "*$" },
59 /* Should this be done some where else? */
60 { "strcpy", BUF_CLEARED, 0, "*$" },
61 { "strncpy", BUF_CLEARED, 0, "*$" },
62 { "sprintf", BUF_CLEARED, 0, "*$" },
63 { "snprintf", BUF_CLEARED, 0, "*$" },
65 /* Smoosh locking PARAM_ADD/SET internals into BUF_CLEARED */
66 {"spin_lock", BUF_CLEARED, 0, "*$"},
67 {"spin_unlock", BUF_CLEARED, 0, "*$"},
68 {"spin_lock_nested", BUF_CLEARED, 0, "*$"},
69 {"_spin_lock", BUF_CLEARED, 0, "*$"},
70 {"_spin_unlock", BUF_CLEARED, 0, "*$"},
71 {"_spin_lock_nested", BUF_CLEARED, 0, "*$"},
72 {"__spin_lock", BUF_CLEARED, 0, "*$"},
73 {"__spin_unlock", BUF_CLEARED, 0, "*$"},
74 {"__spin_lock_nested", BUF_CLEARED, 0, "*$"},
75 {"raw_spin_lock", BUF_CLEARED, 0, "*$"},
76 {"raw_spin_unlock", BUF_CLEARED, 0, "*$"},
77 {"_raw_spin_lock", BUF_CLEARED, 0, "*$"},
78 {"_raw_spin_lock_nested", BUF_CLEARED, 0, "*$"},
79 {"_raw_spin_unlock", BUF_CLEARED, 0, "*$"},
80 {"__raw_spin_lock", BUF_CLEARED, 0, "*$"},
81 {"__raw_spin_unlock", BUF_CLEARED, 0, "*$"},
83 {"spin_lock_irq", BUF_CLEARED, 0, "*$"},
84 {"spin_unlock_irq", BUF_CLEARED, 0, "*$"},
85 {"_spin_lock_irq", BUF_CLEARED, 0, "*$"},
86 {"_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
87 {"__spin_lock_irq", BUF_CLEARED, 0, "*$"},
88 {"__spin_unlock_irq", BUF_CLEARED, 0, "*$"},
89 {"_raw_spin_lock_irq", BUF_CLEARED, 0, "*$"},
90 {"_raw_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
91 {"__raw_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
92 {"spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
93 {"spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
94 {"_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
95 {"_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
96 {"__spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
97 {"__spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
98 {"_raw_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
99 {"_raw_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
100 {"__raw_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
101 {"__raw_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
102 {"spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
103 {"_spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
104 {"__spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
105 {"_raw_spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
106 {"spin_lock_bh", BUF_CLEARED, 0, "*$"},
107 {"spin_unlock_bh", BUF_CLEARED, 0, "*$"},
108 {"_spin_lock_bh", BUF_CLEARED, 0, "*$"},
109 {"_spin_unlock_bh", BUF_CLEARED, 0, "*$"},
110 {"__spin_lock_bh", BUF_CLEARED, 0, "*$"},
111 {"__spin_unlock_bh", BUF_CLEARED, 0, "*$"},
113 {"read_lock", BUF_CLEARED, 0, "*$"},
114 {"down_read", BUF_CLEARED, 0, "*$"},
115 {"down_read_nested", BUF_CLEARED, 0, "*$"},
116 {"up_read", BUF_CLEARED, 0, "*$"},
117 {"read_unlock", BUF_CLEARED, 0, "*$"},
118 {"_read_lock", BUF_CLEARED, 0, "*$"},
119 {"_read_unlock", BUF_CLEARED, 0, "*$"},
120 {"__read_lock", BUF_CLEARED, 0, "*$"},
121 {"__read_unlock", BUF_CLEARED, 0, "*$"},
122 {"_raw_read_lock", BUF_CLEARED, 0, "*$"},
123 {"_raw_read_unlock", BUF_CLEARED, 0, "*$"},
124 {"__raw_read_lock", BUF_CLEARED, 0, "*$"},
125 {"__raw_read_unlock", BUF_CLEARED, 0, "*$"},
126 {"read_lock_irq", BUF_CLEARED, 0, "*$"},
127 {"read_unlock_irq" , BUF_CLEARED, 0, "*$"},
128 {"_read_lock_irq", BUF_CLEARED, 0, "*$"},
129 {"_read_unlock_irq", BUF_CLEARED, 0, "*$"},
130 {"__read_lock_irq", BUF_CLEARED, 0, "*$"},
131 {"__read_unlock_irq", BUF_CLEARED, 0, "*$"},
132 {"_raw_read_unlock_irq", BUF_CLEARED, 0, "*$"},
133 {"_raw_read_lock_irq", BUF_CLEARED, 0, "*$"},
134 {"_raw_read_lock_bh", BUF_CLEARED, 0, "*$"},
135 {"_raw_read_unlock_bh", BUF_CLEARED, 0, "*$"},
136 {"read_lock_irqsave", BUF_CLEARED, 0, "*$"},
137 {"read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
138 {"_read_lock_irqsave", BUF_CLEARED, 0, "*$"},
139 {"_read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
140 {"__read_lock_irqsave", BUF_CLEARED, 0, "*$"},
141 {"__read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
142 {"read_lock_bh", BUF_CLEARED, 0, "*$"},
143 {"read_unlock_bh", BUF_CLEARED, 0, "*$"},
144 {"_read_lock_bh", BUF_CLEARED, 0, "*$"},
145 {"_read_unlock_bh", BUF_CLEARED, 0, "*$"},
146 {"__read_lock_bh", BUF_CLEARED, 0, "*$"},
147 {"__read_unlock_bh", BUF_CLEARED, 0, "*$"},
148 {"__raw_read_lock_bh", BUF_CLEARED, 0, "*$"},
149 {"__raw_read_unlock_bh", BUF_CLEARED, 0, "*$"},
151 {"_raw_read_lock_irqsave", BUF_CLEARED, 0, "*$"},
152 {"_raw_read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
153 {"_raw_spin_lock_bh", BUF_CLEARED, 0, "*$"},
154 {"_raw_spin_lock_nest_lock", BUF_CLEARED, 0, "*$"},
155 {"_raw_spin_unlock_bh", BUF_CLEARED, 0, "*$"},
156 {"_raw_write_lock_irqsave", BUF_CLEARED, 0, "*$"},
157 {"_raw_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
158 {"__raw_write_unlock_irq", BUF_CLEARED, 0, "*$"},
159 {"__raw_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
161 {"write_lock", BUF_CLEARED, 0, "*$"},
162 {"down_write", BUF_CLEARED, 0, "*$"},
163 {"down_write_nested", BUF_CLEARED, 0, "*$"},
164 {"up_write", BUF_CLEARED, 0, "*$"},
165 {"write_unlock", BUF_CLEARED, 0, "*$"},
166 {"_write_lock", BUF_CLEARED, 0, "*$"},
167 {"_write_unlock", BUF_CLEARED, 0, "*$"},
168 {"__write_lock", BUF_CLEARED, 0, "*$"},
169 {"__write_unlock", BUF_CLEARED, 0, "*$"},
170 {"write_lock_irq", BUF_CLEARED, 0, "*$"},
171 {"write_unlock_irq", BUF_CLEARED, 0, "*$"},
172 {"_write_lock_irq", BUF_CLEARED, 0, "*$"},
173 {"_write_unlock_irq", BUF_CLEARED, 0, "*$"},
174 {"__write_lock_irq", BUF_CLEARED, 0, "*$"},
175 {"__write_unlock_irq", BUF_CLEARED, 0, "*$"},
176 {"_raw_write_unlock_irq", BUF_CLEARED, 0, "*$"},
177 {"write_lock_irqsave", BUF_CLEARED, 0, "*$"},
178 {"write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
179 {"_write_lock_irqsave", BUF_CLEARED, 0, "*$"},
180 {"_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
181 {"__write_lock_irqsave", BUF_CLEARED, 0, "*$"},
182 {"__write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
183 {"write_lock_bh", BUF_CLEARED, 0, "*$"},
184 {"write_unlock_bh", BUF_CLEARED, 0, "*$"},
185 {"_write_lock_bh", BUF_CLEARED, 0, "*$"},
186 {"_write_unlock_bh", BUF_CLEARED, 0, "*$"},
187 {"__write_lock_bh", BUF_CLEARED, 0, "*$"},
188 {"__write_unlock_bh", BUF_CLEARED, 0, "*$"},
189 {"_raw_write_lock", BUF_CLEARED, 0, "*$"},
190 {"__raw_write_lock", BUF_CLEARED, 0, "*$"},
191 {"_raw_write_unlock", BUF_CLEARED, 0, "*$"},
192 {"__raw_write_unlock", BUF_CLEARED, 0, "*$"},
193 {"_raw_write_lock_bh", BUF_CLEARED, 0, "*$"},
194 {"_raw_write_unlock_bh", BUF_CLEARED, 0, "*$"},
195 {"_raw_write_lock_irq", BUF_CLEARED, 0, "*$"},
197 {"mutex_lock", BUF_CLEARED, 0, "*$"},
198 {"mutex_unlock", BUF_CLEARED, 0, "*$"},
199 {"mutex_destroy", BUF_CLEARED, 0, "*$"},
200 {"mutex_lock_nested", BUF_CLEARED, 0, "*$"},
201 {"mutex_lock_io", BUF_CLEARED, 0, "*$"},
202 {"mutex_lock_io_nested", BUF_CLEARED, 0, "*$"},
205 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
207 struct expression *arg;
208 char *name;
209 struct symbol *sym;
211 while (expr->type == EXPR_ASSIGNMENT)
212 expr = strip_expr(expr->right);
213 if (expr->type != EXPR_CALL)
214 return;
216 arg = get_argument_from_call_expr(expr->args, param);
217 arg = strip_expr(arg);
218 name = get_variable_from_key(arg, key, &sym);
219 if (!name || !sym)
220 goto free;
222 if (strcmp(value, "0") == 0)
223 set_state(my_id, name, sym, &zeroed);
224 else
225 set_state(my_id, name, sym, &cleared);
226 free:
227 free_string(name);
230 static void match_memcpy(const char *fn, struct expression *expr, void *arg)
232 db_param_cleared(expr, PTR_INT(arg), (char *)"*$", (char *)"");
235 static void buf_cleared_db(struct expression *expr, const char *name, struct symbol *sym, const char *value)
237 if (strcmp(value, "0") == 0)
238 set_state(my_id, name, sym, &zeroed);
239 else
240 set_state(my_id, name, sym, &cleared);
243 static void buf_cleared(struct expression *expr, const char *name, struct symbol *sym, void *data)
245 struct func_info *info = data;
246 const char *value = "";
248 if (info && info->value)
249 value = info->value;
251 buf_cleared_db(expr, name, sym, value);
254 static void return_info_callback(int return_id, char *return_ranges,
255 struct expression *returned_expr,
256 int param,
257 const char *printed_name,
258 struct sm_state *sm)
260 if (param < 0)
261 return;
263 if (sm->state != &zeroed &&
264 sm->state != &cleared)
265 return;
267 sql_insert_return_states(return_id, return_ranges, BUF_CLEARED, param,
268 printed_name, (sm->state == &zeroed) ? "0" : "");
271 static bool is_parent(struct sm_state *sm, const char *name, struct symbol *sym, int name_len)
273 const char *sm_name, *var_name;
274 int shared = 0;
275 int i;
277 if (sm->sym != sym)
278 return false;
280 /* I think sm->name always starts with a '*' now */
281 if (sm->name[0] != '*')
282 return false;
283 sm_name = &sm->name[1];
284 var_name = name;
285 if (var_name[0] == '*')
286 var_name++;
288 for (i = 0; i < name_len; i++) {
289 if (!sm_name[i])
290 break;
291 if (sm_name[i] == var_name[i])
292 shared++;
293 else
294 break;
297 if (sm_name[shared] != '\0')
298 return false;
300 if (var_name[shared] == '.' ||
301 var_name[shared] == '-' ||
302 var_name[shared] == '\0')
303 return true;
305 return false;
308 enum clear_zero {
309 CLEAR,
310 ZERO,
311 ANY,
314 static bool parent_was_clear(const char *name, struct symbol *sym, enum clear_zero zero)
316 struct sm_state *sm;
317 char buf[250];
318 int len, i;
320 if (!name || !sym)
321 return false;
323 len = strlen(name);
324 if (len >= sizeof(buf)) {
326 * Haha. If your variable is over 250 chars I want nothing to
327 * to with it.
329 return true;
332 for (i = len - 1; i > 0; i--) {
333 if (name[i] == '.' || name[i] == '-')
334 break;
336 if (i == 0)
337 return false;
338 memcpy(buf, name, i);
339 buf[i] = '\0';
341 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
342 if (!is_parent(sm, name, sym, len))
343 continue;
344 if (zero == ZERO && sm->state == &zeroed)
345 return true;
346 if (zero == CLEAR && sm->state == &cleared)
347 return true;
348 if (zero == ANY)
349 return true;
350 return false;
351 } END_FOR_EACH_SM(sm);
353 return false;
356 bool parent_was_PARAM_CLEAR(const char *name, struct symbol *sym)
358 return parent_was_clear(name, sym, CLEAR);
361 bool parent_was_PARAM_CLEAR_ZERO(const char *name, struct symbol *sym)
363 return parent_was_clear(name, sym, ZERO);
366 static bool already_printed(struct symbol *arg)
368 if (!arg || !arg->ident || !arg->ident->name)
369 return false;
371 return parent_was_clear(arg->ident->name, arg, ANY);
374 static bool sym_is_void_pointer(struct symbol *sym)
376 struct symbol *type;
378 type = get_real_base_type(sym);
379 if (!type || type->type != SYM_PTR)
380 return false;
381 type = get_real_base_type(type);
383 return type == &void_ctype;
386 static bool sym_is_union_pointer(struct symbol *sym)
388 struct symbol *type;
390 type = get_real_base_type(sym);
391 if (!type || type->type != SYM_PTR)
392 return false;
393 while (type && type->type == SYM_PTR)
394 type = get_real_base_type(type);
395 if (!type || type->type != SYM_UNION)
396 return false;
398 return true;
401 static bool ambiguous_members_set(struct symbol *arg)
403 struct sm_state *sm;
404 static int param_set_id;
405 int cnt = 0;
407 if (!param_set_id)
408 param_set_id = id_from_name("register_param_set");
410 if (!arg || !arg->ident || !arg->ident->name)
411 return false;
413 if (!sym_is_void_pointer(arg) &&
414 !sym_is_union_pointer(arg))
415 return false;
417 FOR_EACH_MY_SM(param_set_id, __get_cur_stree(), sm) {
418 if (sm->sym == arg)
419 cnt++;
420 } END_FOR_EACH_SM(sm);
422 return cnt > 100;
425 static bool all_struct_members_set(struct symbol *arg)
427 struct symbol *type, *tmp;
428 char buf[80];
430 if (!arg || !arg->ident || !arg->ident->name)
431 return false;
433 type = get_real_base_type(arg);
434 if (!type || type->type != SYM_PTR)
435 return false;
436 type = get_real_base_type(type);
437 if (!type || type->type != SYM_STRUCT)
438 return false;
440 FOR_EACH_PTR(type->symbol_list, tmp) {
441 if (!tmp->ident)
442 return false;
443 snprintf(buf, sizeof(buf), "%s->%s", arg->ident->name, tmp->ident->name);
444 if (!param_was_set_var_sym(buf, arg))
445 return false;
446 } END_FOR_EACH_PTR(tmp);
448 return true;
451 void __promote_sets_to_clears(int return_id, char *return_ranges, struct expression *expr)
453 struct symbol *arg;
454 char buf[256];
455 int i;
458 * This is called after BUF_CLEARED variables have been recorded but
459 * before PARAM_SET. If all the struct members have been set then
460 * promote it to BUF_CLEARED.
463 i = -1;
464 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
465 i++;
467 if (!arg->ident)
468 continue;
469 if (already_printed(arg))
470 continue;
471 if (ambiguous_members_set(arg) ||
472 all_struct_members_set(arg)) {
474 snprintf(buf, sizeof(buf), "*%s", arg->ident->name);
475 set_state(my_id, buf, arg, &cleared);
476 sql_insert_return_states(return_id, return_ranges,
477 BUF_CLEARED, i, "*$", "");
479 } END_FOR_EACH_PTR(arg);
482 static void register_clears_param(void)
484 struct token *token;
485 char name[256];
486 const char *function;
487 int param;
489 if (option_project == PROJ_NONE)
490 return;
492 snprintf(name, 256, "%s.clears_argument", option_project_str);
494 token = get_tokens_file(name);
495 if (!token)
496 return;
497 if (token_type(token) != TOKEN_STREAMBEGIN)
498 return;
499 token = token->next;
500 while (token_type(token) != TOKEN_STREAMEND) {
501 if (token_type(token) != TOKEN_IDENT)
502 return;
503 function = show_ident(token->ident);
504 token = token->next;
505 if (token_type(token) != TOKEN_NUMBER)
506 return;
507 param = atoi(token->number);
508 add_function_hook(function, &match_memcpy, INT_PTR(param));
509 token = token->next;
511 clear_token_alloc();
514 #define USB_DIR_IN 0x80
515 static void match_usb_control_msg(const char *fn, struct expression *expr, void *_size_arg)
517 struct expression *inout;
518 sval_t sval;
520 inout = get_argument_from_call_expr(expr->args, 3);
522 if (get_value(inout, &sval) && !(sval.uvalue & USB_DIR_IN))
523 return;
525 db_param_cleared(expr, 6, (char *)"*$", (char *)"");
528 static void match_assign(struct expression *expr)
530 struct symbol *type;
533 * If we have struct foo x, y; and we say that x = y; then it
534 * initializes the struct holes. So we record that here.
536 type = get_type(expr->left);
537 if (!type || type->type != SYM_STRUCT)
538 return;
540 set_state_expr(my_id, expr->left, &cleared);
543 static void match_array_assign(struct expression *expr)
545 struct expression *array_expr;
547 if (!is_array(expr->left))
548 return;
550 array_expr = get_array_base(expr->left);
551 set_state_expr(my_id, array_expr, &cleared);
554 static void load_func_table(struct func_info *table, int size)
556 struct func_info *info;
557 param_key_hook *cb;
558 int i;
560 for (i = 0; i < size; i++) {
561 info = &table[i];
563 if (info->call_back)
564 cb = info->call_back;
565 else
566 cb = buf_cleared;
568 if (info->implies_start) {
569 return_implies_param_key(info->name,
570 *info->implies_start, *info->implies_end,
571 cb, info->param, info->key, info);
572 } else {
573 add_function_param_key_hook(info->name, cb,
574 info->param, info->key, info);
579 int param_add_set_counter[12];
580 static void db_counter_reset(struct expression *expr)
582 memset(param_add_set_counter, 0, sizeof(param_add_set_counter));
585 static void db_counter_inc(struct expression *expr, int param, char *key, char *value)
587 if (param < 0 || param >= ARRAY_SIZE(param_add_set_counter))
588 return;
589 param_add_set_counter[param]++;
592 static void promote_void_param_sets(struct expression *expr)
594 struct expression *arg, *deref;
595 struct symbol *type;
596 int i;
598 if (expr->type != EXPR_CALL) {
599 sm_msg("unexpected!");
600 return;
603 i = -1;
604 FOR_EACH_PTR(expr->args, arg) {
605 i++;
607 if (param_add_set_counter[i] < 100)
608 continue;
610 type = get_arg_type(expr->fn, i);
611 if (!type || type->type != SYM_PTR)
612 continue;
613 type = get_real_base_type(type);
614 if (type != &void_ctype)
615 continue;
617 deref = deref_expression(arg);
618 set_state_expr(my_id, deref, &cleared);
619 } END_FOR_EACH_PTR(arg);
622 void register_param_cleared(int id)
624 my_id = id;
626 add_hook(&match_assign, ASSIGNMENT_HOOK);
627 add_hook(&match_array_assign, ASSIGNMENT_HOOK);
629 register_clears_param();
631 select_return_states_hook(BUF_CLEARED, &db_param_cleared);
632 add_return_info_callback(my_id, return_info_callback);
634 if (option_project == PROJ_KERNEL)
635 add_function_hook("usb_control_msg", &match_usb_control_msg, NULL);
637 load_func_table(func_table, ARRAY_SIZE(func_table));
639 add_hook(&db_counter_reset, CALL_HOOK_AFTER_INLINE);
640 select_return_states_hook(PARAM_SET, &db_counter_inc);
641 select_return_states_hook(PARAM_ADD, &db_counter_inc);
642 add_hook(&promote_void_param_sets, FUNCTION_CALL_HOOK_AFTER_DB);