2 * Copyright (C) 2013 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
22 #include "smatch_slist.h"
23 #include "smatch_extra.h"
25 #define UNKNOWN_SIZE (-1)
27 static int my_strlen_id
;
29 * The trick with the my_equiv_id is that if we have:
31 * We don't know at that point what the strlen() is but we know it's equivalent
32 * to "foo" so maybe we can find the value of "foo" later.
34 static int my_equiv_id
;
36 static struct smatch_state
*size_to_estate(int size
)
40 sval
.type
= &int_ctype
;
43 return alloc_estate_sval(sval
);
46 static struct smatch_state
*unmatched_strlen_state(struct sm_state
*sm
)
48 return size_to_estate(UNKNOWN_SIZE
);
51 static void set_strlen_undefined(struct sm_state
*sm
, struct expression
*mod_expr
)
53 set_state(sm
->owner
, sm
->name
, sm
->sym
, size_to_estate(UNKNOWN_SIZE
));
56 static void set_strlen_equiv_undefined(struct sm_state
*sm
, struct expression
*mod_expr
)
58 set_state(sm
->owner
, sm
->name
, sm
->sym
, &undefined
);
61 static void match_string_assignment(struct expression
*expr
)
63 struct range_list
*rl
;
67 if (!get_implied_strlen(expr
->right
, &rl
))
69 set_state_expr(my_strlen_id
, expr
->left
, alloc_estate_rl(clone_rl(rl
)));
72 static void match_strlen(const char *fn
, struct expression
*expr
, void *unused
)
74 struct expression
*right
;
75 struct expression
*str
;
76 struct expression
*len_expr
;
78 struct smatch_state
*state
;
80 right
= strip_expr(expr
->right
);
81 str
= get_argument_from_call_expr(right
->args
, 0);
82 len_expr
= strip_expr(expr
->left
);
84 len_name
= expr_to_var(len_expr
);
88 state
= __alloc_smatch_state(0);
89 state
->name
= len_name
;
90 state
->data
= len_expr
;
92 set_state_expr(my_equiv_id
, str
, state
);
95 static void match_strlen_condition(struct expression
*expr
)
97 struct expression
*left
;
98 struct expression
*right
;
99 struct expression
*str
= NULL
;
101 int strlen_right
= 0;
103 struct smatch_state
*true_state
= NULL
;
104 struct smatch_state
*false_state
= NULL
;
107 if (expr
->type
!= EXPR_COMPARE
)
110 left
= strip_expr(expr
->left
);
111 right
= strip_expr(expr
->right
);
113 if (left
->type
== EXPR_CALL
&& sym_name_is("strlen", left
->fn
)) {
114 str
= get_argument_from_call_expr(left
->args
, 0);
117 if (right
->type
== EXPR_CALL
&& sym_name_is("strlen", right
->fn
)) {
118 str
= get_argument_from_call_expr(right
->args
, 0);
122 if (!strlen_left
&& !strlen_right
)
124 if (strlen_left
&& strlen_right
)
129 if (!get_value(right
, &sval
))
132 op
= flip_comparison(op
);
133 if (!get_value(left
, &sval
))
139 case SPECIAL_UNSIGNED_LT
:
140 true_state
= size_to_estate(sval
.value
- 1);
143 case SPECIAL_UNSIGNED_LTE
:
144 true_state
= size_to_estate(sval
.value
);
147 true_state
= size_to_estate(sval
.value
);
149 case SPECIAL_NOTEQUAL
:
150 false_state
= size_to_estate(sval
.value
);
153 case SPECIAL_UNSIGNED_GTE
:
154 false_state
= size_to_estate(sval
.value
- 1);
157 case SPECIAL_UNSIGNED_GT
:
158 false_state
= size_to_estate(sval
.value
);
162 set_true_false_states_expr(my_strlen_id
, str
, true_state
, false_state
);
165 static int get_strlen_from_string(struct expression
*expr
, struct range_list
**rl
)
170 len
= expr
->string
->length
;
171 sval
= sval_type_val(&int_ctype
, len
- 1);
172 *rl
= alloc_rl(sval
, sval
);
177 static int get_strlen_from_state(struct expression
*expr
, struct range_list
**rl
)
179 struct smatch_state
*state
;
181 state
= get_state_expr(my_strlen_id
, expr
);
184 *rl
= estate_rl(state
);
188 static int get_strlen_from_equiv(struct expression
*expr
, struct range_list
**rl
)
190 struct smatch_state
*state
;
192 state
= get_state_expr(my_equiv_id
, expr
);
193 if (!state
|| !state
->data
)
195 if (!get_implied_rl((struct expression
*)state
->data
, rl
))
201 * This returns the strlen() without the NUL char.
203 int get_implied_strlen(struct expression
*expr
, struct range_list
**rl
)
208 expr
= strip_expr(expr
);
209 if (expr
->type
== EXPR_STRING
)
210 return get_strlen_from_string(expr
, rl
);
212 if (get_strlen_from_state(expr
, rl
))
214 if (get_strlen_from_equiv(expr
, rl
))
219 int get_size_from_strlen(struct expression
*expr
)
221 struct range_list
*rl
;
224 if (!get_implied_strlen(expr
, &rl
))
227 if (sval_is_negative(max
) || sval_is_max(max
))
230 return max
.value
+ 1; /* add one because strlen doesn't include the NULL */
233 void set_param_strlen(const char *name
, struct symbol
*sym
, char *key
, char *value
)
235 struct range_list
*rl
= NULL
;
236 struct smatch_state
*state
;
239 if (strncmp(key
, "$", 1) != 0)
242 snprintf(fullname
, 256, "%s%s", name
, key
+ 1);
244 str_to_rl(&int_ctype
, value
, &rl
);
245 if (!rl
|| is_whole_rl(rl
))
247 state
= alloc_estate_rl(rl
);
248 set_state(my_strlen_id
, fullname
, sym
, state
);
251 static void match_call(struct expression
*expr
)
253 struct expression
*arg
;
254 struct range_list
*rl
;
258 FOR_EACH_PTR(expr
->args
, arg
) {
259 if (!get_implied_strlen(arg
, &rl
))
261 if (!is_whole_rl(rl
))
262 sql_insert_caller_info(expr
, STR_LEN
, i
, "$", show_rl(rl
));
264 } END_FOR_EACH_PTR(arg
);
267 static void struct_member_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
269 if (sm
->state
== &merged
)
271 sql_insert_caller_info(call
, STR_LEN
, param
, printed_name
, sm
->state
->name
);
274 void register_strlen(int id
)
278 add_unmatched_state_hook(my_strlen_id
, &unmatched_strlen_state
);
280 select_caller_info_hook(set_param_strlen
, STR_LEN
);
281 add_hook(&match_string_assignment
, ASSIGNMENT_HOOK
);
283 add_modification_hook(my_strlen_id
, &set_strlen_undefined
);
284 add_merge_hook(my_strlen_id
, &merge_estates
);
285 add_hook(&match_call
, FUNCTION_CALL_HOOK
);
286 add_member_info_callback(my_strlen_id
, struct_member_callback
);
287 add_hook(&match_strlen_condition
, CONDITION_HOOK
);
290 void register_strlen_equiv(int id
)
293 add_function_assign_hook("strlen", &match_strlen
, NULL
);
294 add_modification_hook(my_equiv_id
, &set_strlen_equiv_undefined
);