smatch_clear_buffer: memcpy() should set the destination to unknown
[smatch.git] / smatch_clear_buffer.c
blob3d9d140cb3649b35a8997e45437bfb49ff299213
1 /*
2 * smatch/smatch_clear_structs.c
4 * Copyright (C) 2013 Oracle.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include "smatch.h"
11 #include "smatch_slist.h"
12 #include "smatch_extra.h"
14 static int my_id;
16 STATE(uninitialized);
17 STATE(initialized);
19 static int type_sym_array(struct expression *expr)
21 struct symbol *type;
23 /* remove casting the array to a pointer */
24 expr = strip_expr(expr);
25 type = get_type(expr);
26 if (type && type->type == SYM_ARRAY)
27 return 1;
28 return 0;
31 static void set_members_uninitialized(struct expression *expr)
33 struct symbol *type, *sym, *tmp;
34 char *name;
35 char buf[256];
37 type = get_type(expr);
38 if (!type)
39 return;
41 if (type->type != SYM_PTR)
42 return;
43 type = get_real_base_type(type);
45 name = expr_to_var_sym(expr, &sym);
46 if (!name || !sym)
47 goto free;
49 FOR_EACH_PTR(type->symbol_list, tmp) {
50 if (!tmp->ident)
51 continue;
52 snprintf(buf, sizeof(buf), "%s->%s", name, tmp->ident->name);
53 set_state(my_id, buf, sym, &uninitialized);
54 } END_FOR_EACH_PTR(tmp);
56 free:
57 free_string(name);
60 static void initialize_struct_members(struct symbol *type, struct expression *expr, struct expression *to)
62 struct symbol *tmp;
63 struct expression *member;
64 struct expression *assign;
65 int op = '*';
67 if (expr->type == EXPR_PREOP && expr->op == '&') {
68 expr = strip_expr(expr->unop);
69 op = '.';
72 FOR_EACH_PTR(type->symbol_list, tmp) {
73 if (!tmp->ident)
74 continue;
75 member = member_expression(expr, op, tmp->ident);
76 if (type_sym_array(member))
77 continue;
80 * FIXME: the problem here is that sometimes we have:
81 * memset(&my_struct, 0xff, sizeof(my_struct));
82 * and my_struct->foo is a 1 bit bitfield. There is a check
83 * which complains that "255 doesn't fit in ->foo".
85 * For now I just have this ugly hack. But really it's not
86 * handling the memset(..., 0xff, ...) correctly at all so one
87 * more hack barely makes a difference.
90 if (to && type_positive_bits(get_type(member)) >= type_positive_bits(get_type(to)))
91 assign = assign_expression(member, to);
92 else
93 assign = assign_expression(member, unknown_value_expression(member));
95 __pass_to_client(assign, ASSIGNMENT_HOOK);
96 } END_FOR_EACH_PTR(tmp);
99 static void initialize_base_type(struct symbol *type, struct expression *expr,
100 struct expression *to)
102 struct expression *assign;
104 if (type == &void_ctype)
105 return;
106 if (expr->type == EXPR_PREOP && expr->op == '&')
107 expr = strip_expr(expr->unop);
108 else
109 expr = deref_expression(expr);
110 if (!to)
111 to = expr;
112 /* FIXME: see the FIXME in initialize_struct_members() */
113 if (type_positive_bits(get_type(expr)) < type_positive_bits(get_type(to)))
114 to = expr;
115 assign = assign_expression(expr, to);
116 __pass_to_client(assign, ASSIGNMENT_HOOK);
119 static void set_initialized(struct expression *expr, struct expression *to)
121 struct symbol *type;
123 expr = strip_expr(expr);
125 type = get_type(expr);
126 if (!type || type->type != SYM_PTR)
127 return;
128 type = get_real_base_type(type);
129 if (!type)
130 return;
131 if (type->type == SYM_BASETYPE)
132 initialize_base_type(type, expr, to);
133 if (type->type == SYM_STRUCT) {
134 if (expr->type != EXPR_PREOP || expr->op != '&')
135 expr = deref_expression(expr);
136 initialize_struct_members(type, expr, to);
140 int is_uninitialized(struct expression *expr)
142 struct sm_state *sm;
144 sm = get_sm_state_expr(my_id, expr);
145 if (!sm)
146 return 0;
147 return slist_has_state(sm->possible, &uninitialized);
150 int has_uninitialized_members(struct expression *expr)
152 struct symbol *sym;
153 struct symbol *tmp;
154 char *name;
155 char buf[256];
156 struct sm_state *sm;
158 sym = get_type(expr);
159 if (!sym)
160 return 0;
162 if (sym->type == SYM_PTR)
163 sym = get_real_base_type(sym);
165 name = expr_to_var(expr);
166 if (!name)
167 return 0;
169 FOR_EACH_PTR(sym->symbol_list, tmp) {
170 if (!tmp->ident)
171 continue;
172 snprintf(buf, sizeof(buf), "%s->%s", name, tmp->ident->name);
173 sm = get_sm_state(my_id, buf, sym);
174 if (!sm)
175 continue;
176 if (slist_has_state(sm->possible, &uninitialized))
177 return 1;
178 } END_FOR_EACH_PTR(tmp);
180 free_string(name);
181 return 0;
184 static void match_assign(struct expression *expr)
186 struct symbol *type;
188 type = get_type(expr->left);
189 if (!type || type->type != SYM_STRUCT)
190 return;
191 initialize_struct_members(type, expr->left, NULL);
194 static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
196 set_members_uninitialized(expr->left);
199 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
201 struct expression *arg;
203 while (expr->type == EXPR_ASSIGNMENT)
204 expr = strip_expr(expr->right);
205 if (expr->type != EXPR_CALL)
206 return;
208 arg = get_argument_from_call_expr(expr->args, param);
209 if (!arg)
210 return;
212 if (strcmp(value, "0") == 0)
213 set_initialized(arg, zero_expr());
214 else
215 set_initialized(arg, NULL);
218 static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
220 struct expression *buf;
221 struct expression *val;
223 buf = get_argument_from_call_expr(expr->args, 0);
224 val = get_argument_from_call_expr(expr->args, 1);
226 set_initialized(buf, val);
229 static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
231 struct expression *buf;
232 int arg = PTR_INT(_arg);
234 buf = get_argument_from_call_expr(expr->args, arg);
236 set_initialized(buf, NULL);
239 static void reset_initialized(struct sm_state *sm, struct expression *mod_expr)
241 set_state(my_id, sm->name, sm->sym, &initialized);
244 #define USB_DIR_IN 0x80
245 static void match_usb_control_msg(const char *fn, struct expression *expr, void *_size_arg)
247 struct expression *inout;
248 struct expression *buf;
249 sval_t sval;
251 inout = get_argument_from_call_expr(expr->args, 3);
252 buf = get_argument_from_call_expr(expr->args, 6);
254 if (get_value(inout, &sval) && !(sval.uvalue & USB_DIR_IN))
255 return;
257 set_initialized(buf, NULL);
260 static void register_clears_param(void)
262 struct token *token;
263 char name[256];
264 const char *function;
265 int param;
267 if (option_project == PROJ_NONE)
268 return;
270 snprintf(name, 256, "%s.clears_argument", option_project_str);
272 token = get_tokens_file(name);
273 if (!token)
274 return;
275 if (token_type(token) != TOKEN_STREAMBEGIN)
276 return;
277 token = token->next;
278 while (token_type(token) != TOKEN_STREAMEND) {
279 if (token_type(token) != TOKEN_IDENT)
280 return;
281 function = show_ident(token->ident);
282 token = token->next;
283 if (token_type(token) != TOKEN_NUMBER)
284 return;
285 param = atoi(token->number);
286 add_function_hook(function, &match_memcpy, INT_PTR(param));
287 token = token->next;
289 clear_token_alloc();
292 void register_clear_buffer(int id)
294 my_id = id;
295 if (option_project == PROJ_KERNEL) {
296 add_function_assign_hook("kmalloc", &match_alloc, NULL);
297 add_function_hook("usb_control_msg", &match_usb_control_msg, NULL);
299 add_modification_hook(my_id, &reset_initialized);
300 add_hook(&match_assign, ASSIGNMENT_HOOK);
301 add_function_hook("memset", &match_memset, NULL);
302 add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
303 add_function_hook("memmove", &match_memcpy, INT_PTR(0));
304 register_clears_param();
306 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);