extra: create a callback for when smatch_extra changes a state
[smatch.git] / smatch_param_limit.c
blob2002bf5b7265d17ed18112194655351660556fd3
1 /*
2 * sparse/smatch_param_limit.c
4 * Copyright (C) 2012 Oracle.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include "scope.h"
11 #include "smatch.h"
12 #include "smatch_extra.h"
14 static int orig_id;
15 static int modify_id;
16 static int side_effects;
18 static struct smatch_state *orig_states[32];
20 STATE(modified);
22 static struct smatch_state *unmatched_state(struct sm_state *sm)
24 return extra_undefined(estate_type(sm->state));
27 int was_modified_sym(struct symbol *sym)
29 if (!side_effects)
30 return 0;
31 if (!sym || !sym->ident)
32 return 1; /* safer to say it was modified? */
33 if (get_state(modify_id, sym->ident->name, sym))
34 return 1;
35 return 0;
38 static int is_local(struct symbol *sym)
40 if (!sym->scope || !sym->scope->token)
41 return 0;
42 return 1;
45 static void check_expr(struct expression *expr)
47 struct smatch_state *state;
48 struct symbol *sym;
49 char *name;
51 name = get_variable_from_expr_complex(expr, &sym);
52 if (!sym) {
53 side_effects = 1;
54 goto free;
57 if (!is_local(sym))
58 side_effects = 1;
61 * Pointers are so tricky to handle just bail.
63 if (get_real_base_type(sym)->type == SYM_PTR)
64 side_effects = 1;
67 * TODO: it should only do this if we modify something that's not on the
68 * stack.
70 if (get_param_num_from_sym(sym) >= 0) {
71 side_effects = 1;
72 if (!get_state_expr(orig_id, expr)) {
73 state = get_implied_estate(expr);
74 set_state_expr(orig_id, expr, state);
78 set_state_expr(modify_id, expr, &modified);
79 free:
80 free_string(name);
83 static void match_assign(struct expression *expr)
85 check_expr(expr->left);
88 static void unop_expr(struct expression *expr)
90 if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
91 return;
93 expr = strip_expr(expr->unop);
94 check_expr(expr);
97 static int no_effect_func;
98 static int db_no_effect_func(void *unused, int argc, char **argv, char **azColName)
100 no_effect_func = 1;
101 return 0;
104 static int is_no_side_effect_func(struct expression *fn)
106 static char sql_filter[1024];
108 if (fn->type != EXPR_SYMBOL || !fn->symbol)
109 return 0;
111 if (fn->symbol->ctype.modifiers & MOD_STATIC) {
112 snprintf(sql_filter, 1024, "file = '%s' and function = '%s';",
113 get_filename(), fn->symbol->ident->name);
114 } else {
115 snprintf(sql_filter, 1024, "function = '%s' and static = 0;",
116 fn->symbol->ident->name);
119 no_effect_func = 0;
120 run_sql(db_no_effect_func, "select * from no_side_effects where %s", sql_filter);
122 return no_effect_func;
125 static void match_call(struct expression *expr)
127 struct expression *arg, *tmp;
129 if (!is_no_side_effect_func(expr->fn))
130 side_effects = 1;
132 FOR_EACH_PTR(expr->args, arg) {
133 tmp = strip_expr(arg);
134 if (tmp->type != EXPR_PREOP || tmp->op != '&')
135 continue;
136 tmp = strip_expr(tmp->unop);
137 check_expr(expr);
138 } END_FOR_EACH_PTR(arg);
141 static void asm_expr(struct statement *stmt)
144 struct expression *expr;
145 int state = 0;
147 FOR_EACH_PTR(stmt->asm_outputs, expr) {
148 switch (state) {
149 case 0: /* identifier */
150 case 1: /* constraint */
151 state++;
152 continue;
153 case 2: /* expression */
154 state = 0;
155 check_expr(expr);
156 continue;
158 } END_FOR_EACH_PTR(expr);
161 struct smatch_state *get_orig_estate(struct symbol *sym)
163 struct smatch_state *state;
165 if (!sym->ident || !sym->ident->name)
166 return NULL;
168 state = get_state(orig_id, sym->ident->name, sym);
169 if (state)
170 return state;
172 return get_state(SMATCH_EXTRA, sym->ident->name, sym);
175 static void match_after_def(struct symbol *sym)
177 struct smatch_state *state;
178 struct symbol *tmp;
179 int param;
181 param = -1;
182 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
183 param++;
184 if (param >= 32)
185 return;
187 orig_states[param] = NULL;
188 state = get_orig_estate(tmp);
189 if (!state)
190 continue;
191 orig_states[param] = state;
192 } END_FOR_EACH_PTR(tmp);
195 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr, struct state_list *slist)
197 struct smatch_state *state;
198 struct symbol *tmp;
199 int param;
201 param = -1;
202 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
203 param++;
204 state = get_orig_estate(tmp);
205 if (!state)
206 continue;
207 if (param < 32 &&
208 range_lists_equiv(estate_ranges(orig_states[param]), estate_ranges(state)))
209 continue;
210 sm_msg("info: return_limited_param %d %d '%s' '$$' '%s' %s",
211 return_id, param, return_ranges,
212 state->name, global_static());
213 } END_FOR_EACH_PTR(tmp);
216 static void match_end_func(struct symbol *sym)
218 if (option_info && !side_effects)
219 sm_msg("info: no_side_effects %s", global_static());
220 side_effects = 0;
223 void register_param_limit(int id)
225 modify_id = id;
227 add_hook(&match_after_def, AFTER_DEF_HOOK);
228 add_hook(&match_assign, ASSIGNMENT_HOOK);
229 add_hook(&unop_expr, OP_HOOK);
230 add_hook(&match_call, FUNCTION_CALL_HOOK);
231 add_hook(&asm_expr, ASM_HOOK);
232 add_hook(&match_end_func, END_FUNC_HOOK);
233 add_returned_state_callback(&print_return_value_param);
236 void register_param_limit2(int id)
238 orig_id = id;
239 add_merge_hook(orig_id, &merge_estates);
240 add_unmatched_state_hook(orig_id, &unmatched_state);