131ad075eSJohn Levon /*
231ad075eSJohn Levon  * Copyright (C) 2019 Oracle.
331ad075eSJohn Levon  *
431ad075eSJohn Levon  * This program is free software; you can redistribute it and/or
531ad075eSJohn Levon  * modify it under the terms of the GNU General Public License
631ad075eSJohn Levon  * as published by the Free Software Foundation; either version 2
731ad075eSJohn Levon  * of the License, or (at your option) any later version.
831ad075eSJohn Levon  *
931ad075eSJohn Levon  * This program is distributed in the hope that it will be useful,
1031ad075eSJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1131ad075eSJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1231ad075eSJohn Levon  * GNU General Public License for more details.
1331ad075eSJohn Levon  *
1431ad075eSJohn Levon  * You should have received a copy of the GNU General Public License
1531ad075eSJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
1631ad075eSJohn Levon  */
1731ad075eSJohn Levon 
1831ad075eSJohn Levon /*
1931ad075eSJohn Levon  * There are a bunch of allocation functions where we allocate some memory,
2031ad075eSJohn Levon  * set up some struct members and then return the allocated memory.  One
2131ad075eSJohn Levon  * nice thing about this is that we just one pointer to the allocated memory
2231ad075eSJohn Levon  * so what we can do is we can generate a mtag alias for it in the caller.
2331ad075eSJohn Levon  */
2431ad075eSJohn Levon 
2531ad075eSJohn Levon #include "smatch.h"
26*6523a3aaSJohn Levon #include "smatch_extra.h"
2731ad075eSJohn Levon #include "smatch_slist.h"
2831ad075eSJohn Levon 
2931ad075eSJohn Levon static int my_id;
3031ad075eSJohn Levon 
3131ad075eSJohn Levon STATE(fresh);
3231ad075eSJohn Levon 
3331ad075eSJohn Levon struct alloc_info *alloc_funcs;
3431ad075eSJohn Levon 
3531ad075eSJohn Levon struct alloc_info kernel_allocation_funcs[] = {
3631ad075eSJohn Levon 	{"kmalloc", 0},
3731ad075eSJohn Levon 	{"kmalloc_node", 0},
3831ad075eSJohn Levon 	{"kzalloc", 0},
3931ad075eSJohn Levon 	{"kzalloc_node", 0},
4031ad075eSJohn Levon 	{"vmalloc", 0},
4131ad075eSJohn Levon 	{"__vmalloc", 0},
4231ad075eSJohn Levon 	{"kvmalloc", 0},
4331ad075eSJohn Levon 	{"kcalloc", 0, 1},
4431ad075eSJohn Levon 	{"kmalloc_array", 0, 1},
4531ad075eSJohn Levon 	{"sock_kmalloc", 1},
4631ad075eSJohn Levon 	{"kmemdup", 1},
4731ad075eSJohn Levon 	{"kmemdup_user", 1},
4831ad075eSJohn Levon 	{"dma_alloc_attrs", 1},
4931ad075eSJohn Levon 	{"pci_alloc_consistent", 1},
5031ad075eSJohn Levon 	{"pci_alloc_coherent", 1},
5131ad075eSJohn Levon 	{"devm_kmalloc", 1},
5231ad075eSJohn Levon 	{"devm_kzalloc", 1},
5331ad075eSJohn Levon 	{"krealloc", 1},
5431ad075eSJohn Levon 	{"__alloc_bootmem", 0},
5531ad075eSJohn Levon 	{"alloc_bootmem", 0},
5631ad075eSJohn Levon 	{"dma_alloc_contiguous", 1},
5731ad075eSJohn Levon 	{"dma_alloc_coherent", 1},
5831ad075eSJohn Levon 	{},
5931ad075eSJohn Levon };
6031ad075eSJohn Levon 
6131ad075eSJohn Levon struct alloc_info general_allocation_funcs[] = {
6231ad075eSJohn Levon 	{"malloc", 0},
6331ad075eSJohn Levon 	{"calloc", 0, 1},
6431ad075eSJohn Levon 	{"memdup", 1},
6531ad075eSJohn Levon 	{"realloc", 1},
6631ad075eSJohn Levon 	{},
6731ad075eSJohn Levon };
6831ad075eSJohn Levon 
pre_merge_hook(struct sm_state * cur,struct sm_state * other)69*6523a3aaSJohn Levon static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
70*6523a3aaSJohn Levon {
71*6523a3aaSJohn Levon 	struct smatch_state *state;
72*6523a3aaSJohn Levon 	sval_t sval;
73*6523a3aaSJohn Levon 
74*6523a3aaSJohn Levon 	state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
75*6523a3aaSJohn Levon 	if (estate_get_single_value(state, &sval) && sval.value == 0)
76*6523a3aaSJohn Levon 		set_state(my_id, cur->name, cur->sym, &undefined);
77*6523a3aaSJohn Levon }
78*6523a3aaSJohn Levon 
fresh_callback(void * fresh,int argc,char ** argv,char ** azColName)7931ad075eSJohn Levon static int fresh_callback(void *fresh, int argc, char **argv, char **azColName)
8031ad075eSJohn Levon {
8131ad075eSJohn Levon 	*(int *)fresh = 1;
8231ad075eSJohn Levon 	return 0;
8331ad075eSJohn Levon }
8431ad075eSJohn Levon 
fresh_from_db(struct expression * call)8531ad075eSJohn Levon static int fresh_from_db(struct expression *call)
8631ad075eSJohn Levon {
8731ad075eSJohn Levon 	int fresh = 0;
8831ad075eSJohn Levon 
8931ad075eSJohn Levon 	/* for function pointers assume everything is used */
9031ad075eSJohn Levon 	if (call->fn->type != EXPR_SYMBOL)
9131ad075eSJohn Levon 		return 0;
9231ad075eSJohn Levon 
9331ad075eSJohn Levon 	run_sql(&fresh_callback, &fresh,
9431ad075eSJohn Levon 		"select * from return_states where %s and type = %d and parameter = -1 and key = '$' limit 1;",
9531ad075eSJohn Levon 		get_static_filter(call->fn->symbol), FRESH_ALLOC);
9631ad075eSJohn Levon 	return fresh;
9731ad075eSJohn Levon }
9831ad075eSJohn Levon 
is_fresh_alloc_var_sym(const char * var,struct symbol * sym)9931ad075eSJohn Levon bool is_fresh_alloc_var_sym(const char *var, struct symbol *sym)
10031ad075eSJohn Levon {
10131ad075eSJohn Levon 	return get_state(my_id, var, sym) == &fresh;
10231ad075eSJohn Levon }
10331ad075eSJohn Levon 
is_fresh_alloc(struct expression * expr)10431ad075eSJohn Levon bool is_fresh_alloc(struct expression *expr)
10531ad075eSJohn Levon {
10631ad075eSJohn Levon 	sval_t sval;
10731ad075eSJohn Levon 	int i;
10831ad075eSJohn Levon 
10931ad075eSJohn Levon 	if (!expr)
11031ad075eSJohn Levon 		return false;
11131ad075eSJohn Levon 
11231ad075eSJohn Levon 	if (get_implied_value_fast(expr, &sval) && sval.value == 0)
11331ad075eSJohn Levon 		return false;
11431ad075eSJohn Levon 
11531ad075eSJohn Levon 	if (get_state_expr(my_id, expr) == &fresh)
11631ad075eSJohn Levon 		return true;
11731ad075eSJohn Levon 
11831ad075eSJohn Levon 	if (expr->type != EXPR_CALL)
11931ad075eSJohn Levon 		return false;
12031ad075eSJohn Levon 	if (fresh_from_db(expr))
12131ad075eSJohn Levon 		return true;
12231ad075eSJohn Levon 	i = -1;
12331ad075eSJohn Levon 	while (alloc_funcs[++i].fn) {
12431ad075eSJohn Levon 		if (sym_name_is(kernel_allocation_funcs[i].fn, expr->fn))
12531ad075eSJohn Levon 			return true;
12631ad075eSJohn Levon 	}
12731ad075eSJohn Levon 	return false;
12831ad075eSJohn Levon }
12931ad075eSJohn Levon 
record_alloc_func(int return_id,char * return_ranges,struct expression * expr)13031ad075eSJohn Levon static void record_alloc_func(int return_id, char *return_ranges, struct expression *expr)
13131ad075eSJohn Levon {
13231ad075eSJohn Levon 	if (!is_fresh_alloc(expr))
13331ad075eSJohn Levon 		return;
13431ad075eSJohn Levon 	sql_insert_return_states(return_id, return_ranges, FRESH_ALLOC, -1, "$", "");
13531ad075eSJohn Levon }
13631ad075eSJohn Levon 
set_unfresh(struct expression * expr)13731ad075eSJohn Levon static void set_unfresh(struct expression *expr)
13831ad075eSJohn Levon {
13931ad075eSJohn Levon 	struct sm_state *sm;
14031ad075eSJohn Levon 
14131ad075eSJohn Levon 	sm = get_sm_state_expr(my_id, expr);
14231ad075eSJohn Levon 	if (!sm)
14331ad075eSJohn Levon 		return;
14431ad075eSJohn Levon 	if (!slist_has_state(sm->possible, &fresh))
14531ad075eSJohn Levon 		return;
14631ad075eSJohn Levon 	// TODO call unfresh hooks
14731ad075eSJohn Levon 	set_state_expr(my_id, expr, &undefined);
14831ad075eSJohn Levon }
14931ad075eSJohn Levon 
match_assign(struct expression * expr)15031ad075eSJohn Levon static void match_assign(struct expression *expr)
15131ad075eSJohn Levon {
15231ad075eSJohn Levon 	set_unfresh(expr->right);
15331ad075eSJohn Levon }
15431ad075eSJohn Levon 
match_call(struct expression * expr)15531ad075eSJohn Levon static void match_call(struct expression *expr)
15631ad075eSJohn Levon {
15731ad075eSJohn Levon 	struct expression *arg;
15831ad075eSJohn Levon 
15931ad075eSJohn Levon 	FOR_EACH_PTR(expr->args, arg) {
16031ad075eSJohn Levon 		set_unfresh(arg);
16131ad075eSJohn Levon 	} END_FOR_EACH_PTR(arg);
16231ad075eSJohn Levon }
16331ad075eSJohn Levon 
164*6523a3aaSJohn Levon static struct expression *handled;
set_fresh(struct expression * expr)16531ad075eSJohn Levon static void set_fresh(struct expression *expr)
16631ad075eSJohn Levon {
167*6523a3aaSJohn Levon 	struct range_list *rl;
168*6523a3aaSJohn Levon 
16931ad075eSJohn Levon 	expr = strip_expr(expr);
17031ad075eSJohn Levon 	if (expr->type != EXPR_SYMBOL)
17131ad075eSJohn Levon 		return;
172*6523a3aaSJohn Levon 	if (expr == handled)
173*6523a3aaSJohn Levon 		return;
174*6523a3aaSJohn Levon 
175*6523a3aaSJohn Levon 	get_absolute_rl(expr, &rl);
176*6523a3aaSJohn Levon 	rl = rl_intersection(rl, valid_ptr_rl);
177*6523a3aaSJohn Levon 	if (!rl)
178*6523a3aaSJohn Levon 		return;
17931ad075eSJohn Levon 	set_state_expr(my_id, expr, &fresh);
180*6523a3aaSJohn Levon 	handled = expr;
18131ad075eSJohn Levon }
18231ad075eSJohn Levon 
returns_fresh_alloc(struct expression * expr,int param,char * key,char * value)18331ad075eSJohn Levon static void returns_fresh_alloc(struct expression *expr, int param, char *key, char *value)
18431ad075eSJohn Levon {
18531ad075eSJohn Levon 	if (param != -1 || !key || strcmp(key, "$") != 0)
18631ad075eSJohn Levon 		return;
18731ad075eSJohn Levon 	if (expr->type != EXPR_ASSIGNMENT)
18831ad075eSJohn Levon 		return;
18931ad075eSJohn Levon 
19031ad075eSJohn Levon 	set_fresh(expr->left);
19131ad075eSJohn Levon }
19231ad075eSJohn Levon 
match_alloc(const char * fn,struct expression * expr,void * _size_arg)19331ad075eSJohn Levon static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
19431ad075eSJohn Levon {
19531ad075eSJohn Levon 	set_fresh(expr->left);
19631ad075eSJohn Levon }
19731ad075eSJohn Levon 
register_fresh_alloc(int id)19831ad075eSJohn Levon void register_fresh_alloc(int id)
19931ad075eSJohn Levon {
20031ad075eSJohn Levon 	int i;
20131ad075eSJohn Levon 
20231ad075eSJohn Levon 	my_id = id;
20331ad075eSJohn Levon 
20431ad075eSJohn Levon 	if (option_project == PROJ_KERNEL)
20531ad075eSJohn Levon 		alloc_funcs = kernel_allocation_funcs;
20631ad075eSJohn Levon 	else
20731ad075eSJohn Levon 		alloc_funcs = general_allocation_funcs;
20831ad075eSJohn Levon 
20931ad075eSJohn Levon 	i = -1;
21031ad075eSJohn Levon 	while (alloc_funcs[++i].fn)
21131ad075eSJohn Levon 		add_function_assign_hook(alloc_funcs[i].fn, &match_alloc, 0);
21231ad075eSJohn Levon 
21331ad075eSJohn Levon 	add_split_return_callback(&record_alloc_func);
21431ad075eSJohn Levon 	select_return_states_hook(FRESH_ALLOC, &returns_fresh_alloc);
21531ad075eSJohn Levon 	add_hook(&match_assign, ASSIGNMENT_HOOK);
21631ad075eSJohn Levon 	add_hook(&match_call, FUNCTION_CALL_HOOK);
217*6523a3aaSJohn Levon 
218*6523a3aaSJohn Levon 	add_pre_merge_hook(my_id, &pre_merge_hook);
21931ad075eSJohn Levon }
220