kchecker: Fix --outfile handling
[smatch.git] / smatch_points_to_host_data.c
blob494652e1a6973819eee566c8b2794723f7e50422
1 /*
2 * Copyright (C) 2020 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
18 /* The below code is an adjustment of the smatch_points_to_user_data.c to
19 * track (untrusted and potentially malicious) data pointers received by a
20 * guest kernel via various low-level port IO, MMIO, MSR, CPUID and PCI
21 * config space access functions (see func_table for a full list) from an
22 * untrusted host/VMM under confidential computing threat model
23 * (where a guest VM does not trust the host/VMM).
24 * We call this data as 'host' data here. Being able to track host data
25 * allows creating new smatch patterns that can perform various checks
26 * on such data, i.e. verify that that there are no spectre v1 gadgets
27 * on this attack surface or even simply report locations where host data
28 * is being processed in the kernel source code (useful for a targeted code
29 * audit).
30 * Similar as smatch_points_to_user_data.c it works with
31 * smatch_kernel_host_data.c code.
33 * Copyright (C) 2022 Elena Reshetova Intel Corporation.
36 #include "smatch.h"
37 #include "smatch_slist.h"
38 #include "smatch_extra.h"
40 static int my_id;
41 STATE(host_data);
42 STATE(host_data_set);
44 static const char *returns_pointer_to_host_data[] = {
45 "virtqueue_get_buf"
48 struct host_fn_info {
49 const char *name;
50 int type;
51 int param;
52 const char *key;
53 const sval_t *implies_start, *implies_end;
54 func_hook *call_back;
57 struct host_fn_info func_pointer_table[] = {
58 /* Functions that return host data as argument 1 */
59 { "memcpy_fromio", HOST_DATA, 0, "*$" },
60 { "acpi_os_read_iomem", HOST_DATA, 1, "*$" },
61 { "mmio_insb", HOST_DATA, 1, "*$" },
62 { "mmio_insw", HOST_DATA, 1, "*$" },
63 { "mmio_insl", HOST_DATA, 1, "*$" },
64 { "acpi_read_bit_register", HOST_DATA, 1, "*$" },
65 /* Functions that return host data as argument 3 */
66 { "__virtio_cread_many", HOST_DATA, 2, "*$" },
67 { "pci_user_read_config_word", HOST_DATA, 2, "*$" },
68 { "pci_user_read_config_dword", HOST_DATA, 2, "*$" },
69 { "pci_user_read_config_byte", HOST_DATA, 2, "*$" },
70 /* Functions that return host data as argument 4 */
71 { "pci_bus_read_config_byte", HOST_DATA, 3, "*$" },
72 { "pci_bus_read_config_word", HOST_DATA, 3, "*$" },
73 { "pci_bus_read_config_dword", HOST_DATA, 3, "*$" },
74 /* Functions that return host data as argument 6 */
75 { "raw_pci_read", HOST_DATA, 5, "*$" },
76 /* Functions that return host data as arguments 2-5 */
77 { "cpuid", HOST_DATA, 1, "*$" },
78 { "cpuid", HOST_DATA, 2, "*$" },
79 { "cpuid", HOST_DATA, 3, "*$" },
80 { "cpuid", HOST_DATA, 4, "*$" },
81 /* Functions that return host data as arguments 3-6 */
82 { "cpuid_count", HOST_DATA, 2, "*$" },
83 { "cpuid_count", HOST_DATA, 3, "*$" },
84 { "cpuid_count", HOST_DATA, 4, "*$" },
85 { "cpuid_count", HOST_DATA, 5, "*$" },
88 bool is_host_data_fn(struct symbol *fn)
90 int i;
92 if (!fn || !fn->ident)
93 return false;
95 for (i = 0; i < ARRAY_SIZE(returns_pointer_to_host_data); i++) {
96 if (strcmp(fn->ident->name, returns_pointer_to_host_data[i]) == 0) {
97 // func_gets_host_data = true;
98 return true;
101 return false;
104 bool is_fn_points_to_host_data(const char *fn)
106 int i;
108 if (!fn)
109 return false;
111 for (i = 0; i < ARRAY_SIZE(returns_pointer_to_host_data); i++) {
112 if (strcmp(fn, returns_pointer_to_host_data[i]) == 0) {
113 return true;
116 return false;
119 static bool is_points_to_host_data_fn(struct expression *expr)
121 expr = strip_expr(expr);
122 if (!expr || expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL ||
123 !expr->fn->symbol)
124 return false;
125 return is_host_data_fn(expr->fn->symbol);
128 static bool is_array_of_host_data(struct expression *expr)
130 struct expression *deref;
131 struct symbol *type;
133 if (expr->type == EXPR_PREOP && expr->op == '&') {
134 expr = strip_expr(expr->unop);
135 if (expr->type == EXPR_PREOP && expr->op == '*')
136 expr = strip_expr(expr->unop);
139 /* This is for array elements &foo->data[4] */
140 if (expr->type == EXPR_BINOP && expr->op == '+') {
141 if (points_to_host_data(expr->left))
142 return true;
143 if (points_to_host_data(expr->right))
144 return true;
147 /* This is for if you have: foo = skb->data; frob(foo->array); */
148 type = get_type(expr);
149 if (!type || type->type != SYM_ARRAY)
150 return false;
152 if (expr->type != EXPR_DEREF)
153 return false;
154 deref = strip_expr(expr->deref);
155 if (deref->type != EXPR_PREOP || deref->op != '*')
156 return false;
157 deref = strip_expr(deref->unop);
158 return points_to_host_data(deref);
161 bool points_to_host_data(struct expression *expr)
163 struct sm_state *sm;
165 if (!expr)
166 return false;
168 expr = strip_expr(expr);
169 if (!expr)
170 return false;
172 if (is_fake_call(expr))
173 return false;
175 if (expr->type == EXPR_ASSIGNMENT)
176 return points_to_host_data(expr->left);
178 if (is_array_of_host_data(expr))
179 return true;
181 if (expr->type == EXPR_BINOP && expr->op == '+')
182 expr = strip_expr(expr->left);
184 if (is_points_to_host_data_fn(expr))
185 return true;
187 sm = get_sm_state_expr(my_id, expr);
188 if (!sm)
189 return false;
190 if (slist_has_state(sm->possible, &host_data) ||
191 slist_has_state(sm->possible, &host_data_set))
192 return true;
193 return false;
196 void set_points_to_host_data(struct expression *expr, bool is_new)
198 struct expression *tmp;
200 tmp = get_assigned_expr(expr);
201 if (tmp)
202 set_state_expr(my_id, tmp, is_new ? &host_data_set : &host_data);
203 set_state_expr(my_id, expr, is_new ? &host_data_set : &host_data);
206 static void match_assign_host(struct expression *expr)
209 if (is_fake_call(expr->right))
210 return;
212 if (!is_ptr_type(get_type(expr->left))){
213 return;
216 if (points_to_host_data(expr->right)) {
217 set_points_to_host_data(expr->left, false);
218 return;
221 if (get_state_expr(my_id, expr->left)){
222 set_state_expr(my_id, expr->left, &undefined);
226 static void match_memcpy_host(const char *fn, struct expression *expr, void *_unused)
228 struct expression *dest, *src;
230 dest = get_argument_from_call_expr(expr->args, 0);
231 src = get_argument_from_call_expr(expr->args, 1);
233 if (points_to_host_data(src)) {
234 set_points_to_host_data(dest, false);
235 return;
238 if (get_state_expr(my_id, dest))
239 set_state_expr(my_id, dest, &undefined);
242 static void match_host_function(const char *fn, struct expression *expr, void *_unused)
244 struct expression *dest;
246 for (int i = 0; i < ARRAY_SIZE(func_pointer_table); i++)
247 if (sym_name_is(func_pointer_table[i].name, expr->fn)){
248 dest = get_argument_from_call_expr(expr->args, func_pointer_table[i].param);
249 dest = strip_expr(dest);
250 if (!dest)
251 return;
252 set_state_expr(my_id, dest, &host_data);
256 static void return_info_callback_host(int return_id, char *return_ranges,
257 struct expression *returned_expr,
258 int param,
259 const char *printed_name,
260 struct sm_state *sm)
262 int type;
264 if (strncmp(printed_name, "&$", 2) == 0)
265 return;
267 if (param >= 0) {
268 if (!slist_has_state(sm->possible, &host_data_set))
269 return;
270 type = HOST_PTR_SET;
271 } else {
272 if (slist_has_state(sm->possible, &host_data_set))
273 type = HOST_PTR_SET;
274 else if (slist_has_state(sm->possible, &host_data))
275 type = HOST_PTR;
276 else
277 return;
279 if (parent_is_gone_var_sym(sm->name, sm->sym))
280 return;
282 sql_insert_return_states(return_id, return_ranges, type,
283 param, printed_name, "");
286 static void returns_host_ptr_helper(struct expression *expr, int param, char *key, char *value, bool set)
288 struct expression *arg;
289 struct expression *call;
290 char *name;
291 struct symbol *sym;
293 call = expr;
294 while (call->type == EXPR_ASSIGNMENT)
295 call = strip_expr(call->right);
296 if (call->type != EXPR_CALL)
297 return;
299 if (!set && !we_pass_host_data(call))
300 return;
302 if (param == -1) {
303 if (expr->type != EXPR_ASSIGNMENT) {
304 /* Nothing to do. Fake assignments should handle it */
305 return;
307 arg = expr->left;
308 goto set_host;
311 arg = get_argument_from_call_expr(call->args, param);
312 if (!arg)
313 return;
314 set_host:
315 name = get_variable_from_key(arg, key, &sym);
316 if (!name || !sym)
317 goto free;
318 if (set)
319 set_state(my_id, name, sym, &host_data_set);
320 else
321 set_state(my_id, name, sym, &host_data);
322 free:
323 free_string(name);
326 static void returns_host_ptr(struct expression *expr, int param, char *key, char *value)
328 returns_host_ptr_helper(expr, param, key, value, false);
331 static void returns_host_ptr_set(struct expression *expr, int param, char *key, char *value)
333 returns_host_ptr_helper(expr, param, key, value, true);
336 static void set_param_host_ptr(const char *name, struct symbol *sym, char *key, char *value)
338 struct expression *expr;
339 char *fullname;
341 expr = symbol_expression(sym);
342 fullname = get_variable_from_key(expr, key, NULL);
343 if (!fullname)
344 return;
345 set_state(my_id, fullname, sym, &host_data);
348 static void caller_info_callback_host(struct expression *call, int param, char *printed_name, struct sm_state *sm)
350 if (!slist_has_state(sm->possible, &host_data) &&
351 !slist_has_state(sm->possible, &host_data_set))
352 return;
353 sql_insert_caller_info(call, HOST_PTR, param, printed_name, "");
356 void register_points_to_host_data(int id)
358 my_id = id;
360 if (option_project != PROJ_KERNEL)
361 return;
363 add_hook(&match_assign_host, ASSIGNMENT_HOOK);
365 for (int i = 0; i < ARRAY_SIZE(func_pointer_table); i++)
366 add_function_hook(func_pointer_table[i].name, &match_host_function, NULL);
368 add_function_hook("memcpy", &match_memcpy_host, NULL);
369 add_function_hook("__memcpy", &match_memcpy_host, NULL);
371 add_caller_info_callback(my_id, caller_info_callback_host);
372 add_return_info_callback(my_id, return_info_callback_host);
374 select_caller_info_hook(set_param_host_ptr, HOST_PTR);
375 select_return_states_hook(HOST_PTR, &returns_host_ptr);
376 select_return_states_hook(HOST_PTR_SET, &returns_host_ptr_set);