xref: /illumos-gate/usr/src/tools/smatch/src/smatch_mtag_data.c (revision f52943a93040563107b95bccb9db87d9971ef47d)
1 /*
2  * Copyright (C) 2016 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  * What we're doing here is saving all the possible values for static variables.
20  * Later on we might do globals as well.
21  *
22  */
23 
24 #include "smatch.h"
25 #include "smatch_slist.h"
26 #include "smatch_extra.h"
27 
28 static int my_id;
29 static struct stree *vals;
30 
31 static int save_rl(void *_rl, int argc, char **argv, char **azColName)
32 {
33 	unsigned long *rl = _rl;
34 
35 	*rl = strtoul(argv[0], NULL, 10);
36 	return 0;
37 }
38 
39 static struct range_list *select_orig(mtag_t tag, int offset)
40 {
41 	struct range_list *rl = NULL;
42 
43 	mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
44 		tag, offset);
45 	return rl;
46 }
47 
48 static int is_kernel_param(const char *name)
49 {
50 	struct sm_state *tmp;
51 	char buf[256];
52 
53 	/*
54 	 * I'm ignoring these because otherwise Smatch thinks that kernel
55 	 * parameters are always set to the default.
56 	 *
57 	 */
58 
59 	if (option_project != PROJ_KERNEL)
60 		return 0;
61 
62 	snprintf(buf, sizeof(buf), "__param_%s.arg", name);
63 
64 	FOR_EACH_SM(vals, tmp) {
65 		if (strcmp(tmp->name, buf) == 0)
66 			return 1;
67 	} END_FOR_EACH_SM(tmp);
68 
69 	return 0;
70 }
71 
72 static bool is_ignored_macro(struct expression *expr)
73 {
74 	char *macro;
75 
76 	macro = get_macro_name(expr->pos);
77 	if (!macro)
78 		return false;
79 	if (strcmp(macro, "EXPORT_SYMBOL") == 0)
80 		return true;
81 	return false;
82 }
83 
84 static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
85 {
86 	rl = clone_rl_permanent(rl);
87 
88 	mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
89 		tag, offset, DATA_VALUE);
90 	mem_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%lu');",
91 		tag, offset, DATA_VALUE, (unsigned long)rl);
92 }
93 
94 static bool invalid_type(struct symbol *type)
95 {
96 	if (!type)
97 		return true;
98 	if (type == &void_ctype)
99 		return true;
100 	if (type->type == SYM_STRUCT ||
101 	    type->type == SYM_ARRAY ||
102 	    type->type == SYM_UNION)
103 		return true;
104 	return false;
105 }
106 
107 void update_mtag_data(struct expression *expr, struct smatch_state *state)
108 {
109 	struct range_list *orig, *new;
110 	struct symbol *type;
111 	char *name;
112 	mtag_t tag;
113 	int offset;
114 
115 	if (!expr)
116 		return;
117 	if (is_local_variable(expr))
118 		return;
119 	if (is_ignored_macro(expr))
120 		return;
121 	name = expr_to_var(expr);
122 	if (is_kernel_param(name)) {
123 		free_string(name);
124 		return;
125 	}
126 	free_string(name);
127 
128 	if (!expr_to_mtag_offset(expr, &tag, &offset))
129 		return;
130 
131 	type = get_type(expr);
132 	if (offset == 0 && invalid_type(type))
133 		return;
134 
135 	orig = select_orig(tag, offset);
136 	new = rl_union(orig, estate_rl(state));
137 	insert_mtag_data(tag, offset, new);
138 }
139 
140 static void match_global_assign(struct expression *expr)
141 {
142 	struct range_list *rl;
143 	mtag_t tag;
144 	int offset;
145 	char *name;
146 
147 	if (is_ignored_macro(expr))
148 		return;
149 	name = expr_to_var(expr->left);
150 	if (is_kernel_param(name)) {
151 		free_string(name);
152 		return;
153 	}
154 	free_string(name);
155 
156 	if (!expr_to_mtag_offset(expr->left, &tag, &offset))
157 		return;
158 
159 	get_absolute_rl(expr->right, &rl);
160 	insert_mtag_data(tag, offset, rl);
161 }
162 
163 static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
164 {
165 	struct range_list *rl;
166 
167 	if (argc != 4) {
168 		sm_msg("Error saving mtag data");
169 		return 0;
170 	}
171 	if (!option_info)
172 		return 0;
173 
174 	rl = (struct range_list *)strtoul(argv[3], NULL, 10);
175 	sm_msg("SQL: insert into mtag_data values ('%s', '%s', '%s', '%s');",
176 	       argv[0], argv[1], argv[2], show_rl(rl));
177 
178 	return 0;
179 }
180 
181 static void match_end_file(struct symbol_list *sym_list)
182 {
183 	mem_sql(&save_mtag_data, NULL, "select * from mtag_data where type = %d;",
184 		DATA_VALUE);
185 }
186 
187 struct db_info {
188 	struct symbol *type;
189 	struct range_list *rl;
190 };
191 
192 static int get_vals(void *_db_info, int argc, char **argv, char **azColName)
193 {
194 	struct db_info *db_info = _db_info;
195 	struct range_list *tmp;
196 
197 	str_to_rl(db_info->type, argv[0], &tmp);
198 	if (db_info->rl)
199 		db_info->rl = rl_union(db_info->rl, tmp);
200 	else
201 		db_info->rl = tmp;
202 
203 	return 0;
204 }
205 
206 struct db_cache_results {
207 	mtag_t tag;
208 	struct range_list *rl;
209 };
210 static struct db_cache_results cached_results[8];
211 
212 static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, struct range_list **rl)
213 {
214 	struct db_info db_info = {};
215 	mtag_t merged = tag | offset;
216 	static int idx;
217 	int ret;
218 	int i;
219 
220 	for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
221 		if (merged == cached_results[i].tag) {
222 			if (cached_results[i].rl) {
223 				*rl = cached_results[i].rl;
224 				return 1;
225 			}
226 			return 0;
227 		}
228 	}
229 
230 	db_info.type = type;
231 
232 	run_sql(get_vals, &db_info,
233 		"select value from mtag_data where tag = %lld and offset = %d and type = %d;",
234 		tag, offset, DATA_VALUE);
235 	if (!db_info.rl || is_whole_rl(db_info.rl)) {
236 		db_info.rl = NULL;
237 		ret = 0;
238 		goto update_cache;
239 	}
240 
241 	*rl = db_info.rl;
242 	ret = 1;
243 
244 update_cache:
245 	cached_results[idx].tag = merged;
246 	cached_results[idx].rl = db_info.rl;
247 	idx = (idx + 1) % ARRAY_SIZE(cached_results);
248 
249 	return ret;
250 }
251 
252 static void clear_cache(struct symbol *sym)
253 {
254 	memset(cached_results, 0, sizeof(cached_results));
255 }
256 
257 int get_mtag_rl(struct expression *expr, struct range_list **rl)
258 {
259 	struct symbol *type;
260 	mtag_t tag;
261 	int offset;
262 
263 	if (is_local_variable(expr))
264 		return 0;
265 	if (!expr_to_mtag_offset(expr, &tag, &offset))
266 		return 0;
267 	if (offset >= MTAG_OFFSET_MASK)
268 		return 0;
269 
270 	type = get_type(expr);
271 	if (invalid_type(type))
272 		return 0;
273 
274 	return get_rl_from_mtag_offset(tag, offset, type, rl);
275 }
276 
277 void register_mtag_data(int id)
278 {
279 	my_id = id;
280 
281 	add_hook(&clear_cache, FUNC_DEF_HOOK);
282 
283 //	if (!option_info)
284 //		return;
285 	add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
286 	add_hook(&match_end_file, END_FILE_HOOK);
287 }
288 
289