summaryrefslogtreecommitdiff
path: root/test-mok-mirror.c
diff options
context:
space:
mode:
authorPeter Jones <pjones@redhat.com>2021-07-23 14:36:23 -0400
committerPeter Jones <pjones@redhat.com>2021-09-07 17:05:04 -0400
commit397f820b8c091ca5e3023b6dbd2f26fb256a19f0 (patch)
tree2c66ee7be8ed85edc6a4e9206127783ad8e35386 /test-mok-mirror.c
parent63a5ae1f7c9383f43e4431316eb0c77bcb079b98 (diff)
downloadefi-boot-shim-397f820b8c091ca5e3023b6dbd2f26fb256a19f0.tar.gz
efi-boot-shim-397f820b8c091ca5e3023b6dbd2f26fb256a19f0.zip
tests: Add a unit test for mok mirroring
Test that our mok mirroring doesn't ever try to delete any variable that it has previously created, and that it properly mirrors at least MokList, MokListX, and SbatLevel, at least when variables actually work. These tests will fail (rather a lot) without 7f64fd6da9458b73c4. Currently valgrind shows a memory leak in this code which is not introduced in this patch series. Since all of our memory is freed on Exit() or when kernel does ExitBootServices(), this doesn't have any significant repercussions. Signed-off-by: Peter Jones <pjones@redhat.com>
Diffstat (limited to 'test-mok-mirror.c')
-rw-r--r--test-mok-mirror.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/test-mok-mirror.c b/test-mok-mirror.c
new file mode 100644
index 00000000..d7829843
--- /dev/null
+++ b/test-mok-mirror.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * test-mok-mirror.c - try to test our mok mirroring code
+ * Copyright Peter Jones <pjones@redhat.com>
+ */
+
+#include "shim.h"
+#include "mock-variables.h"
+#include "test-data-efivars-1.h"
+
+#include <stdio.h>
+
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+EFI_STATUS
+start_image(EFI_HANDLE image_handle UNUSED, CHAR16 *mm)
+{
+ printf("Attempted to launch %s\n", Str2str(mm));
+ return EFI_SUCCESS;
+}
+
+#define N_TEST_VAR_OPS 40
+struct test_var {
+ EFI_GUID guid;
+ CHAR16 *name;
+ UINT32 attrs;
+ UINTN n_ops;
+ bool must_be_present;
+ bool must_be_absent;
+ mock_variable_op_t ops[N_TEST_VAR_OPS];
+ EFI_STATUS results[N_TEST_VAR_OPS];
+};
+
+static struct test_var *test_vars;
+
+struct mock_mok_variable_config_entry {
+ CHAR8 name[256];
+ UINT64 data_size;
+ const unsigned char *data;
+};
+
+static void
+setvar_post(CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
+ UINTN size, VOID *data, EFI_STATUS *status,
+ mock_variable_op_t op, const char * const file,
+ const int line, const char * const func)
+{
+ if (!test_vars)
+ return;
+
+ for (UINTN i = 0; test_vars[i].name != NULL; i++) {
+ struct test_var *tv = &test_vars[i];
+
+ if (CompareGuid(&tv->guid, guid) != 0 ||
+ StrCmp(tv->name, name) != 0)
+ continue;
+ tv->ops[tv->n_ops] = op;
+ tv->results[tv->n_ops] = *status;
+ tv->n_ops += 1;
+ }
+}
+
+static void
+getvar_post(CHAR16 *name, EFI_GUID *guid,
+ UINT32 *attrs, UINTN *size,
+ VOID *data, EFI_STATUS *status,
+ const char * const file, const int line, const char * func)
+{
+ if (EFI_ERROR(*status) &&
+ (*status != EFI_NOT_FOUND &&
+ *status != EFI_BUFFER_TOO_SMALL)) {
+ printf("%s:%d:%s():Getting "GUID_FMT"-%s ",
+ file, line, func,
+ GUID_ARGS(*guid), Str2str(name));
+ if (attrs)
+ printf("attrs:%s\n", format_var_attrs(*attrs));
+ else
+ printf("attrs:NULL\n");
+ printf("failed:%s\n", efi_strerror(*status));
+ }
+}
+
+static int
+test_mok_mirror_0(void)
+{
+ const char *mok_rt_vars[n_mok_state_variables];
+ EFI_STATUS status;
+ EFI_GUID guid = SHIM_LOCK_GUID;
+ EFI_GUID mok_config_guid = MOK_VARIABLE_STORE;
+ int ret = -1;
+
+ struct test_var test_mok_mirror_0_vars[] = {
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokList",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokListRT",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokListX",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokListXRT",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"SbatLevel",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"SbatLevelRT",
+ .must_be_present = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokIgnoreDB",
+ .must_be_absent = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokSBState",
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .ops = { NONE, },
+ },
+ {.guid = SHIM_LOCK_GUID,
+ .name = L"MokSBStateRT",
+ .must_be_absent = true,
+ .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ .ops = { NONE, },
+ },
+ {.guid = { 0, },
+ .name = NULL,
+ }
+ };
+
+ struct mock_mok_variable_config_entry test_mok_config_table[] = {
+ {.name = "MokListRT",
+ .data_size = sizeof(test_data_efivars_1_MokListRT),
+ .data = test_data_efivars_1_MokListRT
+ },
+ {.name = "MokListXRT",
+ .data_size = sizeof(test_data_efivars_1_MokListXRT),
+ .data = test_data_efivars_1_MokListXRT
+ },
+ {.name = "SbatLevelRT",
+ .data_size = sizeof(test_data_efivars_1_SbatLevelRT),
+ .data = test_data_efivars_1_SbatLevelRT
+ },
+ {.name = { 0, },
+ .data_size = 0,
+ .data = NULL,
+ }
+ };
+
+ for (size_t i = 0; i < n_mok_state_variables; i++) {
+ mok_rt_vars[i] = mok_state_variables[i].rtname8;
+ }
+
+ mock_load_variables("test-data/efivars-1", mok_rt_vars, true);
+
+ mock_set_variable_post_hook = setvar_post;
+ mock_get_variable_post_hook = getvar_post;
+ test_vars = &test_mok_mirror_0_vars[0];
+
+ import_mok_state(NULL);
+
+ for (size_t i = 0; test_mok_mirror_0_vars[i].name != NULL; i++) {
+ struct test_var *tv = &test_mok_mirror_0_vars[i];
+ list_t *pos = NULL;
+ bool found = false;
+ char buf[1];
+ UINTN size = 0;
+ UINT32 attrs = 0;
+ bool present = false;
+
+ list_for_each(pos, &mock_variables) {
+ struct mock_variable *var;
+ bool deleted;
+ bool created;
+
+ var = list_entry(pos, struct mock_variable, list);
+ if (CompareGuid(&tv->guid, &var->guid) != 0 ||
+ StrCmp(var->name, tv->name) != 0)
+ continue;
+ found = true;
+ assert_equal_goto(var->attrs, tv->attrs, err,
+ "\"%s\": wrong attrs; got %s expected %s\n",
+ Str2str(tv->name),
+ format_var_attrs(var->attrs),
+ format_var_attrs(tv->attrs));
+ for (UINTN j = 0; j < N_TEST_VAR_OPS
+ && tv->ops[j] != NONE; j++) {
+ switch (tv->ops[j]) {
+ case NONE:
+ default:
+ break;
+ case CREATE:
+ if (tv->results[j] == EFI_SUCCESS)
+ created = true;
+ break;
+ case DELETE:
+ assert_goto(tv->results[j] != EFI_SUCCESS, err,
+ "Tried to delete absent variable \"%s\"\n",
+ Str2str(tv->name));
+ assert_goto(created == false, err,
+ "Deleted variable \"%s\" was previously created.\n",
+ Str2str(tv->name));
+ break;
+ case APPEND:
+ assert_goto(false, err,
+ "No append action should have been tested\n");
+ break;
+ case REPLACE:
+ assert_goto(false, err,
+ "No replace action should have been tested\n");
+ break;
+ }
+ }
+ }
+ if (tv->must_be_present) {
+ assert_goto(found == true, err,
+ "variable \"%s\" was not found.\n",
+ Str2str(tv->name));
+ }
+
+ if (tv->must_be_absent) {
+ assert_goto(found == false, err,
+ "variable \"%s\" was found.\n",
+ Str2str(tv->name));
+ }
+ }
+
+ uint8_t *pos = NULL;
+ for (size_t i = 0; i < ST->NumberOfTableEntries; i++) {
+ EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i];
+
+ if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0)
+ continue;
+
+ pos = (void *)ct->VendorTable;
+ break;
+ }
+
+ assert_nonzero_goto(pos, err, "%p != 0\n");
+
+ size_t i = 0;
+ while (pos) {
+ struct mock_mok_variable_config_entry *mock_entry =
+ &test_mok_config_table[i];
+ struct mok_variable_config_entry *mok_entry =
+ (struct mok_variable_config_entry *)pos;
+
+ /*
+ * If the tables are different lengths, this will trigger.
+ */
+ assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err,
+ "mok.name[0] %ld != test.name[0] %ld\n");
+ if (mock_entry->name[0] == 0)
+ break;
+
+ assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n");
+ assert_zero_goto(strncmp(mok_entry->name, mock_entry->name,
+ sizeof(mock_entry->name)),
+ err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n",
+ mok_entry->name, mock_entry->name);
+
+ /*
+ * As of 7501b6bb449f ("mok: fix potential buffer overrun in
+ * import_mok_state"), we should not see any mok config
+ * variables with data_size == 0.
+ */
+ assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n");
+
+ assert_equal_goto(mok_entry->data_size, mock_entry->data_size,
+ err, "%ld != %ld\n");
+ assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data,
+ mok_entry->data_size),
+ err, "%ld != %ld\n");
+ pos += offsetof(struct mok_variable_config_entry, data)
+ + mok_entry->data_size;
+ i += 1;
+ }
+
+ ret = 0;
+err:
+ for (UINTN k = 0; k < n_mok_state_variables; k++) {
+ struct mok_state_variable *v =
+ &mok_state_variables[k];
+ if (v->data_size && v->data) {
+ free(v->data);
+ v->data = NULL;
+ v->data_size = 0;
+ }
+ }
+
+ test_vars = NULL;
+ mock_set_variable_post_hook = NULL;
+ mock_get_variable_post_hook = NULL;
+ return ret;
+}
+
+int
+main(void)
+{
+ int status = 0;
+ setbuf(stdout, NULL);
+
+ const char *sort_policy_names[] = {
+ "MOCK_SORT_DESCENDING",
+ "MOCK_SORT_PREPEND",
+ "MOCK_SORT_APPEND",
+ "MOCK_SORT_ASCENDING",
+ "MOCK_SORT_MAX_SENTINEL"
+ };
+
+ const char *del_policy_names[] = {
+ "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO",
+ "MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH",
+ "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO|MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH",
+ "MOCK_VAR_DELETE_ATTR_ALLOW_NONE",
+ NULL
+ };
+
+ int delete_policies[] = {
+ MOCK_VAR_DELETE_ATTR_ALLOW_ZERO,
+ MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH,
+ MOCK_VAR_DELETE_ATTR_ALLOW_ZERO
+ | MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH,
+ 0
+ };
+
+ for (int i = 0; i < MOCK_SORT_MAX_SENTINEL; i++) {
+ mock_variable_sort_policy = i;
+ mock_config_table_sort_policy = i;
+ int j = 0;
+
+ printf("%s: setting variable sort policy to %s\n",
+ program_invocation_short_name, sort_policy_names[i]);
+ do {
+ printf("%s: setting delete policy to %s\n",
+ program_invocation_short_name,
+ del_policy_names[j]);
+
+ mock_variable_delete_attr_policy = delete_policies[j];
+ test(test_mok_mirror_0);
+ mock_finalize_vars_and_configs();
+
+ if (delete_policies[j] == 0)
+ break;
+ } while (++j);
+ }
+
+ return status;
+}
+
+// vim:fenc=utf-8:tw=75:noet