2 * Copyright (C) 2016 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 * The point here is to store the relationships between two variables.
21 * To do that we create a state with the two variables in alphabetical order:
22 * ->name = "x vs y" and the state would be "<". On the false path the state
25 * Part of the trick of it is that if x or y is modified then we need to reset
26 * the state. We need to keep a list of all the states which depend on x and
27 * all the states which depend on y. The link_id code handles this.
32 #include "smatch_extra.h"
33 #include "smatch_slist.h"
35 static int compare_id
;
38 static struct smatch_state
*alloc_link_state(struct string_list
*links
)
40 struct smatch_state
*state
;
41 static char buf
[256] = "";
45 state
= __alloc_smatch_state(0);
47 FOR_EACH_PTR(links
, tmp
) {
48 cnt
+= snprintf(buf
+ cnt
, sizeof(buf
) - cnt
, "%s%s",
49 cnt
? ", " : "", tmp
);
50 if (cnt
>= sizeof(buf
))
52 } END_FOR_EACH_PTR(tmp
);
55 state
->name
= alloc_sname(buf
);
60 static struct smatch_state
*merge_links(struct smatch_state
*s1
, struct smatch_state
*s2
)
62 struct smatch_state
*ret
;
63 struct string_list
*links
;
65 links
= combine_string_lists(s1
->data
, s2
->data
);
66 ret
= alloc_link_state(links
);
70 static void save_link_var_sym(const char *var
, struct symbol
*sym
, const char *link
)
72 struct smatch_state
*old_state
, *new_state
;
73 struct string_list
*links
;
76 old_state
= get_state(link_id
, var
, sym
);
78 links
= clone_str_list(old_state
->data
);
82 new = alloc_sname(link
);
83 insert_string(&links
, new);
85 new_state
= alloc_link_state(links
);
86 set_state(link_id
, var
, sym
, new_state
);
89 static void add_comparison_var_sym(const char *left_name
,
90 struct var_sym_list
*left_vsl
,
92 const char *right_name
, struct var_sym_list
*right_vsl
)
94 struct smatch_state
*state
;
98 if (strcmp(left_name
, right_name
) > 0) {
99 const char *tmp_name
= left_name
;
100 struct var_sym_list
*tmp_vsl
= left_vsl
;
102 left_name
= right_name
;
103 left_vsl
= right_vsl
;
104 right_name
= tmp_name
;
106 comparison
= flip_comparison(comparison
);
108 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left_name
, right_name
);
109 state
= alloc_compare_state(NULL
, left_name
, left_vsl
, comparison
, NULL
, right_name
, right_vsl
);
111 set_state(compare_id
, state_name
, NULL
, state
);
113 FOR_EACH_PTR(left_vsl
, vs
) {
114 save_link_var_sym(vs
->var
, vs
->sym
, state_name
);
115 } END_FOR_EACH_PTR(vs
);
116 FOR_EACH_PTR(right_vsl
, vs
) {
117 save_link_var_sym(vs
->var
, vs
->sym
, state_name
);
118 } END_FOR_EACH_PTR(vs
);
122 * This is quite a bit more limitted, less ambitious, simpler compared to
123 * smatch_camparison.c.
125 void __compare_param_limit_hook(struct expression
*left_expr
, struct expression
*right_expr
,
126 const char *state_name
,
127 struct smatch_state
*true_state
, struct smatch_state
*false_state
)
129 char *left_name
= NULL
;
130 char *right_name
= NULL
;
131 char *tmp_name
= NULL
;
132 struct symbol
*left_sym
, *right_sym
, *tmp_sym
;
134 left_name
= expr_to_var_sym(left_expr
, &left_sym
);
135 if (!left_name
|| !left_sym
)
137 right_name
= expr_to_var_sym(right_expr
, &right_sym
);
138 if (!right_name
|| !right_sym
)
141 if (get_param_num_from_sym(left_sym
) < 0 ||
142 get_param_num_from_sym(right_sym
) < 0)
145 tmp_name
= get_other_name_sym(left_name
, left_sym
, &tmp_sym
);
147 free_string(left_name
);
148 left_name
= tmp_name
;
152 tmp_name
= get_other_name_sym(right_name
, right_sym
, &tmp_sym
);
154 free_string(right_name
);
155 right_name
= tmp_name
;
159 if (param_was_set_var_sym(left_name
, left_sym
))
161 if (param_was_set_var_sym(right_name
, right_sym
))
164 set_true_false_states(compare_id
, state_name
, NULL
, true_state
, false_state
);
165 save_link_var_sym(left_name
, left_sym
, state_name
);
166 save_link_var_sym(right_name
, right_sym
, state_name
);
168 free_string(left_name
);
169 free_string(right_name
);
172 static void print_return_comparison(int return_id
, char *return_ranges
, struct expression
*expr
)
174 struct sm_state
*tmp
;
175 struct string_list
*links
;
178 struct compare_data
*data
;
179 struct var_sym
*left
, *right
;
180 int left_param
, right_param
;
181 static char left_buf
[248];
182 static char right_buf
[248];
183 static char info_buf
[256];
184 const char *tmp_name
;
186 FOR_EACH_MY_SM(link_id
, __get_cur_stree(), tmp
) {
187 links
= tmp
->state
->data
;
188 FOR_EACH_PTR(links
, link
) {
189 sm
= get_sm_state(compare_id
, link
, NULL
);
192 data
= sm
->state
->data
;
194 data
->comparison
== UNKNOWN_COMPARISON
||
195 data
->comparison
== IMPOSSIBLE_COMPARISON
)
197 if (ptr_list_size((struct ptr_list
*)data
->left_vsl
) != 1 ||
198 ptr_list_size((struct ptr_list
*)data
->right_vsl
) != 1)
200 left
= first_ptr_list((struct ptr_list
*)data
->left_vsl
);
201 right
= first_ptr_list((struct ptr_list
*)data
->right_vsl
);
202 if (left
->sym
== right
->sym
&&
203 strcmp(left
->var
, right
->var
) == 0)
206 * Both parameters link to this comparison so only
207 * record the first one.
209 if (left
->sym
!= tmp
->sym
||
210 strcmp(left
->var
, tmp
->name
) != 0)
213 left_param
= get_param_num_from_sym(left
->sym
);
214 right_param
= get_param_num_from_sym(right
->sym
);
215 if (left_param
< 0 || right_param
< 0) /* can't happen hopefully */
218 tmp_name
= get_param_name_var_sym(left
->var
, left
->sym
);
221 snprintf(left_buf
, sizeof(left_buf
), "%s", tmp_name
);
223 tmp_name
= get_param_name_var_sym(right
->var
, right
->sym
);
224 if (!tmp_name
|| tmp_name
[0] != '$')
226 snprintf(right_buf
, sizeof(right_buf
), "$%d%s", right_param
, tmp_name
+ 1);
228 snprintf(info_buf
, sizeof(info_buf
), "%s %s", show_special(data
->comparison
), right_buf
);
229 sql_insert_return_states(return_id
, return_ranges
,
230 COMPARE_LIMIT
, left_param
, left_buf
, info_buf
);
231 } END_FOR_EACH_PTR(link
);
233 } END_FOR_EACH_SM(tmp
);
236 static int parse_comparison(char **value
, int *op
)
244 if (**value
== '=') {
257 *op
= SPECIAL_NOTEQUAL
;
261 if (**value
== '=') {
270 if (**value
!= ' ') {
271 sm_perror("parsing comparison. %s", *value
);
279 static int split_op_param_key(char *value
, int *op
, int *param
, char **key
)
281 static char buf
[256];
284 if (!parse_comparison(&value
, op
))
287 snprintf(buf
, sizeof(buf
), "%s", value
);
294 if (*param
< 0 || *param
> 99)
306 static void db_return_comparison(struct expression
*expr
, int left_param
, char *key
, char *value
)
308 struct expression
*left_arg
, *right_arg
;
309 char *left_name
= NULL
;
310 struct symbol
*left_sym
;
311 char *right_name
= NULL
;
312 struct symbol
*right_sym
;
316 struct var_sym_list
*left_vsl
= NULL
, *right_vsl
= NULL
;
318 while (expr
->type
== EXPR_ASSIGNMENT
)
319 expr
= strip_expr(expr
->right
);
320 if (expr
->type
!= EXPR_CALL
)
323 if (!split_op_param_key(value
, &op
, &right_param
, &right_key
))
326 left_arg
= get_argument_from_call_expr(expr
->args
, left_param
);
330 right_arg
= get_argument_from_call_expr(expr
->args
, right_param
);
334 left_name
= get_variable_from_key(left_arg
, key
, &left_sym
);
335 if (!left_name
|| !left_sym
)
337 if (get_param_num_from_sym(left_sym
) < 0)
340 right_name
= get_variable_from_key(right_arg
, right_key
, &right_sym
);
341 if (!right_name
|| !right_sym
)
343 if (get_param_num_from_sym(right_sym
) < 0)
346 add_var_sym(&left_vsl
, left_name
, left_sym
);
347 add_var_sym(&right_vsl
, right_name
, right_sym
);
349 add_comparison_var_sym(left_name
, left_vsl
, op
, right_name
, right_vsl
);
352 free_string(left_name
);
353 free_string(right_name
);
356 void register_param_compare_limit(int id
)
360 set_dynamic_states(compare_id
);
361 add_merge_hook(compare_id
, &merge_compare_states
);
362 add_split_return_callback(&print_return_comparison
);
364 select_return_states_hook(COMPARE_LIMIT
, &db_return_comparison
);
367 void register_param_compare_limit_links(int id
)
371 set_dynamic_states(link_id
);
372 add_merge_hook(link_id
, &merge_links
);