db/fixup_kernel.sh: fix clear_user() handling
[smatch.git] / smatch_param_cleared.c
blobdeb00f89ddfdc8a4c802530fff3b5b3acadf72d9
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);
37 STATE(add);
39 struct func_info {
40 const char *name;
41 int type;
42 int param;
43 const char *key;
44 const char *value;
45 const sval_t *implies_start, *implies_end;
46 param_key_hook *call_back;
49 static struct func_info func_table[] = {
50 { "memset", BUF_CLEARED, 0, "*$", "0"},
51 { "memzero", BUF_CLEARED, 0, "*$", "0" },
52 { "__memset", BUF_CLEARED, 0, "*$", "0"},
53 { "__memzero", BUF_CLEARED, 0, "*$", "0" },
54 { "__builtin_memset", BUF_CLEARED, 0, "*$", "0"},
56 { "memcpy", BUF_CLEARED, 0, "*$" },
57 { "memmove", BUF_CLEARED, 0, "*$" },
58 { "__memcpy", BUF_CLEARED, 0, "*$" },
59 { "__memmove", BUF_CLEARED, 0, "*$" },
60 { "__builtin_memmove", BUF_CLEARED, 0, "*$" },
61 { "__builtin_memcpy", BUF_CLEARED, 0, "*$" },
63 /* Should this be done some where else? */
64 { "strcpy", BUF_CLEARED, 0, "*$" },
65 { "strncpy", BUF_CLEARED, 0, "*$" },
66 { "sprintf", BUF_CLEARED, 0, "*$" },
67 { "snprintf", BUF_CLEARED, 0, "*$" },
69 /* Smoosh locking PARAM_ADD/SET internals into BUF_CLEARED */
70 {"spin_lock", BUF_CLEARED, 0, "*$"},
71 {"spin_unlock", BUF_CLEARED, 0, "*$"},
72 {"spin_lock_nested", BUF_CLEARED, 0, "*$"},
73 {"_spin_lock", BUF_CLEARED, 0, "*$"},
74 {"_spin_unlock", BUF_CLEARED, 0, "*$"},
75 {"_spin_lock_nested", BUF_CLEARED, 0, "*$"},
76 {"__spin_lock", BUF_CLEARED, 0, "*$"},
77 {"__spin_unlock", BUF_CLEARED, 0, "*$"},
78 {"__spin_lock_nested", BUF_CLEARED, 0, "*$"},
79 {"raw_spin_lock", BUF_CLEARED, 0, "*$"},
80 {"raw_spin_unlock", BUF_CLEARED, 0, "*$"},
81 {"_raw_spin_lock", BUF_CLEARED, 0, "*$"},
82 {"_raw_spin_lock_nested", BUF_CLEARED, 0, "*$"},
83 {"_raw_spin_unlock", BUF_CLEARED, 0, "*$"},
84 {"__raw_spin_lock", BUF_CLEARED, 0, "*$"},
85 {"__raw_spin_unlock", BUF_CLEARED, 0, "*$"},
87 {"spin_lock_irq", BUF_CLEARED, 0, "*$"},
88 {"spin_unlock_irq", BUF_CLEARED, 0, "*$"},
89 {"_spin_lock_irq", BUF_CLEARED, 0, "*$"},
90 {"_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
91 {"__spin_lock_irq", BUF_CLEARED, 0, "*$"},
92 {"__spin_unlock_irq", BUF_CLEARED, 0, "*$"},
93 {"_raw_spin_lock_irq", BUF_CLEARED, 0, "*$"},
94 {"_raw_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
95 {"__raw_spin_unlock_irq", BUF_CLEARED, 0, "*$"},
96 {"spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
97 {"spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
98 {"_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
99 {"_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
100 {"__spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
101 {"__spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
102 {"_raw_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
103 {"_raw_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
104 {"__raw_spin_lock_irqsave", BUF_CLEARED, 0, "*$"},
105 {"__raw_spin_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
106 {"spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
107 {"_spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
108 {"__spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
109 {"_raw_spin_lock_irqsave_nested", BUF_CLEARED, 0, "*$"},
110 {"spin_lock_bh", BUF_CLEARED, 0, "*$"},
111 {"spin_unlock_bh", BUF_CLEARED, 0, "*$"},
112 {"_spin_lock_bh", BUF_CLEARED, 0, "*$"},
113 {"_spin_unlock_bh", BUF_CLEARED, 0, "*$"},
114 {"__spin_lock_bh", BUF_CLEARED, 0, "*$"},
115 {"__spin_unlock_bh", BUF_CLEARED, 0, "*$"},
117 {"read_lock", BUF_CLEARED, 0, "*$"},
118 {"down_read", BUF_CLEARED, 0, "*$"},
119 {"down_read_nested", BUF_CLEARED, 0, "*$"},
120 {"up_read", BUF_CLEARED, 0, "*$"},
121 {"read_unlock", BUF_CLEARED, 0, "*$"},
122 {"_read_lock", BUF_CLEARED, 0, "*$"},
123 {"_read_unlock", BUF_CLEARED, 0, "*$"},
124 {"__read_lock", BUF_CLEARED, 0, "*$"},
125 {"__read_unlock", BUF_CLEARED, 0, "*$"},
126 {"_raw_read_lock", BUF_CLEARED, 0, "*$"},
127 {"_raw_read_unlock", BUF_CLEARED, 0, "*$"},
128 {"__raw_read_lock", BUF_CLEARED, 0, "*$"},
129 {"__raw_read_unlock", BUF_CLEARED, 0, "*$"},
130 {"read_lock_irq", BUF_CLEARED, 0, "*$"},
131 {"read_unlock_irq" , BUF_CLEARED, 0, "*$"},
132 {"_read_lock_irq", BUF_CLEARED, 0, "*$"},
133 {"_read_unlock_irq", BUF_CLEARED, 0, "*$"},
134 {"__read_lock_irq", BUF_CLEARED, 0, "*$"},
135 {"__read_unlock_irq", BUF_CLEARED, 0, "*$"},
136 {"_raw_read_unlock_irq", BUF_CLEARED, 0, "*$"},
137 {"_raw_read_lock_irq", BUF_CLEARED, 0, "*$"},
138 {"_raw_read_lock_bh", BUF_CLEARED, 0, "*$"},
139 {"_raw_read_unlock_bh", BUF_CLEARED, 0, "*$"},
140 {"read_lock_irqsave", BUF_CLEARED, 0, "*$"},
141 {"read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
142 {"_read_lock_irqsave", BUF_CLEARED, 0, "*$"},
143 {"_read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
144 {"__read_lock_irqsave", BUF_CLEARED, 0, "*$"},
145 {"__read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
146 {"read_lock_bh", BUF_CLEARED, 0, "*$"},
147 {"read_unlock_bh", BUF_CLEARED, 0, "*$"},
148 {"_read_lock_bh", BUF_CLEARED, 0, "*$"},
149 {"_read_unlock_bh", BUF_CLEARED, 0, "*$"},
150 {"__read_lock_bh", BUF_CLEARED, 0, "*$"},
151 {"__read_unlock_bh", BUF_CLEARED, 0, "*$"},
152 {"__raw_read_lock_bh", BUF_CLEARED, 0, "*$"},
153 {"__raw_read_unlock_bh", BUF_CLEARED, 0, "*$"},
155 {"_raw_read_lock_irqsave", BUF_CLEARED, 0, "*$"},
156 {"_raw_read_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
157 {"_raw_spin_lock_bh", BUF_CLEARED, 0, "*$"},
158 {"_raw_spin_lock_nest_lock", BUF_CLEARED, 0, "*$"},
159 {"_raw_spin_unlock_bh", BUF_CLEARED, 0, "*$"},
160 {"_raw_write_lock_irqsave", BUF_CLEARED, 0, "*$"},
161 {"_raw_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
162 {"__raw_write_unlock_irq", BUF_CLEARED, 0, "*$"},
163 {"__raw_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
165 {"write_lock", BUF_CLEARED, 0, "*$"},
166 {"down_write", BUF_CLEARED, 0, "*$"},
167 {"down_write_nested", BUF_CLEARED, 0, "*$"},
168 {"up_write", BUF_CLEARED, 0, "*$"},
169 {"write_unlock", BUF_CLEARED, 0, "*$"},
170 {"_write_lock", BUF_CLEARED, 0, "*$"},
171 {"_write_unlock", BUF_CLEARED, 0, "*$"},
172 {"__write_lock", BUF_CLEARED, 0, "*$"},
173 {"__write_unlock", BUF_CLEARED, 0, "*$"},
174 {"write_lock_irq", BUF_CLEARED, 0, "*$"},
175 {"write_unlock_irq", BUF_CLEARED, 0, "*$"},
176 {"_write_lock_irq", BUF_CLEARED, 0, "*$"},
177 {"_write_unlock_irq", BUF_CLEARED, 0, "*$"},
178 {"__write_lock_irq", BUF_CLEARED, 0, "*$"},
179 {"__write_unlock_irq", BUF_CLEARED, 0, "*$"},
180 {"_raw_write_unlock_irq", BUF_CLEARED, 0, "*$"},
181 {"write_lock_irqsave", BUF_CLEARED, 0, "*$"},
182 {"write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
183 {"_write_lock_irqsave", BUF_CLEARED, 0, "*$"},
184 {"_write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
185 {"__write_lock_irqsave", BUF_CLEARED, 0, "*$"},
186 {"__write_unlock_irqrestore", BUF_CLEARED, 0, "*$"},
187 {"write_lock_bh", BUF_CLEARED, 0, "*$"},
188 {"write_unlock_bh", BUF_CLEARED, 0, "*$"},
189 {"_write_lock_bh", BUF_CLEARED, 0, "*$"},
190 {"_write_unlock_bh", BUF_CLEARED, 0, "*$"},
191 {"__write_lock_bh", BUF_CLEARED, 0, "*$"},
192 {"__write_unlock_bh", BUF_CLEARED, 0, "*$"},
193 {"_raw_write_lock", BUF_CLEARED, 0, "*$"},
194 {"__raw_write_lock", BUF_CLEARED, 0, "*$"},
195 {"_raw_write_unlock", BUF_CLEARED, 0, "*$"},
196 {"__raw_write_unlock", BUF_CLEARED, 0, "*$"},
197 {"_raw_write_lock_bh", BUF_CLEARED, 0, "*$"},
198 {"_raw_write_unlock_bh", BUF_CLEARED, 0, "*$"},
199 {"_raw_write_lock_irq", BUF_CLEARED, 0, "*$"},
201 {"mutex_lock", BUF_CLEARED, 0, "*$"},
202 {"mutex_unlock", BUF_CLEARED, 0, "*$"},
203 {"mutex_destroy", BUF_CLEARED, 0, "*$"},
204 {"mutex_lock_nested", BUF_CLEARED, 0, "*$"},
205 {"mutex_lock_io", BUF_CLEARED, 0, "*$"},
206 {"mutex_lock_io_nested", BUF_CLEARED, 0, "*$"},
209 static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
211 if (s1 == &cleared && s2 == &zeroed)
212 return &cleared;
213 if (s1 == &zeroed && s2 == &cleared)
214 return &cleared;
215 return &add;
218 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
220 struct expression *arg;
221 char *name;
222 struct symbol *sym;
224 while (expr->type == EXPR_ASSIGNMENT)
225 expr = strip_expr(expr->right);
226 if (expr->type != EXPR_CALL)
227 return;
229 arg = get_argument_from_call_expr(expr->args, param);
230 arg = strip_expr(arg);
231 name = get_variable_from_key(arg, key, &sym);
232 if (!name || !sym)
233 goto free;
235 if (strcmp(value, "0") == 0)
236 set_state(my_id, name, sym, &zeroed);
237 else
238 set_state(my_id, name, sym, &cleared);
239 free:
240 free_string(name);
243 static void db_buf_add(struct expression *expr, const char *name, struct symbol *sym, void *data)
245 set_state(my_id, name, sym, &add);
248 static void match_memcpy(const char *fn, struct expression *expr, void *arg)
250 db_param_cleared(expr, PTR_INT(arg), (char *)"*$", (char *)"");
253 static void buf_cleared_db(struct expression *expr, const char *name, struct symbol *sym, const char *value)
255 if (strcmp(value, "0") == 0)
256 set_state(my_id, name, sym, &zeroed);
257 else
258 set_state(my_id, name, sym, &cleared);
261 static void buf_cleared(struct expression *expr, const char *name, struct symbol *sym, void *data)
263 struct func_info *info = data;
264 const char *value = "";
266 if (info && info->value)
267 value = info->value;
269 buf_cleared_db(expr, name, sym, value);
272 static bool parent_set(struct stree *set, struct sm_state *sm)
274 struct sm_state *tmp;
275 int len;
276 int ret;
278 FOR_EACH_SM(set, tmp) {
279 len = strlen(tmp->name);
280 ret = strncmp(tmp->name, sm->name, len);
281 if (ret < 0)
282 continue;
283 if (ret > 0)
284 return false;
285 if (sm->name[len] == '-' || sm->name[len] == '.') {
287 * Don't say things are zero when they're not.
288 * Not based on tested but it doesn't feel right.
290 if (sm->state == &cleared && tmp->state == &zeroed)
291 return false;
293 return true;
295 } END_FOR_EACH_SM(tmp);
297 return false;
300 static void return_info_callback(int return_id, char *return_ranges,
301 struct expression *returned_expr,
302 int param,
303 const char *printed_name,
304 struct sm_state *sm)
306 static struct stree *already_set;
307 static int prev_return_id;
309 if (return_id != prev_return_id)
310 free_stree(&already_set);
311 prev_return_id = return_id;
313 if (param < 0)
314 return;
316 if (sm->state != &add &&
317 sm->state != &cleared &&
318 sm->state != &zeroed)
319 return;
321 if (parent_set(already_set, sm))
322 return;
324 sql_insert_return_states(return_id, return_ranges,
325 (sm->state == &add) ? BUF_ADD : BUF_CLEARED,
326 param, printed_name,
327 (sm->state == &zeroed) ? "0" : "");
329 avl_insert(&already_set, sm);
332 static bool is_parent(struct sm_state *sm, const char *name, struct symbol *sym, int name_len)
334 const char *sm_name, *var_name;
335 int shared = 0;
336 int i;
338 if (sm->sym != sym)
339 return false;
341 /* I think sm->name always starts with a '*' now */
342 if (sm->name[0] != '*')
343 return false;
344 sm_name = &sm->name[1];
345 var_name = name;
346 if (var_name[0] == '*')
347 var_name++;
349 for (i = 0; i < name_len; i++) {
350 if (!sm_name[i])
351 break;
352 if (sm_name[i] == var_name[i])
353 shared++;
354 else
355 break;
358 if (sm_name[shared] != '\0')
359 return false;
361 if (var_name[shared] == '.' ||
362 var_name[shared] == '-' ||
363 var_name[shared] == '\0')
364 return true;
366 return false;
369 enum clear_zero {
370 CLEAR,
371 ZERO,
372 ADD,
373 ANY,
376 static bool parent_was_clear(const char *name, struct symbol *sym, enum clear_zero zero)
378 struct sm_state *sm;
379 char buf[250];
380 int len, i;
382 if (!name || !sym)
383 return false;
385 len = strlen(name);
386 if (len >= sizeof(buf)) {
388 * Haha. If your variable is over 250 chars I want nothing to
389 * to with it.
391 return true;
394 for (i = len - 1; i > 0; i--) {
395 if (name[i] == '.' || name[i] == '-')
396 break;
398 if (i == 0)
399 return false;
400 memcpy(buf, name, i);
401 buf[i] = '\0';
403 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
404 if (!is_parent(sm, name, sym, len))
405 continue;
406 if (zero == ZERO && sm->state == &zeroed)
407 return true;
408 if (zero == CLEAR && sm->state == &cleared)
409 return true;
410 if (zero == ADD && sm->state == &add)
411 return true;
412 if (zero == ANY)
413 return true;
414 return false;
415 } END_FOR_EACH_SM(sm);
417 return false;
420 bool parent_was_PARAM_CLEAR(const char *name, struct symbol *sym)
422 return parent_was_clear(name, sym, CLEAR);
425 bool parent_was_PARAM_CLEAR_ZERO(const char *name, struct symbol *sym)
427 return parent_was_clear(name, sym, ZERO);
430 static bool already_printed(struct symbol *arg)
432 if (!arg || !arg->ident)
433 return false;
435 return parent_was_clear(arg->ident->name, arg, ANY);
438 static bool sym_is_void_pointer(struct symbol *sym)
440 struct symbol *type;
442 type = get_real_base_type(sym);
443 if (!type || type->type != SYM_PTR)
444 return false;
445 type = get_real_base_type(type);
447 return type == &void_ctype;
450 static bool sym_is_union_pointer(struct symbol *sym)
452 struct symbol *type;
454 type = get_real_base_type(sym);
455 if (!type || type->type != SYM_PTR)
456 return false;
457 while (type && type->type == SYM_PTR)
458 type = get_real_base_type(type);
459 if (!type || type->type != SYM_UNION)
460 return false;
462 return true;
465 static bool ambiguous_members_set(struct symbol *arg)
467 struct sm_state *sm;
468 static int param_set_id;
469 int cnt = 0;
471 if (!param_set_id)
472 param_set_id = id_from_name("register_param_set");
474 if (!arg || !arg->ident)
475 return false;
477 if (!sym_is_void_pointer(arg) &&
478 !sym_is_union_pointer(arg))
479 return false;
481 FOR_EACH_MY_SM(param_set_id, __get_cur_stree(), sm) {
482 if (sm->sym == arg)
483 cnt++;
484 } END_FOR_EACH_SM(sm);
486 return cnt > 100;
489 static bool all_struct_members_set(struct symbol *arg)
491 struct symbol *type, *tmp;
492 char buf[80];
494 if (!arg || !arg->ident)
495 return false;
497 type = get_real_base_type(arg);
498 if (!type || type->type != SYM_PTR)
499 return false;
500 type = get_real_base_type(type);
501 if (!type || type->type != SYM_STRUCT)
502 return false;
504 FOR_EACH_PTR(type->symbol_list, tmp) {
505 if (!tmp->ident)
506 return false;
507 snprintf(buf, sizeof(buf), "%s->%s", arg->ident->name, tmp->ident->name);
508 if (!param_was_set_var_sym(buf, arg))
509 return false;
510 } END_FOR_EACH_PTR(tmp);
512 return true;
515 void __promote_sets_to_clears(int return_id, char *return_ranges, struct expression *expr)
517 struct symbol *arg;
518 char buf[256];
519 int i;
522 * This is called after BUF_CLEARED variables have been recorded but
523 * before PARAM_SET. If all the struct members have been set then
524 * promote it to BUF_CLEARED.
527 i = -1;
528 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
529 i++;
531 if (!arg->ident)
532 continue;
533 if (already_printed(arg))
534 continue;
535 if (ambiguous_members_set(arg) ||
536 all_struct_members_set(arg)) {
538 snprintf(buf, sizeof(buf), "*%s", arg->ident->name);
539 set_state(my_id, buf, arg, &cleared);
540 sql_insert_return_states(return_id, return_ranges,
541 BUF_CLEARED, i, "*$", "");
543 } END_FOR_EACH_PTR(arg);
546 static void register_clears_param(void)
548 struct token *token;
549 char name[256];
550 const char *function;
551 int param;
553 if (option_project == PROJ_NONE)
554 return;
556 snprintf(name, 256, "%s.clears_argument", option_project_str);
558 token = get_tokens_file(name);
559 if (!token)
560 return;
561 if (token_type(token) != TOKEN_STREAMBEGIN)
562 return;
563 token = token->next;
564 while (token_type(token) != TOKEN_STREAMEND) {
565 if (token_type(token) != TOKEN_IDENT)
566 return;
567 function = show_ident(token->ident);
568 token = token->next;
569 if (token_type(token) != TOKEN_NUMBER)
570 return;
571 param = atoi(token->number);
572 add_function_hook(function, &match_memcpy, INT_PTR(param));
573 token = token->next;
575 clear_token_alloc();
578 #define USB_DIR_IN 0x80
579 static void match_usb_control_msg(const char *fn, struct expression *expr, void *_size_arg)
581 struct expression *inout;
582 sval_t sval;
584 inout = get_argument_from_call_expr(expr->args, 3);
586 if (get_value(inout, &sval) && !(sval.uvalue & USB_DIR_IN))
587 return;
589 db_param_cleared(expr, 6, (char *)"*$", (char *)"");
592 static void match_assign(struct expression *expr)
594 struct symbol *type;
597 * If we have struct foo x, y; and we say that x = y; then it
598 * initializes the struct holes. So we record that here.
600 type = get_type(expr->left);
601 if (!type || type->type != SYM_STRUCT)
602 return;
604 set_state_expr(my_id, expr->left, &cleared);
607 static void match_array_assign(struct expression *expr)
609 struct expression *array_expr;
611 if (!is_array(expr->left))
612 return;
614 array_expr = get_array_base(expr->left);
615 set_state_expr(my_id, array_expr, &cleared);
618 static void load_func_table(struct func_info *table, int size)
620 struct func_info *info;
621 param_key_hook *cb;
622 int i;
624 for (i = 0; i < size; i++) {
625 info = &table[i];
627 if (info->call_back)
628 cb = info->call_back;
629 else
630 cb = buf_cleared;
632 if (info->implies_start) {
633 return_implies_param_key(info->name,
634 *info->implies_start, *info->implies_end,
635 cb, info->param, info->key, info);
636 } else {
637 add_function_param_key_hook(info->name, cb,
638 info->param, info->key, info);
643 int param_add_set_counter[12];
644 static void db_counter_reset(struct expression *expr)
646 memset(param_add_set_counter, 0, sizeof(param_add_set_counter));
649 static void db_counter_inc(struct expression *expr, int param, char *key, char *value)
651 if (param < 0 || param >= ARRAY_SIZE(param_add_set_counter))
652 return;
653 param_add_set_counter[param]++;
656 static void promote_void_param_sets(struct expression *expr)
658 struct expression *arg, *deref;
659 struct symbol *type;
660 int i;
662 if (expr->type != EXPR_CALL) {
663 sm_msg("unexpected!");
664 return;
667 i = -1;
668 FOR_EACH_PTR(expr->args, arg) {
669 i++;
671 if (param_add_set_counter[i] < 100)
672 continue;
674 type = get_arg_type(expr->fn, i);
675 if (!type || type->type != SYM_PTR)
676 continue;
677 type = get_real_base_type(type);
678 if (type != &void_ctype)
679 continue;
681 deref = deref_expression(arg);
682 set_state_expr(my_id, deref, &cleared);
683 } END_FOR_EACH_PTR(arg);
686 void register_param_cleared(int id)
688 my_id = id;
690 add_merge_hook(my_id, &merge_hook);
692 add_hook(&match_assign, ASSIGNMENT_HOOK);
693 add_hook(&match_array_assign, ASSIGNMENT_HOOK);
695 register_clears_param();
697 select_return_states_hook(BUF_CLEARED, &db_param_cleared);
698 select_return_param_key(BUF_ADD, &db_buf_add);
699 add_return_info_callback(my_id, return_info_callback);
701 if (option_project == PROJ_KERNEL)
702 add_function_hook("usb_control_msg", &match_usb_control_msg, NULL);
704 load_func_table(func_table, ARRAY_SIZE(func_table));
706 add_hook(&db_counter_reset, CALL_HOOK_AFTER_INLINE);
707 select_return_states_hook(PARAM_SET, &db_counter_inc);
708 select_return_states_hook(PARAM_ADD, &db_counter_inc);
709 add_hook(&promote_void_param_sets, FUNCTION_CALL_HOOK_AFTER_DB);