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 static bool is_points_to_host_data_fn(struct expression
*expr
)
106 expr
= strip_expr(expr
);
107 if (!expr
|| expr
->type
!= EXPR_CALL
|| expr
->fn
->type
!= EXPR_SYMBOL
||
110 return is_host_data_fn(expr
->fn
->symbol
);
113 static bool is_array_of_host_data(struct expression
*expr
)
115 struct expression
*deref
;
118 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&') {
119 expr
= strip_expr(expr
->unop
);
120 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
121 expr
= strip_expr(expr
->unop
);
124 /* This is for array elements &foo->data[4] */
125 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+') {
126 if (points_to_host_data(expr
->left
))
128 if (points_to_host_data(expr
->right
))
132 /* This is for if you have: foo = skb->data; frob(foo->array); */
133 type
= get_type(expr
);
134 if (!type
|| type
->type
!= SYM_ARRAY
)
137 if (expr
->type
!= EXPR_DEREF
)
139 deref
= strip_expr(expr
->deref
);
140 if (deref
->type
!= EXPR_PREOP
|| deref
->op
!= '*')
142 deref
= strip_expr(deref
->unop
);
143 return points_to_host_data(deref
);
146 bool points_to_host_data(struct expression
*expr
)
153 expr
= strip_expr(expr
);
157 if (is_fake_call(expr
))
160 if (expr
->type
== EXPR_ASSIGNMENT
)
161 return points_to_host_data(expr
->left
);
163 if (is_array_of_host_data(expr
))
166 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
167 expr
= strip_expr(expr
->left
);
169 if (is_points_to_host_data_fn(expr
))
172 sm
= get_sm_state_expr(my_id
, expr
);
175 if (slist_has_state(sm
->possible
, &host_data
) ||
176 slist_has_state(sm
->possible
, &host_data_set
))
181 void set_points_to_host_data(struct expression
*expr
, bool is_new
)
183 struct expression
*tmp
;
185 tmp
= get_assigned_expr(expr
);
187 set_state_expr(my_id
, tmp
, is_new
? &host_data_set
: &host_data
);
188 set_state_expr(my_id
, expr
, is_new
? &host_data_set
: &host_data
);
191 static void match_assign_host(struct expression
*expr
)
194 if (is_fake_call(expr
->right
))
197 if (!is_ptr_type(get_type(expr
->left
))){
201 if (points_to_host_data(expr
->right
)) {
202 set_points_to_host_data(expr
->left
, false);
206 if (get_state_expr(my_id
, expr
->left
)){
207 set_state_expr(my_id
, expr
->left
, &undefined
);
211 static void match_memcpy_host(const char *fn
, struct expression
*expr
, void *_unused
)
213 struct expression
*dest
, *src
;
215 dest
= get_argument_from_call_expr(expr
->args
, 0);
216 src
= get_argument_from_call_expr(expr
->args
, 1);
218 if (points_to_host_data(src
)) {
219 set_points_to_host_data(dest
, false);
223 if (get_state_expr(my_id
, dest
))
224 set_state_expr(my_id
, dest
, &undefined
);
227 static void match_host_function(const char *fn
, struct expression
*expr
, void *_unused
)
229 struct expression
*dest
;
231 for (int i
= 0; i
< ARRAY_SIZE(func_pointer_table
); i
++)
232 if (sym_name_is(func_pointer_table
[i
].name
, expr
->fn
)){
233 dest
= get_argument_from_call_expr(expr
->args
, func_pointer_table
[i
].param
);
234 dest
= strip_expr(dest
);
237 set_state_expr(my_id
, dest
, &host_data
);
241 static void return_info_callback_host(int return_id
, char *return_ranges
,
242 struct expression
*returned_expr
,
244 const char *printed_name
,
249 if (strncmp(printed_name
, "&$", 2) == 0)
252 if (!slist_has_state(sm
->possible
, &host_data
))
256 if (!slist_has_state(sm
->possible
, &host_data_set
))
260 if (slist_has_state(sm
->possible
, &host_data_set
))
262 else if (slist_has_state(sm
->possible
, &host_data
))
267 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
270 sql_insert_return_states(return_id
, return_ranges
, type
,
271 param
, printed_name
, "");
274 static void returns_host_ptr_helper(struct expression
*expr
, int param
, char *key
, char *value
, bool set
)
276 struct expression
*arg
;
277 struct expression
*call
;
282 while (call
->type
== EXPR_ASSIGNMENT
)
283 call
= strip_expr(call
->right
);
284 if (call
->type
!= EXPR_CALL
)
287 if (!set
&& !we_pass_host_data(call
))
291 if (expr
->type
!= EXPR_ASSIGNMENT
) {
292 /* Nothing to do. Fake assignments should handle it */
299 arg
= get_argument_from_call_expr(call
->args
, param
);
303 name
= get_variable_from_key(arg
, key
, &sym
);
307 set_state(my_id
, name
, sym
, &host_data_set
);
309 set_state(my_id
, name
, sym
, &host_data
);
314 static void returns_host_ptr(struct expression
*expr
, int param
, char *key
, char *value
)
316 returns_host_ptr_helper(expr
, param
, key
, value
, false);
319 static void returns_host_ptr_set(struct expression
*expr
, int param
, char *key
, char *value
)
321 returns_host_ptr_helper(expr
, param
, key
, value
, true);
324 static void set_param_host_ptr(const char *name
, struct symbol
*sym
, char *key
, char *value
)
326 struct expression
*expr
;
329 expr
= symbol_expression(sym
);
330 fullname
= get_variable_from_key(expr
, key
, NULL
);
333 set_state(my_id
, fullname
, sym
, &host_data
);
336 static void caller_info_callback_host(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
338 if (!slist_has_state(sm
->possible
, &host_data
) &&
339 !slist_has_state(sm
->possible
, &host_data_set
))
341 sql_insert_caller_info(call
, HOST_PTR
, param
, printed_name
, "");
344 void register_points_to_host_data(int id
)
348 if (option_project
!= PROJ_KERNEL
)
351 add_hook(&match_assign_host
, ASSIGNMENT_HOOK
);
353 for (int i
= 0; i
< ARRAY_SIZE(func_pointer_table
); i
++)
354 add_function_hook(func_pointer_table
[i
].name
, &match_host_function
, NULL
);
356 add_function_hook("memcpy", &match_memcpy_host
, NULL
);
357 add_function_hook("__memcpy", &match_memcpy_host
, NULL
);
359 add_caller_info_callback(my_id
, caller_info_callback_host
);
360 add_return_info_callback(my_id
, return_info_callback_host
);
362 select_caller_info_hook(set_param_host_ptr
, HOST_PTR
);
363 select_return_states_hook(HOST_PTR
, &returns_host_ptr
);
364 select_return_states_hook(HOST_PTR_SET
, &returns_host_ptr_set
);