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
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.
37 #include "smatch_slist.h"
38 #include "smatch_extra.h"
44 static const char *returns_pointer_to_host_data
[] = {
53 const sval_t
*implies_start
, *implies_end
;
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
)
92 if (!fn
|| !fn
->ident
)
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;
104 bool is_fn_points_to_host_data(const char *fn
)
111 for (i
= 0; i
< ARRAY_SIZE(returns_pointer_to_host_data
); i
++) {
112 if (strcmp(fn
, returns_pointer_to_host_data
[i
]) == 0) {
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
||
125 return is_host_data_fn(expr
->fn
->symbol
);
128 static bool is_array_of_host_data(struct expression
*expr
)
130 struct expression
*deref
;
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
))
143 if (points_to_host_data(expr
->right
))
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
)
152 if (expr
->type
!= EXPR_DEREF
)
154 deref
= strip_expr(expr
->deref
);
155 if (deref
->type
!= EXPR_PREOP
|| deref
->op
!= '*')
157 deref
= strip_expr(deref
->unop
);
158 return points_to_host_data(deref
);
161 bool points_to_host_data(struct expression
*expr
)
168 expr
= strip_expr(expr
);
172 if (is_fake_call(expr
))
175 if (expr
->type
== EXPR_ASSIGNMENT
)
176 return points_to_host_data(expr
->left
);
178 if (is_array_of_host_data(expr
))
181 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
182 expr
= strip_expr(expr
->left
);
184 if (is_points_to_host_data_fn(expr
))
187 sm
= get_sm_state_expr(my_id
, expr
);
190 if (slist_has_state(sm
->possible
, &host_data
) ||
191 slist_has_state(sm
->possible
, &host_data_set
))
196 void set_points_to_host_data(struct expression
*expr
, bool is_new
)
198 struct expression
*tmp
;
200 tmp
= get_assigned_expr(expr
);
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
))
212 if (!is_ptr_type(get_type(expr
->left
))){
216 if (points_to_host_data(expr
->right
)) {
217 set_points_to_host_data(expr
->left
, false);
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);
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
);
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
,
259 const char *printed_name
,
264 if (strncmp(printed_name
, "&$", 2) == 0)
268 if (!slist_has_state(sm
->possible
, &host_data_set
))
272 if (slist_has_state(sm
->possible
, &host_data_set
))
274 else if (slist_has_state(sm
->possible
, &host_data
))
279 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
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
;
294 while (call
->type
== EXPR_ASSIGNMENT
)
295 call
= strip_expr(call
->right
);
296 if (call
->type
!= EXPR_CALL
)
299 if (!set
&& !we_pass_host_data(call
))
303 if (expr
->type
!= EXPR_ASSIGNMENT
) {
304 /* Nothing to do. Fake assignments should handle it */
311 arg
= get_argument_from_call_expr(call
->args
, param
);
315 name
= get_variable_from_key(arg
, key
, &sym
);
319 set_state(my_id
, name
, sym
, &host_data_set
);
321 set_state(my_id
, name
, sym
, &host_data
);
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
;
341 expr
= symbol_expression(sym
);
342 fullname
= get_variable_from_key(expr
, key
, NULL
);
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
))
353 sql_insert_caller_info(call
, HOST_PTR
, param
, printed_name
, "");
356 void register_points_to_host_data(int id
)
360 if (option_project
!= PROJ_KERNEL
)
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
);