]>
Commit | Line | Data |
---|---|---|
8529e0f7 SM |
1 | // SPDX-License-Identifier: BSD-2-Clause-Patent |
2 | /* | |
3 | * test-mok-mirror.c - try to test our mok mirroring code | |
4 | * Copyright Peter Jones <pjones@redhat.com> | |
5 | */ | |
6 | ||
7 | #include "shim.h" | |
8 | #include "mock-variables.h" | |
9 | #include "test-data-efivars-1.h" | |
10 | ||
11 | #include <stdio.h> | |
12 | ||
13 | #pragma GCC diagnostic ignored "-Wunused-parameter" | |
14 | ||
15 | EFI_STATUS | |
16 | start_image(EFI_HANDLE image_handle UNUSED, CHAR16 *mm) | |
17 | { | |
18 | printf("Attempted to launch %s\n", Str2str(mm)); | |
19 | return EFI_SUCCESS; | |
20 | } | |
21 | ||
22 | #define N_TEST_VAR_OPS 40 | |
23 | struct test_var { | |
24 | EFI_GUID guid; | |
25 | CHAR16 *name; | |
26 | UINT32 attrs; | |
27 | UINTN n_ops; | |
28 | bool must_be_present; | |
29 | bool must_be_absent; | |
30 | mock_variable_op_t ops[N_TEST_VAR_OPS]; | |
31 | EFI_STATUS results[N_TEST_VAR_OPS]; | |
32 | }; | |
33 | ||
34 | static struct test_var *test_vars; | |
35 | ||
36 | struct mock_mok_variable_config_entry { | |
37 | CHAR8 name[256]; | |
38 | UINT64 data_size; | |
39 | const unsigned char *data; | |
40 | }; | |
41 | ||
42 | static void | |
43 | setvar_post(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, | |
44 | UINTN size, VOID *data, EFI_STATUS *status, | |
45 | mock_variable_op_t op, const char * const file, | |
46 | const int line, const char * const func) | |
47 | { | |
48 | if (!test_vars) | |
49 | return; | |
50 | ||
51 | for (UINTN i = 0; test_vars[i].name != NULL; i++) { | |
52 | struct test_var *tv = &test_vars[i]; | |
53 | ||
54 | if (CompareGuid(&tv->guid, guid) != 0 || | |
55 | StrCmp(tv->name, name) != 0) | |
56 | continue; | |
57 | tv->ops[tv->n_ops] = op; | |
58 | tv->results[tv->n_ops] = *status; | |
59 | tv->n_ops += 1; | |
60 | } | |
61 | } | |
62 | ||
63 | static void | |
64 | getvar_post(CHAR16 *name, EFI_GUID *guid, | |
65 | UINT32 *attrs, UINTN *size, | |
66 | VOID *data, EFI_STATUS *status, | |
67 | const char * const file, const int line, const char * func) | |
68 | { | |
69 | if (EFI_ERROR(*status) && | |
70 | (*status != EFI_NOT_FOUND && | |
71 | *status != EFI_BUFFER_TOO_SMALL)) { | |
72 | printf("%s:%d:%s():Getting "GUID_FMT"-%s ", | |
73 | file, line, func, | |
74 | GUID_ARGS(*guid), Str2str(name)); | |
75 | if (attrs) | |
76 | printf("attrs:%s\n", format_var_attrs(*attrs)); | |
77 | else | |
78 | printf("attrs:NULL\n"); | |
79 | printf("failed:%s\n", efi_strerror(*status)); | |
80 | } | |
81 | ||
82 | if (!test_vars) | |
83 | return; | |
84 | ||
85 | for (UINTN i = 0; test_vars[i].name != NULL; i++) { | |
86 | struct test_var *tv = &test_vars[i]; | |
87 | ||
88 | if (CompareGuid(&tv->guid, guid) != 0 || | |
89 | StrCmp(tv->name, name) != 0) | |
90 | continue; | |
91 | tv->ops[tv->n_ops] = GET; | |
92 | tv->results[tv->n_ops] = *status; | |
93 | tv->n_ops += 1; | |
94 | } | |
95 | } | |
96 | ||
97 | static int | |
98 | test_mok_mirror_0(void) | |
99 | { | |
100 | const char *mok_rt_vars[n_mok_state_variables]; | |
101 | EFI_STATUS status; | |
102 | EFI_GUID guid = SHIM_LOCK_GUID; | |
103 | EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; | |
104 | int ret = -1; | |
105 | ||
106 | struct test_var test_mok_mirror_0_vars[] = { | |
107 | {.guid = SHIM_LOCK_GUID, | |
108 | .name = L"MokList", | |
109 | .must_be_present = true, | |
110 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
111 | EFI_VARIABLE_NON_VOLATILE, | |
112 | .ops = { NONE, }, | |
113 | }, | |
114 | {.guid = SHIM_LOCK_GUID, | |
115 | .name = L"MokListRT", | |
116 | .must_be_present = true, | |
117 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
118 | EFI_VARIABLE_RUNTIME_ACCESS, | |
119 | .ops = { NONE, }, | |
120 | }, | |
121 | {.guid = SHIM_LOCK_GUID, | |
122 | .name = L"MokListX", | |
123 | .must_be_present = true, | |
124 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
125 | EFI_VARIABLE_NON_VOLATILE, | |
126 | .ops = { NONE, }, | |
127 | }, | |
128 | {.guid = SHIM_LOCK_GUID, | |
129 | .name = L"MokListXRT", | |
130 | .must_be_present = true, | |
131 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
132 | EFI_VARIABLE_RUNTIME_ACCESS, | |
133 | .ops = { NONE, }, | |
134 | }, | |
135 | {.guid = SHIM_LOCK_GUID, | |
136 | .name = L"SbatLevel", | |
137 | .must_be_present = true, | |
138 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
139 | EFI_VARIABLE_NON_VOLATILE, | |
140 | .ops = { NONE, }, | |
141 | }, | |
142 | {.guid = SHIM_LOCK_GUID, | |
143 | .name = L"SbatLevelRT", | |
144 | .must_be_present = true, | |
145 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
146 | EFI_VARIABLE_RUNTIME_ACCESS, | |
147 | .ops = { NONE, }, | |
148 | }, | |
149 | {.guid = SHIM_LOCK_GUID, | |
150 | .name = L"MokIgnoreDB", | |
151 | .must_be_absent = true, | |
152 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
153 | EFI_VARIABLE_RUNTIME_ACCESS, | |
154 | .ops = { NONE, }, | |
155 | }, | |
156 | {.guid = SHIM_LOCK_GUID, | |
157 | .name = L"MokSBState", | |
158 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
159 | EFI_VARIABLE_NON_VOLATILE, | |
160 | .ops = { NONE, }, | |
161 | }, | |
162 | {.guid = SHIM_LOCK_GUID, | |
163 | .name = L"MokSBStateRT", | |
164 | .must_be_absent = true, | |
165 | .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
166 | EFI_VARIABLE_RUNTIME_ACCESS, | |
167 | .ops = { NONE, }, | |
168 | }, | |
169 | {.guid = { 0, }, | |
170 | .name = NULL, | |
171 | } | |
172 | }; | |
173 | ||
174 | struct mock_mok_variable_config_entry test_mok_config_table[] = { | |
175 | {.name = "MokListRT", | |
176 | .data_size = sizeof(test_data_efivars_1_MokListRT), | |
177 | .data = test_data_efivars_1_MokListRT | |
178 | }, | |
179 | {.name = "MokListXRT", | |
180 | .data_size = sizeof(test_data_efivars_1_MokListXRT), | |
181 | .data = test_data_efivars_1_MokListXRT | |
182 | }, | |
183 | {.name = "SbatLevelRT", | |
184 | .data_size = sizeof(test_data_efivars_1_SbatLevelRT), | |
185 | .data = test_data_efivars_1_SbatLevelRT | |
186 | }, | |
187 | {.name = { 0, }, | |
188 | .data_size = 0, | |
189 | .data = NULL, | |
190 | } | |
191 | }; | |
192 | ||
193 | for (size_t i = 0; i < n_mok_state_variables; i++) { | |
194 | mok_rt_vars[i] = mok_state_variables[i].rtname8; | |
195 | } | |
196 | ||
197 | mock_load_variables("test-data/efivars-1", mok_rt_vars, true); | |
198 | ||
199 | mock_set_variable_post_hook = setvar_post; | |
200 | mock_get_variable_post_hook = getvar_post; | |
201 | test_vars = &test_mok_mirror_0_vars[0]; | |
202 | ||
203 | import_mok_state(NULL); | |
204 | ||
205 | for (size_t i = 0; test_mok_mirror_0_vars[i].name != NULL; i++) { | |
206 | struct test_var *tv = &test_mok_mirror_0_vars[i]; | |
207 | list_t *pos = NULL; | |
208 | bool found = false; | |
209 | char buf[1]; | |
210 | UINTN size = 0; | |
211 | UINT32 attrs = 0; | |
212 | bool present = false; | |
213 | ||
214 | list_for_each(pos, &mock_variables) { | |
215 | struct mock_variable *var; | |
216 | bool deleted; | |
217 | bool created; | |
218 | int gets = 0; | |
219 | ||
220 | var = list_entry(pos, struct mock_variable, list); | |
221 | if (CompareGuid(&tv->guid, &var->guid) != 0 || | |
222 | StrCmp(var->name, tv->name) != 0) | |
223 | continue; | |
224 | found = true; | |
225 | assert_equal_goto(var->attrs, tv->attrs, err, | |
226 | "\"%s\": wrong attrs; got %s expected %s\n", | |
227 | Str2str(tv->name), | |
228 | format_var_attrs(var->attrs), | |
229 | format_var_attrs(tv->attrs)); | |
230 | for (UINTN j = 0; j < N_TEST_VAR_OPS | |
231 | && tv->ops[j] != NONE; j++) { | |
232 | switch (tv->ops[j]) { | |
233 | case NONE: | |
234 | default: | |
235 | break; | |
236 | case CREATE: | |
237 | if (tv->results[j] == EFI_SUCCESS) | |
238 | created = true; | |
239 | break; | |
240 | case DELETE: | |
241 | assert_goto(tv->results[j] != EFI_SUCCESS, err, | |
242 | "Tried to delete absent variable \"%s\"\n", | |
243 | Str2str(tv->name)); | |
244 | assert_goto(created == false, err, | |
245 | "Deleted variable \"%s\" was previously created.\n", | |
246 | Str2str(tv->name)); | |
247 | break; | |
248 | case APPEND: | |
249 | assert_goto(false, err, | |
250 | "No append action should have been tested\n"); | |
251 | break; | |
252 | case REPLACE: | |
253 | assert_goto(false, err, | |
254 | "No replace action should have been tested\n"); | |
255 | break; | |
256 | case GET: | |
257 | if (tv->results[j] == EFI_SUCCESS) | |
258 | gets += 1; | |
259 | break; | |
260 | } | |
261 | } | |
262 | assert_goto(gets == 0 || gets == 1, err, | |
263 | "Variable should not be read %d times.\n", gets); | |
264 | } | |
265 | if (tv->must_be_present) { | |
266 | assert_goto(found == true, err, | |
267 | "variable \"%s\" was not found.\n", | |
268 | Str2str(tv->name)); | |
269 | } | |
270 | ||
271 | if (tv->must_be_absent) { | |
272 | assert_goto(found == false, err, | |
273 | "variable \"%s\" was found.\n", | |
274 | Str2str(tv->name)); | |
275 | } | |
276 | } | |
277 | ||
278 | uint8_t *pos = NULL; | |
279 | for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { | |
280 | EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; | |
281 | ||
282 | if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) | |
283 | continue; | |
284 | ||
285 | pos = (void *)ct->VendorTable; | |
286 | break; | |
287 | } | |
288 | ||
289 | assert_nonzero_goto(pos, err, "%p != 0\n"); | |
290 | ||
291 | size_t i = 0; | |
292 | while (pos) { | |
293 | struct mock_mok_variable_config_entry *mock_entry = | |
294 | &test_mok_config_table[i]; | |
295 | struct mok_variable_config_entry *mok_entry = | |
296 | (struct mok_variable_config_entry *)pos; | |
297 | ||
298 | /* | |
299 | * If the tables are different lengths, this will trigger. | |
300 | */ | |
301 | assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err, | |
302 | "mok.name[0] %ld != test.name[0] %ld\n"); | |
303 | if (mock_entry->name[0] == 0) | |
304 | break; | |
305 | ||
306 | assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n"); | |
307 | assert_zero_goto(strncmp(mok_entry->name, mock_entry->name, | |
308 | sizeof(mock_entry->name)), | |
309 | err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n", | |
310 | mok_entry->name, mock_entry->name); | |
311 | ||
312 | /* | |
313 | * As of 7501b6bb449f ("mok: fix potential buffer overrun in | |
314 | * import_mok_state"), we should not see any mok config | |
315 | * variables with data_size == 0. | |
316 | */ | |
317 | assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n"); | |
318 | ||
319 | assert_equal_goto(mok_entry->data_size, mock_entry->data_size, | |
320 | err, "%ld != %ld\n"); | |
321 | assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data, | |
322 | mok_entry->data_size), | |
323 | err, "%ld != %ld\n"); | |
324 | pos += offsetof(struct mok_variable_config_entry, data) | |
325 | + mok_entry->data_size; | |
326 | i += 1; | |
327 | } | |
328 | ||
329 | ret = 0; | |
330 | err: | |
331 | for (UINTN k = 0; k < n_mok_state_variables; k++) { | |
332 | struct mok_state_variable *v = | |
333 | &mok_state_variables[k]; | |
334 | if (v->data_size && v->data) { | |
335 | free(v->data); | |
336 | v->data = NULL; | |
337 | v->data_size = 0; | |
338 | } | |
339 | } | |
340 | ||
341 | test_vars = NULL; | |
342 | mock_set_variable_post_hook = NULL; | |
343 | mock_get_variable_post_hook = NULL; | |
344 | return ret; | |
345 | } | |
346 | ||
347 | int | |
348 | main(void) | |
349 | { | |
350 | int status = 0; | |
351 | setbuf(stdout, NULL); | |
352 | ||
353 | const char *sort_policy_names[] = { | |
354 | "MOCK_SORT_DESCENDING", | |
355 | "MOCK_SORT_PREPEND", | |
356 | "MOCK_SORT_APPEND", | |
357 | "MOCK_SORT_ASCENDING", | |
358 | "MOCK_SORT_MAX_SENTINEL" | |
359 | }; | |
360 | ||
361 | const char *del_policy_names[] = { | |
362 | "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO", | |
363 | "MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH", | |
364 | "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO|MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH", | |
365 | "MOCK_VAR_DELETE_ATTR_ALLOW_NONE", | |
366 | NULL | |
367 | }; | |
368 | ||
369 | int delete_policies[] = { | |
370 | MOCK_VAR_DELETE_ATTR_ALLOW_ZERO, | |
371 | MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH, | |
372 | MOCK_VAR_DELETE_ATTR_ALLOW_ZERO | |
373 | | MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH, | |
374 | 0 | |
375 | }; | |
376 | ||
377 | for (int i = 0; i < MOCK_SORT_MAX_SENTINEL; i++) { | |
378 | mock_variable_sort_policy = i; | |
379 | mock_config_table_sort_policy = i; | |
380 | int j = 0; | |
381 | ||
382 | printf("%s: setting variable sort policy to %s\n", | |
383 | program_invocation_short_name, sort_policy_names[i]); | |
384 | do { | |
385 | printf("%s: setting delete policy to %s\n", | |
386 | program_invocation_short_name, | |
387 | del_policy_names[j]); | |
388 | ||
389 | mock_variable_delete_attr_policy = delete_policies[j]; | |
390 | test(test_mok_mirror_0); | |
391 | mock_finalize_vars_and_configs(); | |
392 | ||
393 | if (delete_policies[j] == 0) | |
394 | break; | |
395 | } while (++j); | |
396 | } | |
397 | ||
398 | return status; | |
399 | } | |
400 | ||
401 | // vim:fenc=utf-8:tw=75:noet |