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.
30 #include "smatch_slist.h"
31 #include "smatch_extra.h"
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
)
213 if (s1
== &zeroed
&& s2
== &cleared
)
218 static void db_param_cleared(struct expression
*expr
, int param
, char *key
, char *value
)
220 struct expression
*arg
;
224 while (expr
->type
== EXPR_ASSIGNMENT
)
225 expr
= strip_expr(expr
->right
);
226 if (expr
->type
!= EXPR_CALL
)
229 arg
= get_argument_from_call_expr(expr
->args
, param
);
230 arg
= strip_expr(arg
);
231 name
= get_variable_from_key(arg
, key
, &sym
);
235 if (strcmp(value
, "0") == 0)
236 set_state(my_id
, name
, sym
, &zeroed
);
238 set_state(my_id
, name
, sym
, &cleared
);
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
);
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
)
269 buf_cleared_db(expr
, name
, sym
, value
);
272 static void return_info_callback(int return_id
, char *return_ranges
,
273 struct expression
*returned_expr
,
275 const char *printed_name
,
281 if (sm
->state
!= &add
&&
282 sm
->state
!= &cleared
&&
283 sm
->state
!= &zeroed
)
286 sql_insert_return_states(return_id
, return_ranges
,
287 (sm
->state
== &add
) ? BUF_ADD
: BUF_CLEARED
,
289 (sm
->state
== &zeroed
) ? "0" : "");
292 static bool is_parent(struct sm_state
*sm
, const char *name
, struct symbol
*sym
, int name_len
)
294 const char *sm_name
, *var_name
;
301 /* I think sm->name always starts with a '*' now */
302 if (sm
->name
[0] != '*')
304 sm_name
= &sm
->name
[1];
306 if (var_name
[0] == '*')
309 for (i
= 0; i
< name_len
; i
++) {
312 if (sm_name
[i
] == var_name
[i
])
318 if (sm_name
[shared
] != '\0')
321 if (var_name
[shared
] == '.' ||
322 var_name
[shared
] == '-' ||
323 var_name
[shared
] == '\0')
336 static bool parent_was_clear(const char *name
, struct symbol
*sym
, enum clear_zero zero
)
346 if (len
>= sizeof(buf
)) {
348 * Haha. If your variable is over 250 chars I want nothing to
354 for (i
= len
- 1; i
> 0; i
--) {
355 if (name
[i
] == '.' || name
[i
] == '-')
360 memcpy(buf
, name
, i
);
363 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
364 if (!is_parent(sm
, name
, sym
, len
))
366 if (zero
== ZERO
&& sm
->state
== &zeroed
)
368 if (zero
== CLEAR
&& sm
->state
== &cleared
)
370 if (zero
== ADD
&& sm
->state
== &add
)
375 } END_FOR_EACH_SM(sm
);
380 bool parent_was_PARAM_CLEAR(const char *name
, struct symbol
*sym
)
382 return parent_was_clear(name
, sym
, CLEAR
);
385 bool parent_was_PARAM_CLEAR_ZERO(const char *name
, struct symbol
*sym
)
387 return parent_was_clear(name
, sym
, ZERO
);
390 static bool already_printed(struct symbol
*arg
)
392 if (!arg
|| !arg
->ident
)
395 return parent_was_clear(arg
->ident
->name
, arg
, ANY
);
398 static bool sym_is_void_pointer(struct symbol
*sym
)
402 type
= get_real_base_type(sym
);
403 if (!type
|| type
->type
!= SYM_PTR
)
405 type
= get_real_base_type(type
);
407 return type
== &void_ctype
;
410 static bool sym_is_union_pointer(struct symbol
*sym
)
414 type
= get_real_base_type(sym
);
415 if (!type
|| type
->type
!= SYM_PTR
)
417 while (type
&& type
->type
== SYM_PTR
)
418 type
= get_real_base_type(type
);
419 if (!type
|| type
->type
!= SYM_UNION
)
425 static bool ambiguous_members_set(struct symbol
*arg
)
428 static int param_set_id
;
432 param_set_id
= id_from_name("register_param_set");
434 if (!arg
|| !arg
->ident
)
437 if (!sym_is_void_pointer(arg
) &&
438 !sym_is_union_pointer(arg
))
441 FOR_EACH_MY_SM(param_set_id
, __get_cur_stree(), sm
) {
444 } END_FOR_EACH_SM(sm
);
449 static bool all_struct_members_set(struct symbol
*arg
)
451 struct symbol
*type
, *tmp
;
454 if (!arg
|| !arg
->ident
)
457 type
= get_real_base_type(arg
);
458 if (!type
|| type
->type
!= SYM_PTR
)
460 type
= get_real_base_type(type
);
461 if (!type
|| type
->type
!= SYM_STRUCT
)
464 FOR_EACH_PTR(type
->symbol_list
, tmp
) {
467 snprintf(buf
, sizeof(buf
), "%s->%s", arg
->ident
->name
, tmp
->ident
->name
);
468 if (!param_was_set_var_sym(buf
, arg
))
470 } END_FOR_EACH_PTR(tmp
);
475 void __promote_sets_to_clears(int return_id
, char *return_ranges
, struct expression
*expr
)
482 * This is called after BUF_CLEARED variables have been recorded but
483 * before PARAM_SET. If all the struct members have been set then
484 * promote it to BUF_CLEARED.
488 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, arg
) {
493 if (already_printed(arg
))
495 if (ambiguous_members_set(arg
) ||
496 all_struct_members_set(arg
)) {
498 snprintf(buf
, sizeof(buf
), "*%s", arg
->ident
->name
);
499 set_state(my_id
, buf
, arg
, &cleared
);
500 sql_insert_return_states(return_id
, return_ranges
,
501 BUF_CLEARED
, i
, "*$", "");
503 } END_FOR_EACH_PTR(arg
);
506 static void register_clears_param(void)
510 const char *function
;
513 if (option_project
== PROJ_NONE
)
516 snprintf(name
, 256, "%s.clears_argument", option_project_str
);
518 token
= get_tokens_file(name
);
521 if (token_type(token
) != TOKEN_STREAMBEGIN
)
524 while (token_type(token
) != TOKEN_STREAMEND
) {
525 if (token_type(token
) != TOKEN_IDENT
)
527 function
= show_ident(token
->ident
);
529 if (token_type(token
) != TOKEN_NUMBER
)
531 param
= atoi(token
->number
);
532 add_function_hook(function
, &match_memcpy
, INT_PTR(param
));
538 #define USB_DIR_IN 0x80
539 static void match_usb_control_msg(const char *fn
, struct expression
*expr
, void *_size_arg
)
541 struct expression
*inout
;
544 inout
= get_argument_from_call_expr(expr
->args
, 3);
546 if (get_value(inout
, &sval
) && !(sval
.uvalue
& USB_DIR_IN
))
549 db_param_cleared(expr
, 6, (char *)"*$", (char *)"");
552 static void match_assign(struct expression
*expr
)
557 * If we have struct foo x, y; and we say that x = y; then it
558 * initializes the struct holes. So we record that here.
560 type
= get_type(expr
->left
);
561 if (!type
|| type
->type
!= SYM_STRUCT
)
564 set_state_expr(my_id
, expr
->left
, &cleared
);
567 static void match_array_assign(struct expression
*expr
)
569 struct expression
*array_expr
;
571 if (!is_array(expr
->left
))
574 array_expr
= get_array_base(expr
->left
);
575 set_state_expr(my_id
, array_expr
, &cleared
);
578 static void load_func_table(struct func_info
*table
, int size
)
580 struct func_info
*info
;
584 for (i
= 0; i
< size
; i
++) {
588 cb
= info
->call_back
;
592 if (info
->implies_start
) {
593 return_implies_param_key(info
->name
,
594 *info
->implies_start
, *info
->implies_end
,
595 cb
, info
->param
, info
->key
, info
);
597 add_function_param_key_hook(info
->name
, cb
,
598 info
->param
, info
->key
, info
);
603 int param_add_set_counter
[12];
604 static void db_counter_reset(struct expression
*expr
)
606 memset(param_add_set_counter
, 0, sizeof(param_add_set_counter
));
609 static void db_counter_inc(struct expression
*expr
, int param
, char *key
, char *value
)
611 if (param
< 0 || param
>= ARRAY_SIZE(param_add_set_counter
))
613 param_add_set_counter
[param
]++;
616 static void promote_void_param_sets(struct expression
*expr
)
618 struct expression
*arg
, *deref
;
622 if (expr
->type
!= EXPR_CALL
) {
623 sm_msg("unexpected!");
628 FOR_EACH_PTR(expr
->args
, arg
) {
631 if (param_add_set_counter
[i
] < 100)
634 type
= get_arg_type(expr
->fn
, i
);
635 if (!type
|| type
->type
!= SYM_PTR
)
637 type
= get_real_base_type(type
);
638 if (type
!= &void_ctype
)
641 deref
= deref_expression(arg
);
642 set_state_expr(my_id
, deref
, &cleared
);
643 } END_FOR_EACH_PTR(arg
);
646 void register_param_cleared(int id
)
650 add_merge_hook(my_id
, &merge_hook
);
652 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
653 add_hook(&match_array_assign
, ASSIGNMENT_HOOK
);
655 register_clears_param();
657 select_return_states_hook(BUF_CLEARED
, &db_param_cleared
);
658 select_return_param_key(BUF_ADD
, &db_buf_add
);
659 add_return_info_callback(my_id
, return_info_callback
);
661 if (option_project
== PROJ_KERNEL
)
662 add_function_hook("usb_control_msg", &match_usb_control_msg
, NULL
);
664 load_func_table(func_table
, ARRAY_SIZE(func_table
));
666 add_hook(&db_counter_reset
, CALL_HOOK_AFTER_INLINE
);
667 select_return_states_hook(PARAM_SET
, &db_counter_inc
);
668 select_return_states_hook(PARAM_ADD
, &db_counter_inc
);
669 add_hook(&promote_void_param_sets
, FUNCTION_CALL_HOOK_AFTER_DB
);