kernel: handle scoped_guard(spinlock_irq ...)
[smatch.git] / smatch_fresh_alloc.c
bloba918a3d06e5dd0df22e1457aeaaf4057eef928b7
1 /*
2 * Copyright (C) 2019 Oracle.
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.
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.
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
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.
25 #include "smatch.h"
26 #include "smatch_extra.h"
27 #include "smatch_slist.h"
29 static int my_id;
31 STATE(fresh);
33 struct alloc_info *alloc_funcs;
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 {"memdup_user", 1},
48 {"dma_alloc_attrs", 1},
49 {"dma_alloc_coherent", 1},
50 {"devm_kmalloc", 1},
51 {"devm_kzalloc", 1},
52 {"krealloc", 1},
53 {"__alloc_bootmem", 0},
54 {"alloc_bootmem", 0},
55 {"dma_alloc_contiguous", 1},
56 {"dma_alloc_coherent", 1},
57 {},
60 struct alloc_info general_allocation_funcs[] = {
61 {"malloc", 0},
62 {"calloc", 0, 1},
63 {"memdup", 1},
64 {"realloc", 1},
65 {},
68 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
70 struct smatch_state *state;
71 sval_t sval;
73 state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
74 if (estate_get_single_value(state, &sval) && sval.value == 0)
75 set_state(my_id, cur->name, cur->sym, &undefined);
78 static int fresh_callback(void *fresh, int argc, char **argv, char **azColName)
80 *(int *)fresh = 1;
81 return 0;
84 static int fresh_from_db(struct expression *call)
86 static struct expression *prev_call;
87 static int prev_ret;
88 int fresh = 0;
90 if (is_fake_call(call))
91 return 0;
93 if (call == prev_call)
94 return prev_ret;
95 prev_call = call;
97 /* for function pointers assume everything is used */
98 if (call->fn->type != EXPR_SYMBOL) {
99 prev_ret = 0;
100 return 0;
103 run_sql(&fresh_callback, &fresh,
104 "select * from return_states where %s and type = %d and parameter = -1 and key = '$' limit 1;",
105 get_static_filter(call->fn->symbol), FRESH_ALLOC);
107 prev_ret = fresh;
108 return fresh;
111 bool is_fresh_alloc_var_sym(const char *var, struct symbol *sym)
113 return get_state(my_id, var, sym) == &fresh;
116 bool is_fresh_alloc(struct expression *expr)
118 sval_t sval;
119 int i;
121 if (!expr)
122 return false;
124 if (get_implied_value_fast(expr, &sval) && sval.value == 0)
125 return false;
127 if (get_state_expr(my_id, expr) == &fresh)
128 return true;
130 if (expr->type != EXPR_CALL)
131 return false;
132 if (fresh_from_db(expr))
133 return true;
134 i = -1;
135 while (alloc_funcs[++i].fn) {
136 if (sym_name_is(kernel_allocation_funcs[i].fn, expr->fn))
137 return true;
139 return false;
142 static void record_alloc_func(int return_id, char *return_ranges, struct expression *expr)
144 if (!is_fresh_alloc(expr))
145 return;
146 sql_insert_return_states(return_id, return_ranges, FRESH_ALLOC, -1, "$", "");
149 static void set_unfresh(struct expression *expr)
151 struct sm_state *sm;
153 sm = get_sm_state_expr(my_id, expr);
154 if (!sm)
155 return;
156 if (!slist_has_state(sm->possible, &fresh))
157 return;
158 // TODO call unfresh hooks
159 set_state_expr(my_id, expr, &undefined);
162 static void match_assign(struct expression *expr)
164 set_unfresh(expr->right);
167 static void match_call(struct expression *expr)
169 struct expression *arg;
171 FOR_EACH_PTR(expr->args, arg) {
172 set_unfresh(arg);
173 } END_FOR_EACH_PTR(arg);
176 static struct expression *handled;
177 static void set_fresh(struct expression *expr)
179 struct range_list *rl;
181 expr = strip_expr(expr);
182 if (expr->type != EXPR_SYMBOL)
183 return;
184 if (expr == handled)
185 return;
187 get_absolute_rl(expr, &rl);
188 rl = rl_intersection(rl, valid_ptr_rl);
189 if (!rl)
190 return;
191 set_state_expr(my_id, expr, &fresh);
192 handled = expr;
195 static void returns_fresh_alloc(struct expression *expr, int param, char *key, char *value)
197 if (param != -1 || !key || strcmp(key, "$") != 0)
198 return;
199 if (expr->type != EXPR_ASSIGNMENT)
200 return;
202 set_fresh(expr->left);
205 static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
207 set_fresh(expr->left);
210 void register_fresh_alloc(int id)
212 int i;
214 my_id = id;
216 if (option_project == PROJ_KERNEL)
217 alloc_funcs = kernel_allocation_funcs;
218 else
219 alloc_funcs = general_allocation_funcs;
221 i = -1;
222 while (alloc_funcs[++i].fn)
223 add_function_assign_hook(alloc_funcs[i].fn, &match_alloc, 0);
225 add_split_return_callback(&record_alloc_func);
226 select_return_states_hook(FRESH_ALLOC, &returns_fresh_alloc);
227 add_hook(&match_assign, ASSIGNMENT_HOOK);
228 add_hook(&match_call, FUNCTION_CALL_HOOK);
230 add_pre_merge_hook(my_id, &pre_merge_hook);