summaryrefslogtreecommitdiff
path: root/test-mok-mirror.c
diff options
context:
space:
mode:
Diffstat (limited to 'test-mok-mirror.c')
-rw-r--r--test-mok-mirror.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/test-mok-mirror.c b/test-mok-mirror.c
new file mode 100644
index 00000000..3479ddf8
--- /dev/null
+++ b/test-mok-mirror.c
@@ -0,0 +1,401 @@
+// 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));
+ }
+
+ 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] = GET;
+ tv->results[tv->n_ops] = *status;
+ tv->n_ops += 1;
+ }
+}
+
+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;
+ int gets = 0;
+
+ 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;
+ case GET:
+ if (tv->results[j] == EFI_SUCCESS)
+ gets += 1;
+ break;
+ }
+ }
+ assert_goto(gets == 0 || gets == 1, err,
+ "Variable should not be read %d times.\n", gets);
+ }
+ 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