1 /*
2  * Copyright (C) 2017 Oracle.
3  *
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.
8  *
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.
13  *
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
16  */
17 
18 /*
19  * Take a look at request_threaded_irq().  It takes thread_fn and dev_id.  Then
20  * it does:
21  *
22  *	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
23  *	action->thread_fn = thread_fn;
24  *	action->dev_id = dev_id;
25  *
26  * It doesn't ever pass action back to the higher levels, but instead registers
27  * it with the lower levels.
28  *
29  * The kzalloc() allocation creates a new mtag.  We don't know at this point
30  * what "thread_fn" and "dev_id" are because they come from many different
31  * sources.
32  *
33  * So what we do is we pass the information back to the callers that thread_fn
34  * and dev_id are stored as a specific mtag data.  Then when the callers *do*
35  * know what values are passed they create an mtag_alias.  An mtag_alias is a
36  * many to one relationship.  Then they store that in mtag_data using the
37  * mtag_alias.
38  *
39  */
40 
41 #include "smatch.h"
42 #include "smatch_extra.h"
43 #include "smatch_slist.h"
44 
45 static int my_id;
46 
47 struct tag_assign_info {
48 	mtag_t tag;
49 	int offset;
50 };
51 ALLOCATOR(tag_assign_info, "tag name offset");
52 
alloc_tag_data_state(mtag_t tag,char * name,int offset)53 static struct smatch_state *alloc_tag_data_state(mtag_t tag, char *name, int offset)
54 {
55 	struct smatch_state *state;
56 	struct tag_assign_info *data;
57 
58 	data = __alloc_tag_assign_info(0);
59 	data->tag = tag;
60 	data->offset = offset;
61 
62 	state = __alloc_smatch_state(0);
63 	state->name = alloc_sname(name);
64 	state->data = data;
65 	return state;
66 }
67 
merge_tag_info(struct smatch_state * s1,struct smatch_state * s2)68 struct smatch_state *merge_tag_info(struct smatch_state *s1, struct smatch_state *s2)
69 {
70 	/* Basically ignore undefined states */
71 	if (s1 == &undefined)
72 		return s2;
73 	if (s2 == &undefined)
74 		return s1;
75 
76 	return &merged;
77 }
78 
match_assign(struct expression * expr)79 static void match_assign(struct expression *expr)
80 {
81 	struct expression *left;
82 	struct symbol *right_sym;
83 	char *name;
84 	mtag_t tag;
85 	int offset;
86 	int param;
87 
88 	if (expr->op != '=')
89 		return;
90 	left = strip_expr(expr->left);
91 	if (is_local_variable(left))
92 		return;
93 	right_sym = expr_to_sym(expr->right);
94 	if (!right_sym)
95 		return;
96 
97 	param = get_param_num_from_sym(right_sym);
98 	if (param < 0)
99 		return;
100 	// FIXME:  modify param_has_filter_data() to take a name/sym
101 	if (!expr_to_mtag_offset(left, &tag, &offset))
102 		return;
103 	name = expr_to_str(left);
104 	if (!name)
105 		return;
106 	set_state_expr(my_id, expr->right, alloc_tag_data_state(tag, name, offset));
107 	free_string(name);
108 }
109 
propogate_assignment(struct expression * expr,mtag_t tag,int offset,int param,char * key)110 static void propogate_assignment(struct expression *expr, mtag_t tag, int offset, int param, char *key)
111 {
112 	struct expression *arg;
113 	int orig_param;
114 	char buf[32];
115 	char *name;
116 	struct symbol *sym;
117 
118 	arg = get_argument_from_call_expr(expr->args, param);
119 	if (!arg)
120 		return;
121 	name = get_variable_from_key(arg, key, &sym);
122 	if (!name || !sym)
123 		goto free;
124 
125 	orig_param = get_param_num_from_sym(sym);
126 	if (orig_param < 0)
127 		goto free;
128 
129 	snprintf(buf, sizeof(buf), "$->[%d]", offset);
130 	set_state(my_id, name, sym, alloc_tag_data_state(tag, buf, offset));
131 free:
132 	free_string(name);
133 }
134 
assign_to_alias(struct expression * expr,int param,mtag_t tag,int offset,char * key)135 static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int offset, char *key)
136 {
137 	struct expression *arg, *gen_expr;
138 	struct range_list *rl;
139 	mtag_t arg_tag;
140 	mtag_t alias;
141 	int arg_offset;
142 
143 	arg = get_argument_from_call_expr(expr->args, param);
144 	if (!arg)
145 		return;
146 
147 	gen_expr = gen_expression_from_key(arg, key);
148 	if (!gen_expr)
149 		return;
150 
151 	get_absolute_rl(gen_expr, &rl);
152 
153 	if (!create_mtag_alias(tag, expr, &alias))
154 		return;
155 
156 //	insert_mtag_data(alias, offset, rl);
157 
158 	// FIXME:  is arg_offset handled correctly?
159 	if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) &&
160 	    arg_offset < MTAG_OFFSET_MASK)
161 		sql_insert_mtag_map(alias, offset, arg_tag, arg_offset);
162 }
163 
call_does_mtag_assign(struct expression * expr,int param,char * key,char * value)164 static void call_does_mtag_assign(struct expression *expr, int param, char *key, char *value)
165 {
166 	char *p;
167 	mtag_t tag;
168 	int offset;
169 
170 	while (expr->type == EXPR_ASSIGNMENT)
171 		expr = strip_expr(expr->right);
172 	if (expr->type != EXPR_CALL)
173 		return;
174 
175 	tag = strtoul(value, NULL, 10);
176 	p = strchr(value, '+');
177 	if (!p)
178 		return;
179 	offset = atoi(p + 1);
180 
181 //	save_mtag_to_map(expr, tag, offset, param, key, value);
182 	propogate_assignment(expr, tag, offset, param, key);
183 	assign_to_alias(expr, param, tag, offset, key);
184 }
185 
print_stored_to_mtag(int return_id,char * return_ranges,struct expression * expr)186 static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
187 {
188 	struct sm_state *sm;
189 	struct tag_assign_info *data;
190 	char buf[256];
191 	const char *param_name;
192 	int param;
193 
194 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
195 		if (!sm->state->data)
196 			continue;
197 
198 		param = get_param_num_from_sym(sm->sym);
199 		if (param < 0)
200 			continue;
201 		param_name = get_param_name(sm);
202 		if (!param_name)
203 			continue;
204 
205 		data = sm->state->data;
206 		snprintf(buf, sizeof(buf), "%lld+%d", data->tag, data->offset);
207 		sql_insert_return_states(return_id, return_ranges, MTAG_ASSIGN, param, param_name, buf);
208 	} END_FOR_EACH_SM(sm);
209 }
210 
register_param_to_mtag_data(int id)211 void register_param_to_mtag_data(int id)
212 {
213 	my_id = id;
214 
215 	set_dynamic_states(my_id);
216 	add_hook(&match_assign, ASSIGNMENT_HOOK);
217 	select_return_states_hook(MTAG_ASSIGN, &call_does_mtag_assign);
218 	add_merge_hook(my_id, &merge_tag_info);
219 	add_split_return_callback(&print_stored_to_mtag);
220 }
221 
222