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.
45 #include "smatch_extra.h"
46 #include "smatch_slist.h"
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
)
59 if (tmp
->id
== constraint
) {
60 if (tmp
->op
== SPECIAL_LTE
)
65 new = __alloc_constraint(0);
68 REPLACE_CURRENT_PTR(tmp
, new);
72 new = __alloc_constraint(0);
75 INSERT_CURRENT(new, tmp
);
77 } END_FOR_EACH_PTR(tmp
);
79 new = __alloc_constraint(0);
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
);
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
);
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];
121 FOR_EACH_PTR(list
, con
) {
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
);
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)
143 list
= merge_constraint_lists(s1
->data
, s2
->data
);
144 return alloc_constraint_state(list
);
147 static int negate_gt(int op
)
151 case SPECIAL_UNSIGNED_GT
:
153 case SPECIAL_UNSIGNED_GTE
:
154 return negate_comparison(op
);
159 static char *get_func_constraint(struct expression
*expr
)
164 if (is_fake_call(expr
))
166 name
= expr_to_str(expr
->fn
);
169 snprintf(buf
, sizeof(buf
), "%s()", name
);
171 return alloc_string(buf
);
174 static char *get_toplevel_name(struct expression
*expr
)
179 expr
= strip_expr(expr
);
180 if (expr
->type
!= EXPR_SYMBOL
|| !expr
->symbol
|| !expr
->symbol
->ident
)
184 if (!(sym
->ctype
.modifiers
& MOD_TOPLEVEL
))
187 if (sym
->ctype
.modifiers
& MOD_STATIC
)
188 snprintf(buf
, sizeof(buf
), "%s %s", get_base_file(), sym
->ident
->name
);
190 snprintf(buf
, sizeof(buf
), "extern %s", sym
->ident
->name
);
192 return alloc_string(buf
);
195 char *get_constraint_str(struct expression
*expr
)
201 if (expr
->type
== EXPR_CALL
)
202 return get_func_constraint(expr
);
203 name
= get_toplevel_name(expr
);
206 return get_member_name(expr
);
209 static int save_int_callback(void *_p
, int argc
, char **argv
, char **azColName
)
217 static int constraint_str_to_id(const char *str
)
221 run_sql(save_int_callback
, &id
,
222 "select id from constraints where str = '%s'", str
);
227 static int save_constraint_str(void *_str
, int argc
, char **argv
, char **azColName
)
231 *str
= alloc_string(argv
[0]);
235 static char *constraint_id_to_str(int id
)
239 run_sql(save_constraint_str
, &str
,
240 "select str from constraints where id = '%d'", id
);
245 static int save_op_callback(void *_p
, int argc
, char **argv
, char **azColName
)
249 if (argv
[0][0] == '<' && argv
[0][1] == '=')
256 static int save_str_callback(void *_p
, int argc
, char **argv
, char **azColName
)
261 *p
= alloc_string(argv
[0]);
265 snprintf(buf
, sizeof(buf
), "%s, %s", *p
, argv
[0]);
266 *p
= alloc_string(buf
);
271 char *get_required_constraint(const char *data_str
)
273 char *required
= NULL
;
275 run_sql(save_str_callback
, &required
,
276 "select bound from constraints_required where data = '%s'", data_str
);
281 static int get_required_op(char *data_str
, char *con_str
)
285 run_sql(save_op_callback
, &op
,
286 "select op from constraints_required where data = '%s' and bound = '%s'", data_str
, con_str
);
291 char *unmet_constraint(struct expression
*data
, struct expression
*offset
)
293 struct smatch_state
*state
;
294 struct constraint_list
*list
;
295 struct constraint
*con
;
301 state
= get_state_expr(my_id
, offset
);
306 data_str
= get_constraint_str(data
);
310 required
= get_required_constraint(data_str
);
314 /* check the list of bounds on our index against the list that work */
315 FOR_EACH_PTR(list
, con
) {
318 con_str
= constraint_id_to_str(con
->id
);
320 sm_msg("constraint %d not found", con
->id
);
324 req_op
= get_required_op(data_str
, con_str
);
325 free_string(con_str
);
328 if (con
->op
== '<' || con
->op
== req_op
) {
332 } END_FOR_EACH_PTR(con
);
336 free_string(required
);
339 free_string(data_str
);
343 struct string_list
*saved_constraints
;
344 static void save_new_constraint(const char *con
)
346 if (list_has_string(saved_constraints
, con
))
348 insert_string(&saved_constraints
, con
);
349 sql_save_constraint(con
);
352 static void match_condition(struct expression
*expr
)
354 struct expression
*left
, *right
;
355 struct constraint_list
*constraints
;
356 struct smatch_state
*state
;
361 if (expr
->type
!= EXPR_COMPARE
)
364 if (expr
->op
== SPECIAL_EQUAL
||
365 expr
->op
== SPECIAL_NOTEQUAL
)
371 constraint
= get_constraint_str(right
);
374 constraint_id
= constraint_str_to_id(constraint
);
375 if (constraint_id
< 0)
376 save_new_constraint(constraint
);
377 free_string(constraint
);
378 if (constraint_id
< 0)
381 constraints
= get_constraints(left
);
382 constraints
= clone_constraint_list(constraints
);
383 op
= negate_gt(expr
->op
);
384 add_constraint(&constraints
, remove_unsigned_from_comparison(op
), constraint_id
);
385 state
= alloc_constraint_state(constraints
);
388 set_true_false_states_expr(my_id
, left
, state
, NULL
);
390 set_true_false_states_expr(my_id
, left
, NULL
, state
);
393 struct constraint_list
*get_constraints(struct expression
*expr
)
395 struct smatch_state
*state
;
397 state
= get_state_expr(my_id
, expr
);
403 static void match_caller_info(struct expression
*expr
)
405 struct expression
*tmp
;
406 struct smatch_state
*state
;
410 FOR_EACH_PTR(expr
->args
, tmp
) {
412 state
= get_state_expr(my_id
, tmp
);
413 if (!state
|| state
== &merged
|| state
== &undefined
)
415 sql_insert_caller_info(expr
, CONSTRAINT
, i
, "$", state
->name
);
416 } END_FOR_EACH_PTR(tmp
);
419 static void struct_member_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
421 if (sm
->state
== &merged
|| sm
->state
== &undefined
)
423 sql_insert_caller_info(call
, CONSTRAINT
, param
, printed_name
, sm
->state
->name
);
426 static void print_return_implies_constrained(int return_id
, char *return_ranges
, struct expression
*expr
)
428 struct smatch_state
*orig
;
430 const char *param_name
;
433 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
434 if (sm
->state
== &merged
|| sm
->state
== &undefined
)
437 param
= get_param_num_from_sym(sm
->sym
);
441 orig
= get_state_stree(get_start_states(), my_id
, sm
->name
, sm
->sym
);
442 if (orig
&& strcmp(sm
->state
->name
, orig
->name
) == 0)
445 param_name
= get_param_name(sm
);
449 sql_insert_return_states(return_id
, return_ranges
, CONSTRAINT
,
450 param
, param_name
, sm
->state
->name
);
451 } END_FOR_EACH_SM(sm
);
454 static struct smatch_state
*constraint_str_to_state(char *value
)
456 struct constraint_list
*list
= NULL
;
470 id
= strtoll(p
, &p
, 10);
471 add_constraint(&list
, op
, id
);
479 return alloc_constraint_state(list
);
482 static void db_returns_constrained(struct expression
*expr
, int param
, char *key
, char *value
)
487 name
= return_state_to_var_sym(expr
, param
, key
, &sym
);
491 set_state(my_id
, name
, sym
, constraint_str_to_state(value
));
496 void register_constraints(int id
)
500 add_merge_hook(my_id
, &merge_func
);
501 add_hook(&match_condition
, CONDITION_HOOK
);
503 add_hook(&match_caller_info
, FUNCTION_CALL_HOOK
);
504 add_member_info_callback(my_id
, struct_member_callback
);
506 add_split_return_callback(print_return_implies_constrained
);
507 select_return_states_hook(CONSTRAINT
, &db_returns_constrained
);