buf_size: allow strncmp("foo", bar, 100) where 100 is larger than "foo"
[smatch.git] / smatch_clear_buffer.c
blob6439497f2a0d6ffe66970d968f5f0e86c5f8658f
1 /*
2 * Copyright (C) 2013 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
18 #include "smatch.h"
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
22 static int my_id;
24 STATE(uninitialized);
25 STATE(initialized);
27 static int type_sym_array(struct expression *expr)
29 struct symbol *type;
31 /* remove casting the array to a pointer */
32 expr = strip_expr(expr);
33 type = get_type(expr);
34 if (type && type->type == SYM_ARRAY)
35 return 1;
36 return 0;
39 static void set_members_uninitialized(struct expression *expr)
41 struct symbol *type, *sym, *tmp;
42 char *name;
43 char buf[256];
45 type = get_type(expr);
46 if (!type)
47 return;
49 if (type->type != SYM_PTR)
50 return;
51 type = get_real_base_type(type);
53 name = expr_to_var_sym(expr, &sym);
54 if (!name || !sym)
55 goto free;
57 FOR_EACH_PTR(type->symbol_list, tmp) {
58 if (!tmp->ident)
59 continue;
60 snprintf(buf, sizeof(buf), "%s->%s", name, tmp->ident->name);
61 set_state(my_id, buf, sym, &uninitialized);
62 } END_FOR_EACH_PTR(tmp);
64 free:
65 free_string(name);
68 static void initialize_struct_members(struct symbol *type, struct expression *expr, struct expression *to)
70 struct symbol *tmp;
71 struct expression *member;
72 struct expression *assign;
73 int op = '*';
75 if (expr->type == EXPR_PREOP && expr->op == '&') {
76 expr = strip_expr(expr->unop);
77 op = '.';
80 FOR_EACH_PTR(type->symbol_list, tmp) {
81 if (!tmp->ident)
82 continue;
83 member = member_expression(expr, op, tmp->ident);
84 if (type_sym_array(member))
85 continue;
88 * FIXME: the problem here is that sometimes we have:
89 * memset(&my_struct, 0xff, sizeof(my_struct));
90 * and my_struct->foo is a 1 bit bitfield. There is a check
91 * which complains that "255 doesn't fit in ->foo".
93 * For now I just have this ugly hack. But really it's not
94 * handling the memset(..., 0xff, ...) correctly at all so one
95 * more hack barely makes a difference.
98 if (to && type_positive_bits(get_type(member)) >= type_positive_bits(get_type(to)))
99 assign = assign_expression(member, to);
100 else
101 assign = assign_expression(member, member);
103 __pass_to_client(assign, ASSIGNMENT_HOOK);
104 } END_FOR_EACH_PTR(tmp);
107 static void initialize_base_type(struct symbol *type, struct expression *expr,
108 struct expression *to)
110 struct expression *assign;
112 if (type == &void_ctype)
113 return;
114 if (expr->type == EXPR_PREOP && expr->op == '&')
115 expr = strip_expr(expr->unop);
116 else
117 expr = deref_expression(expr);
118 if (!to)
119 to = expr;
120 /* FIXME: see the FIXME in initialize_struct_members() */
121 if (type_positive_bits(get_type(expr)) < type_positive_bits(get_type(to)))
122 to = expr;
123 assign = assign_expression(expr, to);
124 __pass_to_client(assign, ASSIGNMENT_HOOK);
127 static void set_initialized(struct expression *expr, struct expression *to)
129 struct symbol *type;
131 expr = strip_expr(expr);
133 type = get_type(expr);
134 if (!type || type->type != SYM_PTR)
135 return;
136 type = get_real_base_type(type);
137 if (!type)
138 return;
139 if (type->type == SYM_BASETYPE)
140 initialize_base_type(type, expr, to);
141 if (type->type == SYM_STRUCT) {
142 if (expr->type != EXPR_PREOP || expr->op != '&')
143 expr = deref_expression(expr);
144 initialize_struct_members(type, expr, to);
148 int is_uninitialized(struct expression *expr)
150 struct sm_state *sm;
152 sm = get_sm_state_expr(my_id, expr);
153 if (!sm)
154 return 0;
155 return slist_has_state(sm->possible, &uninitialized);
158 int has_uninitialized_members(struct expression *expr)
160 struct symbol *sym;
161 struct symbol *tmp;
162 char *name;
163 char buf[256];
164 struct sm_state *sm;
166 sym = get_type(expr);
167 if (!sym)
168 return 0;
170 if (sym->type == SYM_PTR)
171 sym = get_real_base_type(sym);
173 name = expr_to_var(expr);
174 if (!name)
175 return 0;
177 FOR_EACH_PTR(sym->symbol_list, tmp) {
178 if (!tmp->ident)
179 continue;
180 snprintf(buf, sizeof(buf), "%s->%s", name, tmp->ident->name);
181 sm = get_sm_state(my_id, buf, sym);
182 if (!sm)
183 continue;
184 if (slist_has_state(sm->possible, &uninitialized))
185 return 1;
186 } END_FOR_EACH_PTR(tmp);
188 free_string(name);
189 return 0;
192 static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
194 set_members_uninitialized(expr->left);
197 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
199 struct expression *arg;
201 while (expr->type == EXPR_ASSIGNMENT)
202 expr = strip_expr(expr->right);
203 if (expr->type != EXPR_CALL)
204 return;
206 arg = get_argument_from_call_expr(expr->args, param);
207 if (!arg)
208 return;
210 if (strcmp(value, "0") == 0)
211 set_initialized(arg, zero_expr());
212 else
213 set_initialized(arg, NULL);
216 static void reset_initialized(struct sm_state *sm, struct expression *mod_expr)
218 set_state(my_id, sm->name, sm->sym, &initialized);
221 #define USB_DIR_IN 0x80
222 static void match_usb_control_msg(const char *fn, struct expression *expr, void *_size_arg)
224 struct expression *inout;
225 struct expression *buf;
226 sval_t sval;
228 inout = get_argument_from_call_expr(expr->args, 3);
229 buf = get_argument_from_call_expr(expr->args, 6);
231 if (get_value(inout, &sval) && !(sval.uvalue & USB_DIR_IN))
232 return;
234 set_initialized(buf, NULL);
237 void register_clear_buffer(int id)
239 my_id = id;
240 if (option_project == PROJ_KERNEL) {
241 add_function_assign_hook("kmalloc", &match_alloc, NULL);
242 add_function_hook("usb_control_msg", &match_usb_control_msg, NULL);
244 add_modification_hook(my_id, &reset_initialized);
246 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);