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"
43 static const char *returns_pointer_to_host_data
[] = {
52 const sval_t
*implies_start
, *implies_end
;
56 struct host_fn_info func_pointer_table
[] = {
57 /* Functions that return host data as argument 1 */
58 { "memcpy_fromio", HOST_DATA
, 0, "*$" },
59 { "acpi_os_read_iomem", HOST_DATA
, 1, "*$" },
60 { "mmio_insb", HOST_DATA
, 1, "*$" },
61 { "mmio_insw", HOST_DATA
, 1, "*$" },
62 { "mmio_insl", HOST_DATA
, 1, "*$" },
63 { "acpi_read_bit_register", HOST_DATA
, 1, "*$" },
64 /* Functions that return host data as argument 3 */
65 { "__virtio_cread_many", HOST_DATA
, 2, "*$" },
66 { "pci_user_read_config_word", HOST_DATA
, 2, "*$" },
67 { "pci_user_read_config_dword", HOST_DATA
, 2, "*$" },
68 { "pci_user_read_config_byte", HOST_DATA
, 2, "*$" },
69 /* Functions that return host data as argument 4 */
70 { "pci_bus_read_config_byte", HOST_DATA
, 3, "*$" },
71 { "pci_bus_read_config_word", HOST_DATA
, 3, "*$" },
72 { "pci_bus_read_config_dword", HOST_DATA
, 3, "*$" },
73 /* Functions that return host data as argument 6 */
74 { "raw_pci_read", HOST_DATA
, 5, "*$" },
75 /* Functions that return host data as arguments 2-5 */
76 { "cpuid", HOST_DATA
, 1, "*$" },
77 { "cpuid", HOST_DATA
, 2, "*$" },
78 { "cpuid", HOST_DATA
, 3, "*$" },
79 { "cpuid", HOST_DATA
, 4, "*$" },
80 /* Functions that return host data as arguments 3-6 */
81 { "cpuid_count", HOST_DATA
, 2, "*$" },
82 { "cpuid_count", HOST_DATA
, 3, "*$" },
83 { "cpuid_count", HOST_DATA
, 4, "*$" },
84 { "cpuid_count", HOST_DATA
, 5, "*$" },
87 bool is_host_data_fn(struct symbol
*fn
)
91 if (!fn
|| !fn
->ident
)
94 for (i
= 0; i
< ARRAY_SIZE(returns_pointer_to_host_data
); i
++) {
95 if (strcmp(fn
->ident
->name
, returns_pointer_to_host_data
[i
]) == 0) {
96 // func_gets_host_data = true;
103 static bool is_points_to_host_data_fn(struct expression
*expr
)
105 expr
= strip_expr(expr
);
106 if (!expr
|| expr
->type
!= EXPR_CALL
|| expr
->fn
->type
!= EXPR_SYMBOL
||
109 return is_host_data_fn(expr
->fn
->symbol
);
112 static bool is_array_of_host_data(struct expression
*expr
)
114 struct expression
*deref
;
117 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&') {
118 expr
= strip_expr(expr
->unop
);
119 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
120 expr
= strip_expr(expr
->unop
);
123 /* This is for array elements &foo->data[4] */
124 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+') {
125 if (points_to_host_data(expr
->left
))
127 if (points_to_host_data(expr
->right
))
131 /* This is for if you have: foo = skb->data; frob(foo->array); */
132 type
= get_type(expr
);
133 if (!type
|| type
->type
!= SYM_ARRAY
)
136 if (expr
->type
!= EXPR_DEREF
)
138 deref
= strip_expr(expr
->deref
);
139 if (deref
->type
!= EXPR_PREOP
|| deref
->op
!= '*')
141 deref
= strip_expr(deref
->unop
);
142 return points_to_host_data(deref
);
145 bool points_to_host_data(struct expression
*expr
)
152 expr
= strip_expr(expr
);
156 if (is_fake_call(expr
))
159 if (expr
->type
== EXPR_ASSIGNMENT
)
160 return points_to_host_data(expr
->left
);
162 if (is_array_of_host_data(expr
))
165 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
166 expr
= strip_expr(expr
->left
);
168 if (is_points_to_host_data_fn(expr
))
171 sm
= get_sm_state_expr(my_id
, expr
);
172 if (sm
&& slist_has_state(sm
->possible
, &host_data
))
177 void set_points_to_host_data(struct expression
*expr
)
179 struct expression
*tmp
;
181 tmp
= get_assigned_expr(expr
);
183 set_state_expr(my_id
, tmp
, &host_data
);
184 set_state_expr(my_id
, expr
, &host_data
);
187 static void match_assign_host(struct expression
*expr
)
190 if (is_fake_call(expr
->right
))
193 if (!is_ptr_type(get_type(expr
->left
))){
197 if (points_to_host_data(expr
->right
)) {
198 set_points_to_host_data(expr
->left
);
202 if (get_state_expr(my_id
, expr
->left
)){
203 set_state_expr(my_id
, expr
->left
, &undefined
);
207 static void match_memcpy_host(const char *fn
, struct expression
*expr
, void *_unused
)
209 struct expression
*dest
, *src
;
211 dest
= get_argument_from_call_expr(expr
->args
, 0);
212 src
= get_argument_from_call_expr(expr
->args
, 1);
214 if (points_to_host_data(src
)) {
215 set_points_to_host_data(dest
);
219 if (get_state_expr(my_id
, dest
))
220 set_state_expr(my_id
, dest
, &undefined
);
223 static void match_host_function(const char *fn
, struct expression
*expr
, void *_unused
)
225 struct expression
*dest
;
227 for (int i
= 0; i
< ARRAY_SIZE(func_pointer_table
); i
++)
228 if (sym_name_is(func_pointer_table
[i
].name
, expr
->fn
)){
229 dest
= get_argument_from_call_expr(expr
->args
, func_pointer_table
[i
].param
);
230 dest
= strip_expr(dest
);
233 set_state_expr(my_id
, dest
, &host_data
);
237 static void return_info_callback_host(int return_id
, char *return_ranges
,
238 struct expression
*returned_expr
,
240 const char *printed_name
,
243 int type
= HOST_PTR_SET
;
245 if (!slist_has_state(sm
->possible
, &host_data
))
249 if (get_state_stree(get_start_states(), my_id
, sm
->name
, sm
->sym
))
252 if (!param_was_set_var_sym(sm
->name
, sm
->sym
))
255 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
258 sql_insert_return_states(return_id
, return_ranges
, type
,
259 param
, printed_name
, "");
262 static void returns_host_ptr_helper(struct expression
*expr
, int param
, char *key
, char *value
, bool set
)
264 struct expression
*arg
;
265 struct expression
*call
;
270 while (call
->type
== EXPR_ASSIGNMENT
)
271 call
= strip_expr(call
->right
);
272 if (call
->type
!= EXPR_CALL
)
275 if (!set
&& !we_pass_host_data(call
))
279 if (expr
->type
!= EXPR_ASSIGNMENT
) {
280 /* Nothing to do. Fake assignments should handle it */
287 arg
= get_argument_from_call_expr(call
->args
, param
);
291 name
= get_variable_from_key(arg
, key
, &sym
);
294 set_state(my_id
, name
, sym
, &host_data
);
299 static void returns_host_ptr(struct expression
*expr
, int param
, char *key
, char *value
)
301 returns_host_ptr_helper(expr
, param
, key
, value
, false);
304 static void returns_host_ptr_set(struct expression
*expr
, int param
, char *key
, char *value
)
306 returns_host_ptr_helper(expr
, param
, key
, value
, true);
309 static void set_param_host_ptr(const char *name
, struct symbol
*sym
, char *key
, char *value
)
311 struct expression
*expr
;
314 expr
= symbol_expression(sym
);
315 fullname
= get_variable_from_key(expr
, key
, NULL
);
318 set_state(my_id
, fullname
, sym
, &host_data
);
321 static void caller_info_callback_host(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
324 if (!slist_has_state(sm
->possible
, &host_data
))
326 sql_insert_caller_info(call
, HOST_PTR
, param
, printed_name
, "");
329 void register_points_to_host_data(int id
)
333 if (option_project
!= PROJ_KERNEL
)
336 add_hook(&match_assign_host
, ASSIGNMENT_HOOK
);
338 for (int i
= 0; i
< ARRAY_SIZE(func_pointer_table
); i
++)
339 add_function_hook(func_pointer_table
[i
].name
, &match_host_function
, NULL
);
341 add_function_hook("memcpy", &match_memcpy_host
, NULL
);
342 add_function_hook("__memcpy", &match_memcpy_host
, NULL
);
344 add_caller_info_callback(my_id
, caller_info_callback_host
);
345 add_return_info_callback(my_id
, return_info_callback_host
);
347 select_caller_info_hook(set_param_host_ptr
, HOST_PTR
);
348 select_return_states_hook(HOST_PTR
, &returns_host_ptr
);
349 select_return_states_hook(HOST_PTR_SET
, &returns_host_ptr_set
);