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
;
45 state
= __alloc_smatch_state(0);
48 FOR_EACH_PTR(links
, tmp
) {
50 snprintf(buf
, sizeof(buf
), "%s", tmp
);
52 append(buf
, ", ", sizeof(buf
));
53 append(buf
, tmp
, sizeof(buf
));
55 } END_FOR_EACH_PTR(tmp
);
57 state
->name
= alloc_sname(buf
);
62 static struct smatch_state
*merge_links(struct smatch_state
*s1
, struct smatch_state
*s2
)
64 struct smatch_state
*ret
;
65 struct string_list
*links
;
67 links
= combine_string_lists(s1
->data
, s2
->data
);
68 ret
= alloc_link_state(links
);
72 static void save_link_var_sym(const char *var
, struct symbol
*sym
, const char *link
)
74 struct smatch_state
*old_state
, *new_state
;
75 struct string_list
*links
;
78 old_state
= get_state(link_id
, var
, sym
);
80 links
= clone_str_list(old_state
->data
);
84 new = alloc_sname(link
);
85 insert_string(&links
, new);
87 new_state
= alloc_link_state(links
);
88 set_state(link_id
, var
, sym
, new_state
);
91 static void add_comparison_var_sym(const char *left_name
,
92 struct var_sym_list
*left_vsl
,
94 const char *right_name
, struct var_sym_list
*right_vsl
)
96 struct smatch_state
*state
;
100 if (strcmp(left_name
, right_name
) > 0) {
101 const char *tmp_name
= left_name
;
102 struct var_sym_list
*tmp_vsl
= left_vsl
;
104 left_name
= right_name
;
105 left_vsl
= right_vsl
;
106 right_name
= tmp_name
;
108 comparison
= flip_comparison(comparison
);
110 snprintf(state_name
, sizeof(state_name
), "%s vs %s", left_name
, right_name
);
111 state
= alloc_compare_state(NULL
, left_name
, left_vsl
, comparison
, NULL
, right_name
, right_vsl
);
113 set_state(compare_id
, state_name
, NULL
, state
);
115 FOR_EACH_PTR(left_vsl
, vs
) {
116 save_link_var_sym(vs
->var
, vs
->sym
, state_name
);
117 } END_FOR_EACH_PTR(vs
);
118 FOR_EACH_PTR(right_vsl
, vs
) {
119 save_link_var_sym(vs
->var
, vs
->sym
, state_name
);
120 } END_FOR_EACH_PTR(vs
);
124 * This is quite a bit more limitted, less ambitious, simpler compared to
125 * smatch_camparison.c.
127 void __compare_param_limit_hook(struct expression
*left_expr
, struct expression
*right_expr
,
128 const char *state_name
,
129 struct smatch_state
*true_state
, struct smatch_state
*false_state
)
131 char *left_name
= NULL
;
132 char *right_name
= NULL
;
133 char *tmp_name
= NULL
;
134 struct symbol
*left_sym
, *right_sym
, *tmp_sym
;
136 left_name
= expr_to_var_sym(left_expr
, &left_sym
);
137 if (!left_name
|| !left_sym
)
139 right_name
= expr_to_var_sym(right_expr
, &right_sym
);
140 if (!right_name
|| !right_sym
)
143 if (get_param_num_from_sym(left_sym
) < 0 ||
144 get_param_num_from_sym(right_sym
) < 0)
147 tmp_name
= get_other_name_sym(left_name
, left_sym
, &tmp_sym
);
149 free_string(left_name
);
150 left_name
= tmp_name
;
154 tmp_name
= get_other_name_sym(right_name
, right_sym
, &tmp_sym
);
156 free_string(right_name
);
157 right_name
= tmp_name
;
161 if (param_was_set_var_sym(left_name
, left_sym
))
163 if (param_was_set_var_sym(right_name
, right_sym
))
166 set_true_false_states(compare_id
, state_name
, NULL
, true_state
, false_state
);
167 save_link_var_sym(left_name
, left_sym
, state_name
);
168 save_link_var_sym(right_name
, right_sym
, state_name
);
170 free_string(left_name
);
171 free_string(right_name
);
174 static void print_return_comparison(int return_id
, char *return_ranges
, struct expression
*expr
)
176 struct sm_state
*tmp
;
177 struct string_list
*links
;
180 struct compare_data
*data
;
181 struct var_sym
*left
, *right
;
182 int left_param
, right_param
;
183 static char left_buf
[256];
184 static char right_buf
[256];
185 static char info_buf
[256];
186 const char *tmp_name
;
188 FOR_EACH_MY_SM(link_id
, __get_cur_stree(), tmp
) {
189 links
= tmp
->state
->data
;
190 FOR_EACH_PTR(links
, link
) {
191 sm
= get_sm_state(compare_id
, link
, NULL
);
194 data
= sm
->state
->data
;
195 if (!data
|| !data
->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_msg("internal error 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
), 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 add_merge_hook(compare_id
, &merge_compare_states
);
361 add_split_return_callback(&print_return_comparison
);
363 select_return_states_hook(COMPARE_LIMIT
, &db_return_comparison
);
366 void register_param_compare_limit_links(int id
)
370 add_merge_hook(link_id
, &merge_links
);