11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2016 Oracle.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon  * What we're doing here is saving all the possible values for static variables.
201f5207b7SJohn Levon  * Later on we might do globals as well.
211f5207b7SJohn Levon  *
221f5207b7SJohn Levon  */
231f5207b7SJohn Levon 
241f5207b7SJohn Levon #include "smatch.h"
251f5207b7SJohn Levon #include "smatch_slist.h"
261f5207b7SJohn Levon #include "smatch_extra.h"
271f5207b7SJohn Levon 
281f5207b7SJohn Levon static int my_id;
291f5207b7SJohn Levon static struct stree *vals;
301f5207b7SJohn Levon 
save_rl(void * _rl,int argc,char ** argv,char ** azColName)311f5207b7SJohn Levon static int save_rl(void *_rl, int argc, char **argv, char **azColName)
321f5207b7SJohn Levon {
331f5207b7SJohn Levon 	unsigned long *rl = _rl;
341f5207b7SJohn Levon 
351f5207b7SJohn Levon 	*rl = strtoul(argv[0], NULL, 10);
361f5207b7SJohn Levon 	return 0;
371f5207b7SJohn Levon }
381f5207b7SJohn Levon 
select_orig(mtag_t tag,int offset)39efe51d0cSJohn Levon static struct range_list *select_orig(mtag_t tag, int offset)
401f5207b7SJohn Levon {
411f5207b7SJohn Levon 	struct range_list *rl = NULL;
421f5207b7SJohn Levon 
431f5207b7SJohn Levon 	mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
441f5207b7SJohn Levon 		tag, offset);
451f5207b7SJohn Levon 	return rl;
461f5207b7SJohn Levon }
471f5207b7SJohn Levon 
is_kernel_param(const char * name)481f5207b7SJohn Levon static int is_kernel_param(const char *name)
491f5207b7SJohn Levon {
501f5207b7SJohn Levon 	struct sm_state *tmp;
511f5207b7SJohn Levon 	char buf[256];
521f5207b7SJohn Levon 
531f5207b7SJohn Levon 	/*
541f5207b7SJohn Levon 	 * I'm ignoring these because otherwise Smatch thinks that kernel
551f5207b7SJohn Levon 	 * parameters are always set to the default.
561f5207b7SJohn Levon 	 *
571f5207b7SJohn Levon 	 */
581f5207b7SJohn Levon 
591f5207b7SJohn Levon 	if (option_project != PROJ_KERNEL)
601f5207b7SJohn Levon 		return 0;
611f5207b7SJohn Levon 
621f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "__param_%s.arg", name);
631f5207b7SJohn Levon 
641f5207b7SJohn Levon 	FOR_EACH_SM(vals, tmp) {
651f5207b7SJohn Levon 		if (strcmp(tmp->name, buf) == 0)
661f5207b7SJohn Levon 			return 1;
671f5207b7SJohn Levon 	} END_FOR_EACH_SM(tmp);
681f5207b7SJohn Levon 
691f5207b7SJohn Levon 	return 0;
701f5207b7SJohn Levon }
711f5207b7SJohn Levon 
is_ignored_macro(struct expression * expr)72c85f09ccSJohn Levon static bool is_ignored_macro(struct expression *expr)
73c85f09ccSJohn Levon {
74c85f09ccSJohn Levon 	char *macro;
75c85f09ccSJohn Levon 
76c85f09ccSJohn Levon 	macro = get_macro_name(expr->pos);
77c85f09ccSJohn Levon 	if (!macro)
78c85f09ccSJohn Levon 		return false;
79c85f09ccSJohn Levon 	if (strcmp(macro, "EXPORT_SYMBOL") == 0)
80c85f09ccSJohn Levon 		return true;
81c85f09ccSJohn Levon 	return false;
82c85f09ccSJohn Levon }
83c85f09ccSJohn Levon 
is_head_next(struct expression * expr)84*6523a3aaSJohn Levon static bool is_head_next(struct expression *expr)
85*6523a3aaSJohn Levon {
86*6523a3aaSJohn Levon 	struct symbol *type;
87*6523a3aaSJohn Levon 
88*6523a3aaSJohn Levon 	/* Smatch thinks head->next == head is always true.  *sad face* */
89*6523a3aaSJohn Levon 
90*6523a3aaSJohn Levon 	if (option_project != PROJ_KERNEL)
91*6523a3aaSJohn Levon 		return false;
92*6523a3aaSJohn Levon 
93*6523a3aaSJohn Levon 	if (expr->type != EXPR_DEREF)
94*6523a3aaSJohn Levon 		return false;
95*6523a3aaSJohn Levon 	if (!expr->member || !expr->member->name ||
96*6523a3aaSJohn Levon 	    strcmp(expr->member->name, "next") != 0)
97*6523a3aaSJohn Levon 		return false;
98*6523a3aaSJohn Levon 
99*6523a3aaSJohn Levon 	type = get_type(expr->deref);
100*6523a3aaSJohn Levon 	if (!type)
101*6523a3aaSJohn Levon 		return false;
102*6523a3aaSJohn Levon 	if (type->type == SYM_PTR)
103*6523a3aaSJohn Levon 		type = get_real_base_type(type);
104*6523a3aaSJohn Levon 	if (type->type != SYM_STRUCT)
105*6523a3aaSJohn Levon 		return false;
106*6523a3aaSJohn Levon 	if (!type->ident || !type->ident->name ||
107*6523a3aaSJohn Levon 	    strcmp(type->ident->name, "list_head") != 0)
108*6523a3aaSJohn Levon 		return false;
109*6523a3aaSJohn Levon 	return true;
110*6523a3aaSJohn Levon }
111*6523a3aaSJohn Levon 
112*6523a3aaSJohn Levon mtag_t ignored_mtag;
is_ignored_tag(mtag_t tag)113*6523a3aaSJohn Levon static bool is_ignored_tag(mtag_t tag)
114*6523a3aaSJohn Levon {
115*6523a3aaSJohn Levon 	if (tag == ignored_mtag)
116*6523a3aaSJohn Levon 		return true;
117*6523a3aaSJohn Levon 	return false;
118*6523a3aaSJohn Levon }
119*6523a3aaSJohn Levon 
insert_mtag_data(mtag_t tag,int offset,struct range_list * rl)120efe51d0cSJohn Levon static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
1211f5207b7SJohn Levon {
122*6523a3aaSJohn Levon 	if (is_ignored_tag(tag))
123*6523a3aaSJohn Levon 		return;
124*6523a3aaSJohn Levon 
1251f5207b7SJohn Levon 	rl = clone_rl_permanent(rl);
1261f5207b7SJohn Levon 
1271f5207b7SJohn Levon 	mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
1281f5207b7SJohn Levon 		tag, offset, DATA_VALUE);
1291f5207b7SJohn Levon 	mem_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%lu');",
1301f5207b7SJohn Levon 		tag, offset, DATA_VALUE, (unsigned long)rl);
1311f5207b7SJohn Levon }
1321f5207b7SJohn Levon 
invalid_type(struct symbol * type)133c85f09ccSJohn Levon static bool invalid_type(struct symbol *type)
134c85f09ccSJohn Levon {
135c85f09ccSJohn Levon 	if (!type)
136c85f09ccSJohn Levon 		return true;
137c85f09ccSJohn Levon 	if (type == &void_ctype)
138c85f09ccSJohn Levon 		return true;
139c85f09ccSJohn Levon 	if (type->type == SYM_STRUCT ||
140c85f09ccSJohn Levon 	    type->type == SYM_ARRAY ||
141c85f09ccSJohn Levon 	    type->type == SYM_UNION)
142c85f09ccSJohn Levon 		return true;
143c85f09ccSJohn Levon 	return false;
144c85f09ccSJohn Levon }
145c85f09ccSJohn Levon 
parent_is_fresh_alloc(struct expression * expr)146*6523a3aaSJohn Levon static bool parent_is_fresh_alloc(struct expression *expr)
147*6523a3aaSJohn Levon {
148*6523a3aaSJohn Levon 	struct symbol *sym;
149*6523a3aaSJohn Levon 
150*6523a3aaSJohn Levon 	sym = expr_to_sym(expr);
151*6523a3aaSJohn Levon 	if (!sym || !sym->ident)
152*6523a3aaSJohn Levon 		return false;
153*6523a3aaSJohn Levon 	return is_fresh_alloc_var_sym(sym->ident->name, sym);
154*6523a3aaSJohn Levon }
155*6523a3aaSJohn Levon 
update_mtag_data(struct expression * expr,struct smatch_state * state)156c85f09ccSJohn Levon void update_mtag_data(struct expression *expr, struct smatch_state *state)
1571f5207b7SJohn Levon {
158c85f09ccSJohn Levon 	struct range_list *orig, *new;
159efe51d0cSJohn Levon 	struct symbol *type;
1601f5207b7SJohn Levon 	char *name;
161efe51d0cSJohn Levon 	mtag_t tag;
162efe51d0cSJohn Levon 	int offset;
1631f5207b7SJohn Levon 
164c85f09ccSJohn Levon 	if (!expr)
165c85f09ccSJohn Levon 		return;
166c85f09ccSJohn Levon 	if (is_local_variable(expr))
167c85f09ccSJohn Levon 		return;
168c85f09ccSJohn Levon 	if (is_ignored_macro(expr))
169c85f09ccSJohn Levon 		return;
170*6523a3aaSJohn Levon 	if (is_head_next(expr))
171*6523a3aaSJohn Levon 		return;
1721f5207b7SJohn Levon 	name = expr_to_var(expr);
1731f5207b7SJohn Levon 	if (is_kernel_param(name)) {
1741f5207b7SJohn Levon 		free_string(name);
1751f5207b7SJohn Levon 		return;
1761f5207b7SJohn Levon 	}
1771f5207b7SJohn Levon 	free_string(name);
1781f5207b7SJohn Levon 
179efe51d0cSJohn Levon 	if (!expr_to_mtag_offset(expr, &tag, &offset))
180efe51d0cSJohn Levon 		return;
181efe51d0cSJohn Levon 
182efe51d0cSJohn Levon 	type = get_type(expr);
183c85f09ccSJohn Levon 	if (offset == 0 && invalid_type(type))
1841f5207b7SJohn Levon 		return;
1851f5207b7SJohn Levon 
186*6523a3aaSJohn Levon 	if (parent_is_fresh_alloc(expr))
187*6523a3aaSJohn Levon 		orig = NULL;
188*6523a3aaSJohn Levon 	else
189*6523a3aaSJohn Levon 		orig = select_orig(tag, offset);
190c85f09ccSJohn Levon 	new = rl_union(orig, estate_rl(state));
191efe51d0cSJohn Levon 	insert_mtag_data(tag, offset, new);
1921f5207b7SJohn Levon }
1931f5207b7SJohn Levon 
match_global_assign(struct expression * expr)1941f5207b7SJohn Levon static void match_global_assign(struct expression *expr)
1951f5207b7SJohn Levon {
1961f5207b7SJohn Levon 	struct range_list *rl;
197efe51d0cSJohn Levon 	mtag_t tag;
198efe51d0cSJohn Levon 	int offset;
1991f5207b7SJohn Levon 	char *name;
2001f5207b7SJohn Levon 
201c85f09ccSJohn Levon 	if (is_ignored_macro(expr))
202c85f09ccSJohn Levon 		return;
203*6523a3aaSJohn Levon 	if (is_head_next(expr->left))
204*6523a3aaSJohn Levon 		return;
2051f5207b7SJohn Levon 	name = expr_to_var(expr->left);
2061f5207b7SJohn Levon 	if (is_kernel_param(name)) {
2071f5207b7SJohn Levon 		free_string(name);
2081f5207b7SJohn Levon 		return;
2091f5207b7SJohn Levon 	}
2101f5207b7SJohn Levon 	free_string(name);
2111f5207b7SJohn Levon 
212efe51d0cSJohn Levon 	if (!expr_to_mtag_offset(expr->left, &tag, &offset))
2131f5207b7SJohn Levon 		return;
2141f5207b7SJohn Levon 
2151f5207b7SJohn Levon 	get_absolute_rl(expr->right, &rl);
216efe51d0cSJohn Levon 	insert_mtag_data(tag, offset, rl);
2171f5207b7SJohn Levon }
2181f5207b7SJohn Levon 
save_mtag_data(void * _unused,int argc,char ** argv,char ** azColName)2191f5207b7SJohn Levon static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
2201f5207b7SJohn Levon {
2211f5207b7SJohn Levon 	struct range_list *rl;
2221f5207b7SJohn Levon 
2231f5207b7SJohn Levon 	if (argc != 4) {
2241f5207b7SJohn Levon 		sm_msg("Error saving mtag data");
2251f5207b7SJohn Levon 		return 0;
2261f5207b7SJohn Levon 	}
2271f5207b7SJohn Levon 	if (!option_info)
2281f5207b7SJohn Levon 		return 0;
2291f5207b7SJohn Levon 
2301f5207b7SJohn Levon 	rl = (struct range_list *)strtoul(argv[3], NULL, 10);
2311f5207b7SJohn Levon 	sm_msg("SQL: insert into mtag_data values ('%s', '%s', '%s', '%s');",
2321f5207b7SJohn Levon 	       argv[0], argv[1], argv[2], show_rl(rl));
2331f5207b7SJohn Levon 
2341f5207b7SJohn Levon 	return 0;
2351f5207b7SJohn Levon }
2361f5207b7SJohn Levon 
match_end_file(struct symbol_list * sym_list)2371f5207b7SJohn Levon static void match_end_file(struct symbol_list *sym_list)
2381f5207b7SJohn Levon {
2391f5207b7SJohn Levon 	mem_sql(&save_mtag_data, NULL, "select * from mtag_data where type = %d;",
2401f5207b7SJohn Levon 		DATA_VALUE);
2411f5207b7SJohn Levon }
2421f5207b7SJohn Levon 
2431f5207b7SJohn Levon struct db_info {
2441f5207b7SJohn Levon 	struct symbol *type;
2451f5207b7SJohn Levon 	struct range_list *rl;
2461f5207b7SJohn Levon };
2471f5207b7SJohn Levon 
get_vals(void * _db_info,int argc,char ** argv,char ** azColName)2481f5207b7SJohn Levon static int get_vals(void *_db_info, int argc, char **argv, char **azColName)
2491f5207b7SJohn Levon {
2501f5207b7SJohn Levon 	struct db_info *db_info = _db_info;
2511f5207b7SJohn Levon 	struct range_list *tmp;
2521f5207b7SJohn Levon 
2531f5207b7SJohn Levon 	str_to_rl(db_info->type, argv[0], &tmp);
2541f5207b7SJohn Levon 	if (db_info->rl)
2551f5207b7SJohn Levon 		db_info->rl = rl_union(db_info->rl, tmp);
2561f5207b7SJohn Levon 	else
2571f5207b7SJohn Levon 		db_info->rl = tmp;
2581f5207b7SJohn Levon 
2591f5207b7SJohn Levon 	return 0;
2601f5207b7SJohn Levon }
2611f5207b7SJohn Levon 
2621f5207b7SJohn Levon struct db_cache_results {
263efe51d0cSJohn Levon 	mtag_t tag;
2641f5207b7SJohn Levon 	struct range_list *rl;
2651f5207b7SJohn Levon };
2661f5207b7SJohn Levon static struct db_cache_results cached_results[8];
2671f5207b7SJohn Levon 
get_rl_from_mtag_offset(mtag_t tag,int offset,struct symbol * type,struct range_list ** rl)268efe51d0cSJohn Levon static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, struct range_list **rl)
2691f5207b7SJohn Levon {
2701f5207b7SJohn Levon 	struct db_info db_info = {};
271efe51d0cSJohn Levon 	mtag_t merged = tag | offset;
2721f5207b7SJohn Levon 	static int idx;
2731f5207b7SJohn Levon 	int ret;
2741f5207b7SJohn Levon 	int i;
2751f5207b7SJohn Levon 
2761f5207b7SJohn Levon 	for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
277efe51d0cSJohn Levon 		if (merged == cached_results[i].tag) {
2781f5207b7SJohn Levon 			if (cached_results[i].rl) {
2791f5207b7SJohn Levon 				*rl = cached_results[i].rl;
2801f5207b7SJohn Levon 				return 1;
2811f5207b7SJohn Levon 			}
2821f5207b7SJohn Levon 			return 0;
2831f5207b7SJohn Levon 		}
2841f5207b7SJohn Levon 	}
2851f5207b7SJohn Levon 
2861f5207b7SJohn Levon 	db_info.type = type;
2871f5207b7SJohn Levon 
2881f5207b7SJohn Levon 	run_sql(get_vals, &db_info,
2891f5207b7SJohn Levon 		"select value from mtag_data where tag = %lld and offset = %d and type = %d;",
2901f5207b7SJohn Levon 		tag, offset, DATA_VALUE);
2911f5207b7SJohn Levon 	if (!db_info.rl || is_whole_rl(db_info.rl)) {
2921f5207b7SJohn Levon 		db_info.rl = NULL;
2931f5207b7SJohn Levon 		ret = 0;
2941f5207b7SJohn Levon 		goto update_cache;
2951f5207b7SJohn Levon 	}
2961f5207b7SJohn Levon 
2971f5207b7SJohn Levon 	*rl = db_info.rl;
2981f5207b7SJohn Levon 	ret = 1;
2991f5207b7SJohn Levon 
3001f5207b7SJohn Levon update_cache:
301efe51d0cSJohn Levon 	cached_results[idx].tag = merged;
3021f5207b7SJohn Levon 	cached_results[idx].rl = db_info.rl;
3031f5207b7SJohn Levon 	idx = (idx + 1) % ARRAY_SIZE(cached_results);
3041f5207b7SJohn Levon 
3051f5207b7SJohn Levon 	return ret;
3061f5207b7SJohn Levon }
3071f5207b7SJohn Levon 
clear_cache(struct symbol * sym)3081f5207b7SJohn Levon static void clear_cache(struct symbol *sym)
3091f5207b7SJohn Levon {
3101f5207b7SJohn Levon 	memset(cached_results, 0, sizeof(cached_results));
3111f5207b7SJohn Levon }
3121f5207b7SJohn Levon 
get_mtag_rl(struct expression * expr,struct range_list ** rl)3131f5207b7SJohn Levon int get_mtag_rl(struct expression *expr, struct range_list **rl)
3141f5207b7SJohn Levon {
3151f5207b7SJohn Levon 	struct symbol *type;
316efe51d0cSJohn Levon 	mtag_t tag;
317efe51d0cSJohn Levon 	int offset;
3181f5207b7SJohn Levon 
319c85f09ccSJohn Levon 	if (is_local_variable(expr))
320c85f09ccSJohn Levon 		return 0;
321efe51d0cSJohn Levon 	if (!expr_to_mtag_offset(expr, &tag, &offset))
322efe51d0cSJohn Levon 		return 0;
323efe51d0cSJohn Levon 	if (offset >= MTAG_OFFSET_MASK)
3241f5207b7SJohn Levon 		return 0;
3251f5207b7SJohn Levon 
3261f5207b7SJohn Levon 	type = get_type(expr);
327c85f09ccSJohn Levon 	if (invalid_type(type))
3281f5207b7SJohn Levon 		return 0;
3291f5207b7SJohn Levon 
330efe51d0cSJohn Levon 	return get_rl_from_mtag_offset(tag, offset, type, rl);
3311f5207b7SJohn Levon }
3321f5207b7SJohn Levon 
register_mtag_data(int id)3331f5207b7SJohn Levon void register_mtag_data(int id)
3341f5207b7SJohn Levon {
3351f5207b7SJohn Levon 	my_id = id;
3361f5207b7SJohn Levon 
337*6523a3aaSJohn Levon 	ignored_mtag = str_to_mtag("extern boot_params");
3381f5207b7SJohn Levon 	add_hook(&clear_cache, FUNC_DEF_HOOK);
3391f5207b7SJohn Levon 
3401f5207b7SJohn Levon //	if (!option_info)
3411f5207b7SJohn Levon //		return;
3421f5207b7SJohn Levon 	add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
3431f5207b7SJohn Levon 	add_hook(&match_end_file, END_FILE_HOOK);
3441f5207b7SJohn Levon }
3451f5207b7SJohn Levon 
346