extra: don't reset impossible states after a condition
[smatch.git] / smatch_fn_arg_link.c
blobdaff38c424c8c9a96c70f6e6ff2fab7c34c741d6
1 /*
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 * What we're trying to do here is record links between function pointers and
20 * function data. If you have foo->function(foo->data); that's very easy. But
21 * the problem is maybe when you pass the function and the data as parameters.
25 #include "smatch.h"
26 #include <ctype.h>
28 static int my_id;
30 static void save_in_fn_ptr_data_link_table(struct expression *fn, struct expression *arg)
32 struct symbol *fn_sym, *arg_sym;
33 struct symbol *type;
34 char *fn_name, *arg_name;
35 int sym_len;
36 char fn_buf[128];
37 char arg_buf[128];
39 fn_name = expr_to_var_sym(fn, &fn_sym);
40 arg_name = expr_to_var_sym(arg, &arg_sym);
41 if (!fn_sym || !fn_sym->ident || !arg_sym || !fn_name || !arg_name)
42 goto free;
43 if (fn_sym != arg_sym)
44 goto free;
46 sym_len = fn_sym->ident->len;
48 /* This is ignoring
49 * net/mac80211/driver-ops.h:482 drv_sta_remove() FN: local->ops->sta_remove ARG: &local->hw
50 * but ideally the restriction can be removed later.
52 if (strncmp(fn_name, arg_name, sym_len) != 0)
53 goto free;
55 type = get_real_base_type(fn_sym);
56 if (!type)
57 goto free;
58 if (type->type != SYM_PTR)
59 goto free;
60 type = get_real_base_type(type);
61 if (!type || type->type != SYM_STRUCT || !type->ident)
62 goto free;
64 snprintf(fn_buf, sizeof(fn_buf), "(struct %s)%s", type->ident->name,
65 fn_name + sym_len);
67 snprintf(arg_buf, sizeof(arg_buf), "(struct %s)%s", type->ident->name,
68 arg_name + sym_len);
70 sql_insert_fn_ptr_data_link(fn_buf, arg_buf);
71 free:
72 free_string(arg_name);
73 free_string(fn_name);
76 static int print_calls_parameter(struct expression *call)
78 struct expression *arg;
79 int fn_param, arg_param;
80 char buf[32];
82 fn_param = get_param_num(call->fn);
83 if (fn_param < 0)
84 return 0;
86 arg = get_argument_from_call_expr(call->args, 0);
87 if (!arg)
88 return 0;
90 arg_param = get_param_num(arg);
91 if (arg_param < 0)
92 return 0;
94 snprintf(buf, sizeof(buf), "%d", arg_param);
95 sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
96 return 0;
99 static int print_call_is_linked(struct expression *call)
101 struct expression *fn, *tmp;
102 struct expression *arg;
103 struct symbol *fn_sym;
104 struct symbol *arg_sym = NULL;
105 int i;
107 fn = strip_expr(call->fn);
108 tmp = get_assigned_expr(fn);
109 if (tmp)
110 fn = tmp;
111 if (fn->type != EXPR_DEREF || !fn->member)
112 return 0;
114 fn_sym = expr_to_sym(fn);
115 if (!fn_sym)
116 return 0;
118 i = -1;
119 FOR_EACH_PTR(call->args, arg) {
120 i++;
121 tmp = get_assigned_expr(arg);
122 if (tmp)
123 arg = tmp;
124 arg_sym = expr_to_sym(arg);
125 if (arg_sym == fn_sym) {
126 save_in_fn_ptr_data_link_table(fn, arg);
127 return 1;
129 } END_FOR_EACH_PTR(arg);
131 return 0;
134 static int is_recursive_call(struct expression *call)
136 if (call->fn->type != EXPR_SYMBOL)
137 return 0;
138 if (call->fn->symbol == cur_func_sym)
139 return 1;
140 return 0;
143 static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
145 struct expression *arg;
146 struct expression *tmp;
147 struct symbol *fn_sym, *arg_sym;
148 struct symbol *type;
149 int data_nr;
150 int fn_param, arg_param;
152 if (is_recursive_call(call))
153 return;
155 type = get_type(fn);
156 if (!type || type->type != SYM_PTR)
157 return;
158 type = get_real_base_type(type);
159 if (!type || type->type != SYM_FN)
160 return;
161 tmp = get_assigned_expr(fn);
162 if (tmp)
163 fn = tmp;
165 if (!isdigit(value[0]))
166 return;
167 data_nr = atoi(value);
168 arg = get_argument_from_call_expr(call->args, data_nr);
169 if (!arg)
170 return;
171 tmp = get_assigned_expr(arg);
172 if (tmp)
173 arg = tmp;
175 fn_param = get_param_num(fn);
176 arg_param = get_param_num(arg);
177 if (fn_param >= 0 && arg_param >= 0) {
178 char buf[32];
180 snprintf(buf, sizeof(buf), "%d", arg_param);
181 sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
182 return;
185 fn_sym = expr_to_sym(fn);
186 if (!fn_sym)
187 return;
188 arg_sym = expr_to_sym(arg);
189 if (arg_sym != fn_sym)
190 return;
191 save_in_fn_ptr_data_link_table(fn, tmp);
194 static void match_call_info(struct expression *call)
196 if (print_calls_parameter(call))
197 return;
198 if (print_call_is_linked(call))
199 return;
202 void register_fn_arg_link(int id)
204 my_id = id;
206 if (!option_info)
207 return;
209 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
210 select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);