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_rl(sval_t sval)
40 {
41 	struct range_list *rl = NULL;
42 	mtag_t tag = sval.uvalue & ~MTAG_OFFSET_MASK;
43 	int offset = sval.uvalue & MTAG_OFFSET_MASK;
44 
45 	mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
46 		tag, offset);
47 	return rl;
48 }
49 
50 static int is_kernel_param(const char *name)
51 {
52 	struct sm_state *tmp;
53 	char buf[256];
54 
55 	/*
56 	 * I'm ignoring these because otherwise Smatch thinks that kernel
57 	 * parameters are always set to the default.
58 	 *
59 	 */
60 
61 	if (option_project != PROJ_KERNEL)
62 		return 0;
63 
64 	snprintf(buf, sizeof(buf), "__param_%s.arg", name);
65 
66 	FOR_EACH_SM(vals, tmp) {
67 		if (strcmp(tmp->name, buf) == 0)
68 			return 1;
69 	} END_FOR_EACH_SM(tmp);
70 
71 	return 0;
72 }
73 
74 void insert_mtag_data(sval_t sval, struct range_list *rl)
75 {
76 	mtag_t tag = sval.uvalue & ~MTAG_OFFSET_MASK;
77 	int offset = sval.uvalue & MTAG_OFFSET_MASK;
78 
79 	rl = clone_rl_permanent(rl);
80 
81 	mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
82 		tag, offset, DATA_VALUE);
83 	mem_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%lu');",
84 		tag, offset, DATA_VALUE, (unsigned long)rl);
85 }
86 
87 void update_mtag_data(struct expression *expr)
88 {
89 	struct range_list *orig, *new, *rl;
90 	char *name;
91 	sval_t sval;
92 
93 	name = expr_to_var(expr);
94 	if (is_kernel_param(name)) {
95 		free_string(name);
96 		return;
97 	}
98 	free_string(name);
99 
100 	if (!get_mtag_addr_sval(expr, &sval))
101 		return;
102 
103 	get_absolute_rl(expr, &rl);
104 
105 	orig = select_orig_rl(sval);
106 	new = rl_union(orig, rl);
107 	insert_mtag_data(sval, new);
108 }
109 
110 static void match_global_assign(struct expression *expr)
111 {
112 	struct range_list *rl;
113 	sval_t sval;
114 	char *name;
115 
116 	name = expr_to_var(expr->left);
117 	if (is_kernel_param(name)) {
118 		free_string(name);
119 		return;
120 	}
121 	free_string(name);
122 
123 	if (!get_mtag_addr_sval(expr->left, &sval))
124 		return;
125 
126 	get_absolute_rl(expr->right, &rl);
127 	insert_mtag_data(sval, rl);
128 }
129 
130 static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
131 {
132 	struct range_list *rl;
133 
134 	if (argc != 4) {
135 		sm_msg("Error saving mtag data");
136 		return 0;
137 	}
138 	if (!option_info)
139 		return 0;
140 
141 	rl = (struct range_list *)strtoul(argv[3], NULL, 10);
142 	sm_msg("SQL: insert into mtag_data values ('%s', '%s', '%s', '%s');",
143 	       argv[0], argv[1], argv[2], show_rl(rl));
144 
145 	return 0;
146 }
147 
148 static void match_end_file(struct symbol_list *sym_list)
149 {
150 	mem_sql(&save_mtag_data, NULL, "select * from mtag_data where type = %d;",
151 		DATA_VALUE);
152 }
153 
154 struct db_info {
155 	struct symbol *type;
156 	struct range_list *rl;
157 };
158 
159 static int get_vals(void *_db_info, int argc, char **argv, char **azColName)
160 {
161 	struct db_info *db_info = _db_info;
162 	struct range_list *tmp;
163 
164 	str_to_rl(db_info->type, argv[0], &tmp);
165 	if (db_info->rl)
166 		db_info->rl = rl_union(db_info->rl, tmp);
167 	else
168 		db_info->rl = tmp;
169 
170 	return 0;
171 }
172 
173 struct db_cache_results {
174 	sval_t sval;
175 	struct range_list *rl;
176 };
177 static struct db_cache_results cached_results[8];
178 
179 static int get_rl_from_mtag_sval(sval_t sval, struct symbol *type, struct range_list **rl)
180 {
181 	struct db_info db_info = {};
182 	mtag_t tag;
183 	int offset;
184 	static int idx;
185 	int ret;
186 	int i;
187 
188 	for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
189 		if (sval.uvalue == cached_results[i].sval.uvalue) {
190 			if (cached_results[i].rl) {
191 				*rl = cached_results[i].rl;
192 				return 1;
193 			}
194 			return 0;
195 		}
196 	}
197 
198 	tag = sval.uvalue & ~MTAG_OFFSET_MASK;
199 	offset = sval.uvalue & MTAG_OFFSET_MASK;
200 	if (offset == MTAG_OFFSET_MASK) {
201 		ret = 0;
202 		goto update_cache;
203 	}
204 	db_info.type = type;
205 
206 	run_sql(get_vals, &db_info,
207 		"select value from mtag_data where tag = %lld and offset = %d and type = %d;",
208 		tag, offset, DATA_VALUE);
209 	if (!db_info.rl || is_whole_rl(db_info.rl)) {
210 		db_info.rl = NULL;
211 		ret = 0;
212 		goto update_cache;
213 	}
214 
215 	*rl = db_info.rl;
216 	ret = 1;
217 
218 update_cache:
219 	cached_results[idx].sval = sval;
220 	cached_results[idx].rl = db_info.rl;
221 	idx = (idx + 1) % ARRAY_SIZE(cached_results);
222 
223 	return ret;
224 }
225 
226 static void clear_cache(struct symbol *sym)
227 {
228 	memset(cached_results, 0, sizeof(cached_results));
229 }
230 
231 int get_mtag_rl(struct expression *expr, struct range_list **rl)
232 {
233 	struct symbol *type;
234 	sval_t sval;
235 
236 	if (!get_mtag_addr_sval(expr, &sval))
237 		return 0;
238 
239 	type = get_type(expr);
240 	if (!type)
241 		return 0;
242 
243 	return get_rl_from_mtag_sval(sval, type, rl);
244 }
245 
246 void register_mtag_data(int id)
247 {
248 	my_id = id;
249 
250 	add_hook(&clear_cache, FUNC_DEF_HOOK);
251 
252 //	if (!option_info)
253 //		return;
254 	add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
255 	add_hook(&match_end_file, END_FILE_HOOK);
256 }
257 
258