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
) {
62 if (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
)
199 expr
= strip_expr(expr
);
202 if (expr
->type
== EXPR_CALL
)
203 return get_func_constraint(expr
);
204 if (expr
->type
== EXPR_BINOP
)
205 return expr_to_str(expr
);
206 name
= get_toplevel_name(expr
);
209 return get_member_name(expr
);
212 static int save_int_callback(void *_p
, int argc
, char **argv
, char **azColName
)
220 static int constraint_str_to_id(const char *str
)
224 run_sql(save_int_callback
, &id
,
225 "select id from constraints where str = '%q'", str
);
230 static int save_constraint_str(void *_str
, int argc
, char **argv
, char **azColName
)
234 *str
= alloc_string(argv
[0]);
238 static char *constraint_id_to_str(int id
)
242 run_sql(save_constraint_str
, &str
,
243 "select str from constraints where id = '%d'", id
);
248 static int save_op_callback(void *_p
, int argc
, char **argv
, char **azColName
)
252 if (argv
[0][0] == '<' && argv
[0][1] == '=')
259 static int save_str_callback(void *_p
, int argc
, char **argv
, char **azColName
)
264 *p
= alloc_string(argv
[0]);
268 snprintf(buf
, sizeof(buf
), "%s, %s", *p
, argv
[0]);
269 *p
= alloc_string(buf
);
274 char *get_required_constraint(const char *data_str
)
276 char *required
= NULL
;
278 run_sql(save_str_callback
, &required
,
279 "select bound from constraints_required where data = '%q'", data_str
);
284 static int get_required_op(char *data_str
, char *con_str
)
288 run_sql(save_op_callback
, &op
,
289 "select op from constraints_required where data = '%q' and bound = '%q'", data_str
, con_str
);
294 char *unmet_constraint(struct expression
*data
, struct expression
*offset
)
296 struct smatch_state
*state
;
297 struct constraint_list
*list
;
298 struct constraint
*con
;
303 data_str
= get_constraint_str(data
);
307 required
= get_required_constraint(data_str
);
311 state
= get_state_expr(my_id
, offset
);
316 /* check the list of bounds on our index against the list that work */
317 FOR_EACH_PTR(list
, con
) {
320 con_str
= constraint_id_to_str(con
->id
);
322 sm_msg("constraint %d not found", con
->id
);
326 req_op
= get_required_op(data_str
, con_str
);
327 free_string(con_str
);
330 if (con
->op
== '<' || con
->op
== req_op
) {
331 free_string(required
);
335 } END_FOR_EACH_PTR(con
);
338 free_string(data_str
);
342 struct string_list
*saved_constraints
;
343 static void save_new_constraint(const char *con
)
345 if (!insert_string(&saved_constraints
, con
))
347 sql_save_constraint(con
);
350 static void handle_comparison(struct expression
*left
, int op
, struct expression
*right
)
352 struct constraint_list
*constraints
;
353 struct smatch_state
*state
;
359 /* known values are handled in smatch extra */
360 if (get_value(left
, &sval
) || get_value(right
, &sval
))
363 constraint
= get_constraint_str(right
);
366 constraint_id
= constraint_str_to_id(constraint
);
367 if (constraint_id
< 0)
368 save_new_constraint(constraint
);
369 free_string(constraint
);
370 if (constraint_id
< 0)
373 constraints
= get_constraints(left
);
374 constraints
= clone_constraint_list(constraints
);
375 op
= negate_gt(orig_op
);
376 add_constraint(&constraints
, remove_unsigned_from_comparison(op
), constraint_id
);
377 state
= alloc_constraint_state(constraints
);
380 set_true_false_states_expr(my_id
, left
, state
, NULL
);
382 set_true_false_states_expr(my_id
, left
, NULL
, state
);
385 static void match_condition(struct expression
*expr
)
387 if (expr
->type
!= EXPR_COMPARE
)
390 if (expr
->op
== SPECIAL_EQUAL
||
391 expr
->op
== SPECIAL_NOTEQUAL
)
394 handle_comparison(expr
->left
, expr
->op
, expr
->right
);
395 handle_comparison(expr
->right
, flip_comparison(expr
->op
), expr
->left
);
398 struct constraint_list
*get_constraints(struct expression
*expr
)
400 struct smatch_state
*state
;
402 state
= get_state_expr(my_id
, expr
);
408 static void match_caller_info(struct expression
*expr
)
410 struct expression
*tmp
;
411 struct smatch_state
*state
;
415 FOR_EACH_PTR(expr
->args
, tmp
) {
417 state
= get_state_expr(my_id
, tmp
);
418 if (!state
|| state
== &merged
|| state
== &undefined
)
420 sql_insert_caller_info(expr
, CONSTRAINT
, i
, "$", state
->name
);
421 } END_FOR_EACH_PTR(tmp
);
424 static void struct_member_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
426 if (sm
->state
== &merged
|| sm
->state
== &undefined
)
428 sql_insert_caller_info(call
, CONSTRAINT
, param
, printed_name
, sm
->state
->name
);
431 static struct smatch_state
*constraint_str_to_state(char *value
)
433 struct constraint_list
*list
= NULL
;
447 id
= strtoll(p
, &p
, 10);
448 add_constraint(&list
, op
, id
);
456 return alloc_constraint_state(list
);
459 static void set_param_constrained(const char *name
, struct symbol
*sym
, char *key
, char *value
)
463 if (strcmp(key
, "*$") == 0)
464 snprintf(fullname
, sizeof(fullname
), "*%s", name
);
465 else if (strncmp(key
, "$", 1) == 0)
466 snprintf(fullname
, 256, "%s%s", name
, key
+ 1);
470 set_state(my_id
, name
, sym
, constraint_str_to_state(value
));
473 static void print_return_implies_constrained(int return_id
, char *return_ranges
, struct expression
*expr
)
475 struct smatch_state
*orig
;
477 const char *param_name
;
480 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
481 if (sm
->state
== &merged
|| sm
->state
== &undefined
)
484 param
= get_param_num_from_sym(sm
->sym
);
488 orig
= get_state_stree(get_start_states(), my_id
, sm
->name
, sm
->sym
);
489 if (orig
&& strcmp(sm
->state
->name
, orig
->name
) == 0)
492 param_name
= get_param_name(sm
);
496 sql_insert_return_states(return_id
, return_ranges
, CONSTRAINT
,
497 param
, param_name
, sm
->state
->name
);
498 } END_FOR_EACH_SM(sm
);
501 static void db_returns_constrained(struct expression
*expr
, int param
, char *key
, char *value
)
506 name
= return_state_to_var_sym(expr
, param
, key
, &sym
);
510 set_state(my_id
, name
, sym
, constraint_str_to_state(value
));
515 void register_constraints(int id
)
519 set_dynamic_states(my_id
);
520 add_merge_hook(my_id
, &merge_func
);
521 add_hook(&match_condition
, CONDITION_HOOK
);
523 add_hook(&match_caller_info
, FUNCTION_CALL_HOOK
);
524 add_member_info_callback(my_id
, struct_member_callback
);
525 select_caller_info_hook(&set_param_constrained
, CONSTRAINT
);
527 add_split_return_callback(print_return_implies_constrained
);
528 select_return_states_hook(CONSTRAINT
, &db_returns_constrained
);