validation/sm_casts.c: update to not use an unintialized variable
[smatch.git] / smatch_constraints.c
blobdca42432af558a467dfe6cea39fbbf72383c03da
1 /*
2 * Copyright (C) 2017 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 * Basically I see constraints as a way of saying "x <= some_limit". The
20 * problem is that smatch_capped is not granullar enough.
22 * This is mostly for finding out of bounds errors. So there are different
23 * types of constraints. Quite often we have "foo->xxx[i] = 42;" and we want
24 * to verify that "i" is less than foo->size.
26 * My idea was that we could automatically figure out these constraints. And we
27 * could load them in the DB so that they are the same every time. As in a
28 * constraint could be "< (struct whatever)->size" and give that in ID that
29 * would be constant until you completely wiped the DB. So when you do a normal
30 * DB rebuild then the first thing it will do is preserve all the constraints.
31 * I guess the reason to do it this way is to save space... I sometimes suspect
32 * that worrying about saving space is premature optimization.
34 * The other thing that I want to do a little bit different here is how I merge
35 * constraints. If a constraint is true on both sides, then that's normal. If
36 * we merge constraint 23 and 67 then we get constraint 23|67. If we merge 23
37 * with &undefined then we get &undefined. We can also have two constraints
38 * that are both true so we could have (45&23)|12 which means either both 45 and
39 * 23 are true or 12 is true.
44 #include "smatch.h"
45 #include "smatch_extra.h"
46 #include "smatch_slist.h"
48 static int my_id;
50 ALLOCATOR(constraint, "constraints");
52 static void add_constraint(struct constraint_list **list, int op, int constraint)
54 struct constraint *tmp, *new;
56 FOR_EACH_PTR(*list, tmp) {
57 if (tmp->id < constraint)
58 continue;
59 if (tmp->id == constraint) {
60 if (tmp->op == SPECIAL_LTE)
61 return;
62 if (op == '<')
63 return;
65 new = __alloc_constraint(0);
66 new->op = op;
67 new->id = constraint;
68 REPLACE_CURRENT_PTR(tmp, new);
69 return;
72 new = __alloc_constraint(0);
73 new->op = op;
74 new->id = constraint;
75 INSERT_CURRENT(new, tmp);
76 return;
77 } END_FOR_EACH_PTR(tmp);
79 new = __alloc_constraint(0);
80 new->op = op;
81 new->id = constraint;
82 add_ptr_list(list, new);
85 static struct constraint_list *merge_constraint_lists(struct constraint_list *one, struct constraint_list *two)
87 struct constraint_list *ret = NULL;
88 struct constraint *tmp;
90 // FIXME: not || but &&
91 FOR_EACH_PTR(one, tmp) {
92 add_constraint(&ret, tmp->op, tmp->id);
93 } END_FOR_EACH_PTR(tmp);
95 FOR_EACH_PTR(two, tmp) {
96 add_constraint(&ret, tmp->op, tmp->id);
97 } END_FOR_EACH_PTR(tmp);
99 return ret;
102 static struct constraint_list *clone_constraint_list(struct constraint_list *list)
104 struct constraint_list *ret = NULL;
105 struct constraint *tmp;
107 FOR_EACH_PTR(list, tmp) {
108 add_constraint(&ret, tmp->op, tmp->id);
109 } END_FOR_EACH_PTR(tmp);
111 return ret;
114 static struct smatch_state *alloc_constraint_state(struct constraint_list *list)
116 struct smatch_state *state;
117 struct constraint *con;
118 static char buf[256];
119 int cnt = 0;
121 FOR_EACH_PTR(list, con) {
122 if (cnt != 0)
123 cnt += snprintf(buf + cnt, sizeof(buf) - cnt, ", ");
124 cnt += snprintf(buf + cnt, sizeof(buf) - cnt, "%s%d",
125 show_special(con->op), con->id);
126 } END_FOR_EACH_PTR(con);
128 state = __alloc_smatch_state(0);
129 state->name = alloc_string(buf);
130 state->data = list;
131 return state;
134 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
136 struct constraint_list *list;
138 // FIXME: use the dead code below instead
139 if (strcmp(s1->name, s2->name) == 0)
140 return s1;
141 return &merged;
143 list = merge_constraint_lists(s1->data, s2->data);
144 return alloc_constraint_state(list);
147 static int negate_gt(int op)
149 switch (op) {
150 case '>':
151 case SPECIAL_UNSIGNED_GT:
152 case SPECIAL_GTE:
153 case SPECIAL_UNSIGNED_GTE:
154 return negate_comparison(op);
156 return op;
159 static char *get_toplevel_name(struct expression *expr)
161 struct symbol *sym;
162 char buf[256];
164 expr = strip_expr(expr);
165 if (expr->type != EXPR_SYMBOL || !expr->symbol || !expr->symbol->ident)
166 return NULL;
168 sym = expr->symbol;
169 if (!(sym->ctype.modifiers & MOD_TOPLEVEL))
170 return NULL;
172 if (sym->ctype.modifiers & MOD_STATIC)
173 snprintf(buf, sizeof(buf), "%s %s", get_base_file(), sym->ident->name);
174 else
175 snprintf(buf, sizeof(buf), "extern %s", sym->ident->name);
177 return alloc_string(buf);
180 char *get_constraint_str(struct expression *expr)
182 char *name;
184 name = get_toplevel_name(expr);
185 if (name)
186 return name;
187 return get_member_name(expr);
190 static int save_int_callback(void *_p, int argc, char **argv, char **azColName)
192 int *p = _p;
194 *p = atoi(argv[0]);
195 return 0;
198 static int constraint_str_to_id(const char *str)
200 int id = -1;
202 run_sql(save_int_callback, &id,
203 "select id from constraints where str = '%s'", str);
205 return id;
208 static int save_constraint_str(void *_str, int argc, char **argv, char **azColName)
210 char **str = _str;
212 *str = alloc_string(argv[0]);
213 return 0;
216 static char *constraint_id_to_str(int id)
218 char *str = NULL;
220 run_sql(save_constraint_str, &str,
221 "select str from constraints where id = '%d'", id);
223 return str;
226 static int save_op_callback(void *_p, int argc, char **argv, char **azColName)
228 int *p = _p;
230 if (argv[0][0] == '<' && argv[0][1] == '=')
231 *p = SPECIAL_LTE;
232 else
233 *p = '<';
234 return 0;
237 static int save_str_callback(void *_p, int argc, char **argv, char **azColName)
239 char **p = _p;
241 if (!*p) {
242 *p = alloc_string(argv[0]);
243 } else {
244 char buf[256];
246 snprintf(buf, sizeof(buf), "%s, %s", *p, argv[0]);
247 *p = alloc_string(buf);
249 return 0;
252 char *get_required_constraint(const char *data_str)
254 char *required = NULL;
256 run_sql(save_str_callback, &required,
257 "select bound from constraints_required where data = '%s'", data_str);
259 return required;
262 static int get_required_op(char *data_str, char *con_str)
264 int op = 0;
266 run_sql(save_op_callback, &op,
267 "select op from constraints_required where data = '%s' and bound = '%s'", data_str, con_str);
269 return op;
272 char *unmet_constraint(struct expression *data, struct expression *offset)
274 struct smatch_state *state;
275 struct constraint_list *list;
276 struct constraint *con;
277 char *data_str;
278 char *required;
279 int req_op;
280 int found = 0;
282 state = get_state_expr(my_id, offset);
283 if (!state)
284 return NULL;
285 list = state->data;
287 data_str = get_constraint_str(data);
288 if (!data_str)
289 return NULL;
291 required = get_required_constraint(data_str);
292 if (!required)
293 return NULL;
295 /* check the list of bounds on our index against the list that work */
296 FOR_EACH_PTR(list, con) {
297 char *con_str;
299 con_str = constraint_id_to_str(con->id);
300 if (!con_str) {
301 sm_msg("constraint %d not found", con->id);
302 continue;
305 req_op = get_required_op(data_str, con_str);
306 free_string(con_str);
307 if (!req_op)
308 continue;
309 if (con->op == '<' || con->op == req_op) {
310 found = 1;
311 goto free_data;
313 } END_FOR_EACH_PTR(con);
315 free_data:
316 if (found) {
317 free_string(required);
318 required = NULL;
320 free_string(data_str);
321 return required;
324 struct string_list *saved_constraints;
325 static void save_new_constraint(const char *con)
327 if (list_has_string(saved_constraints, con))
328 return;
329 insert_string(&saved_constraints, con);
330 sql_save_constraint(con);
333 static void match_condition(struct expression *expr)
335 struct expression *left, *right;
336 struct constraint_list *constraints;
337 struct smatch_state *state;
338 char *constraint;
339 int constraint_id;
340 int op;
342 if (expr->type != EXPR_COMPARE)
343 return;
345 if (expr->op == SPECIAL_EQUAL ||
346 expr->op == SPECIAL_NOTEQUAL)
347 return;
349 left = expr->left;
350 right = expr->right;
352 constraint = get_constraint_str(right);
353 if (!constraint)
354 return;
355 constraint_id = constraint_str_to_id(constraint);
356 if (constraint_id < 0)
357 save_new_constraint(constraint);
358 free_string(constraint);
359 if (constraint_id < 0)
360 return;
362 constraints = get_constraints(left);
363 constraints = clone_constraint_list(constraints);
364 op = negate_gt(expr->op);
365 add_constraint(&constraints, op, constraint_id);
366 state = alloc_constraint_state(constraints);
368 if (op == expr->op)
369 set_true_false_states_expr(my_id, left, state, NULL);
370 else
371 set_true_false_states_expr(my_id, left, NULL, state);
374 struct constraint_list *get_constraints(struct expression *expr)
376 struct smatch_state *state;
378 state = get_state_expr(my_id, expr);
379 if (!state)
380 return NULL;
381 return state->data;
384 static void match_caller_info(struct expression *expr)
386 struct expression *tmp;
387 struct smatch_state *state;
388 int i;
390 i = -1;
391 FOR_EACH_PTR(expr->args, tmp) {
392 i++;
393 state = get_state_expr(my_id, tmp);
394 if (!state || state == &merged || state == &undefined)
395 continue;
396 sql_insert_caller_info(expr, CONSTRAINT, i, "$", state->name);
397 } END_FOR_EACH_PTR(tmp);
400 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
402 if (sm->state == &merged || sm->state == &undefined)
403 return;
404 sql_insert_caller_info(call, CONSTRAINT, param, printed_name, sm->state->name);
407 static void print_return_implies_constrained(int return_id, char *return_ranges, struct expression *expr)
409 struct smatch_state *orig;
410 struct sm_state *sm;
411 const char *param_name;
412 int param;
414 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
415 if (sm->state == &merged || sm->state == &undefined)
416 continue;
418 param = get_param_num_from_sym(sm->sym);
419 if (param < 0)
420 continue;
422 orig = get_state_stree(get_start_states(), my_id, sm->name, sm->sym);
423 if (orig && strcmp(sm->state->name, orig->name) == 0)
424 continue;
426 param_name = get_param_name(sm);
427 if (!param_name)
428 continue;
430 sql_insert_return_states(return_id, return_ranges, CONSTRAINT,
431 param, param_name, sm->state->name);
432 } END_FOR_EACH_SM(sm);
435 static struct smatch_state *constraint_str_to_state(char *value)
437 struct constraint_list *list = NULL;
438 char *p = value;
439 int op;
440 long long id;
442 while (true) {
443 op = '<';
444 if (*p != '<')
445 return &undefined;
446 p++;
447 if (*p == '=') {
448 op = SPECIAL_LTE;
449 p++;
451 id = strtoll(p, &p, 10);
452 add_constraint(&list, op, id);
453 if (*p != ',')
454 break;
455 p++;
456 if (*p != ' ')
457 return &undefined;
460 return alloc_constraint_state(list);
463 static void db_returns_constrained(struct expression *expr, int param, char *key, char *value)
465 char *name;
466 struct symbol *sym;
468 name = return_state_to_var_sym(expr, param, key, &sym);
469 if (!name || !sym)
470 goto free;
472 set_state(my_id, name, sym, constraint_str_to_state(value));
473 free:
474 free_string(name);
477 void register_constraints(int id)
479 my_id = id;
481 add_merge_hook(my_id, &merge_func);
482 add_hook(&match_condition, CONDITION_HOOK);
484 add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
485 add_member_info_callback(my_id, struct_member_callback);
487 add_split_return_callback(print_return_implies_constrained);
488 select_return_states_hook(CONSTRAINT, &db_returns_constrained);