xref: /illumos-gate/usr/src/tools/smatch/src/smatch_fresh_alloc.c (revision 6523a3aa7f325d64841382707603be7a86e68147)
1 /*
2  * Copyright (C) 2019 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  * There are a bunch of allocation functions where we allocate some memory,
20  * set up some struct members and then return the allocated memory.  One
21  * nice thing about this is that we just one pointer to the allocated memory
22  * so what we can do is we can generate a mtag alias for it in the caller.
23  */
24 
25 #include "smatch.h"
26 #include "smatch_extra.h"
27 #include "smatch_slist.h"
28 
29 static int my_id;
30 
31 STATE(fresh);
32 
33 struct alloc_info *alloc_funcs;
34 
35 struct alloc_info kernel_allocation_funcs[] = {
36 	{"kmalloc", 0},
37 	{"kmalloc_node", 0},
38 	{"kzalloc", 0},
39 	{"kzalloc_node", 0},
40 	{"vmalloc", 0},
41 	{"__vmalloc", 0},
42 	{"kvmalloc", 0},
43 	{"kcalloc", 0, 1},
44 	{"kmalloc_array", 0, 1},
45 	{"sock_kmalloc", 1},
46 	{"kmemdup", 1},
47 	{"kmemdup_user", 1},
48 	{"dma_alloc_attrs", 1},
49 	{"pci_alloc_consistent", 1},
50 	{"pci_alloc_coherent", 1},
51 	{"devm_kmalloc", 1},
52 	{"devm_kzalloc", 1},
53 	{"krealloc", 1},
54 	{"__alloc_bootmem", 0},
55 	{"alloc_bootmem", 0},
56 	{"dma_alloc_contiguous", 1},
57 	{"dma_alloc_coherent", 1},
58 	{},
59 };
60 
61 struct alloc_info general_allocation_funcs[] = {
62 	{"malloc", 0},
63 	{"calloc", 0, 1},
64 	{"memdup", 1},
65 	{"realloc", 1},
66 	{},
67 };
68 
pre_merge_hook(struct sm_state * cur,struct sm_state * other)69 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
70 {
71 	struct smatch_state *state;
72 	sval_t sval;
73 
74 	state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
75 	if (estate_get_single_value(state, &sval) && sval.value == 0)
76 		set_state(my_id, cur->name, cur->sym, &undefined);
77 }
78 
fresh_callback(void * fresh,int argc,char ** argv,char ** azColName)79 static int fresh_callback(void *fresh, int argc, char **argv, char **azColName)
80 {
81 	*(int *)fresh = 1;
82 	return 0;
83 }
84 
fresh_from_db(struct expression * call)85 static int fresh_from_db(struct expression *call)
86 {
87 	int fresh = 0;
88 
89 	/* for function pointers assume everything is used */
90 	if (call->fn->type != EXPR_SYMBOL)
91 		return 0;
92 
93 	run_sql(&fresh_callback, &fresh,
94 		"select * from return_states where %s and type = %d and parameter = -1 and key = '$' limit 1;",
95 		get_static_filter(call->fn->symbol), FRESH_ALLOC);
96 	return fresh;
97 }
98 
is_fresh_alloc_var_sym(const char * var,struct symbol * sym)99 bool is_fresh_alloc_var_sym(const char *var, struct symbol *sym)
100 {
101 	return get_state(my_id, var, sym) == &fresh;
102 }
103 
is_fresh_alloc(struct expression * expr)104 bool is_fresh_alloc(struct expression *expr)
105 {
106 	sval_t sval;
107 	int i;
108 
109 	if (!expr)
110 		return false;
111 
112 	if (get_implied_value_fast(expr, &sval) && sval.value == 0)
113 		return false;
114 
115 	if (get_state_expr(my_id, expr) == &fresh)
116 		return true;
117 
118 	if (expr->type != EXPR_CALL)
119 		return false;
120 	if (fresh_from_db(expr))
121 		return true;
122 	i = -1;
123 	while (alloc_funcs[++i].fn) {
124 		if (sym_name_is(kernel_allocation_funcs[i].fn, expr->fn))
125 			return true;
126 	}
127 	return false;
128 }
129 
record_alloc_func(int return_id,char * return_ranges,struct expression * expr)130 static void record_alloc_func(int return_id, char *return_ranges, struct expression *expr)
131 {
132 	if (!is_fresh_alloc(expr))
133 		return;
134 	sql_insert_return_states(return_id, return_ranges, FRESH_ALLOC, -1, "$", "");
135 }
136 
set_unfresh(struct expression * expr)137 static void set_unfresh(struct expression *expr)
138 {
139 	struct sm_state *sm;
140 
141 	sm = get_sm_state_expr(my_id, expr);
142 	if (!sm)
143 		return;
144 	if (!slist_has_state(sm->possible, &fresh))
145 		return;
146 	// TODO call unfresh hooks
147 	set_state_expr(my_id, expr, &undefined);
148 }
149 
match_assign(struct expression * expr)150 static void match_assign(struct expression *expr)
151 {
152 	set_unfresh(expr->right);
153 }
154 
match_call(struct expression * expr)155 static void match_call(struct expression *expr)
156 {
157 	struct expression *arg;
158 
159 	FOR_EACH_PTR(expr->args, arg) {
160 		set_unfresh(arg);
161 	} END_FOR_EACH_PTR(arg);
162 }
163 
164 static struct expression *handled;
set_fresh(struct expression * expr)165 static void set_fresh(struct expression *expr)
166 {
167 	struct range_list *rl;
168 
169 	expr = strip_expr(expr);
170 	if (expr->type != EXPR_SYMBOL)
171 		return;
172 	if (expr == handled)
173 		return;
174 
175 	get_absolute_rl(expr, &rl);
176 	rl = rl_intersection(rl, valid_ptr_rl);
177 	if (!rl)
178 		return;
179 	set_state_expr(my_id, expr, &fresh);
180 	handled = expr;
181 }
182 
returns_fresh_alloc(struct expression * expr,int param,char * key,char * value)183 static void returns_fresh_alloc(struct expression *expr, int param, char *key, char *value)
184 {
185 	if (param != -1 || !key || strcmp(key, "$") != 0)
186 		return;
187 	if (expr->type != EXPR_ASSIGNMENT)
188 		return;
189 
190 	set_fresh(expr->left);
191 }
192 
match_alloc(const char * fn,struct expression * expr,void * _size_arg)193 static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
194 {
195 	set_fresh(expr->left);
196 }
197 
register_fresh_alloc(int id)198 void register_fresh_alloc(int id)
199 {
200 	int i;
201 
202 	my_id = id;
203 
204 	if (option_project == PROJ_KERNEL)
205 		alloc_funcs = kernel_allocation_funcs;
206 	else
207 		alloc_funcs = general_allocation_funcs;
208 
209 	i = -1;
210 	while (alloc_funcs[++i].fn)
211 		add_function_assign_hook(alloc_funcs[i].fn, &match_alloc, 0);
212 
213 	add_split_return_callback(&record_alloc_func);
214 	select_return_states_hook(FRESH_ALLOC, &returns_fresh_alloc);
215 	add_hook(&match_assign, ASSIGNMENT_HOOK);
216 	add_hook(&match_call, FUNCTION_CALL_HOOK);
217 
218 	add_pre_merge_hook(my_id, &pre_merge_hook);
219 }
220