diff options
Diffstat (limited to 'test-mok-mirror.c')
| -rw-r--r-- | test-mok-mirror.c | 575 |
1 files changed, 445 insertions, 130 deletions
diff --git a/test-mok-mirror.c b/test-mok-mirror.c index f34f62a9..38b7ed97 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -8,6 +8,7 @@ #include "mock-variables.h" #include "test-data-efivars-1.h" +#include <err.h> #include <stdio.h> #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -21,21 +22,51 @@ start_image(EFI_HANDLE image_handle UNUSED, CHAR16 *mm) #define N_TEST_VAR_OPS 40 struct test_var { + /* + * The GUID, name, and attributes of the variables + */ EFI_GUID guid; CHAR16 *name; UINT32 attrs; - UINTN n_ops; + /* + * If the variable is required to be present, with the attributes + * specified above, for a test to pass + */ bool must_be_present; + /* + * If the variable is required to be absent, no matter what the + * attributes, for a test to pass + */ bool must_be_absent; + /* + * The number of operations on this variable that we've seen + */ + UINTN n_ops; + /* + * the operations that have occurred on this variable + */ mock_variable_op_t ops[N_TEST_VAR_OPS]; + /* + * the result codes of those operations + */ EFI_STATUS results[N_TEST_VAR_OPS]; }; static struct test_var *test_vars; struct mock_mok_variable_config_entry { + /* + * The name of an entry we expect to see in the MokVars + * configuration table + */ CHAR8 name[256]; + /* + * The size of its data + */ UINT64 data_size; + /* + * A pointer to what the data should be + */ const unsigned char *data; }; @@ -94,16 +125,217 @@ getvar_post(CHAR16 *name, EFI_GUID *guid, } } +static EFI_STATUS +check_variables(struct test_var *vars) +{ + for (size_t i = 0; vars[i].name != NULL; i++) { + struct test_var *tv = &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) { + /* + * This asserts if it isn't present, and if that's + * the case, then the attributes are already + * validated in the search loop + */ + assert_goto(found == true, err, + "variable \"%s\" was not found.\n", + Str2str(tv->name)); + } + + if (tv->must_be_absent) { + /* + * deliberately does not check the attributes at + * this time. + */ + assert_goto(found == false, err, + "variable \"%s\" was found.\n", + Str2str(tv->name)); + } + } + + return EFI_SUCCESS; +err: + return EFI_INVALID_PARAMETER; +} + +static EFI_STATUS +check_config_table(struct mock_mok_variable_config_entry *test_configs, + uint8_t *config_pos) +{ + size_t i = 0; + struct mok_variable_config_entry *mok_entry = NULL; + struct mock_mok_variable_config_entry *mock_entry = NULL; + + while (config_pos) { + mock_entry = &test_configs[i]; + mok_entry = (struct mok_variable_config_entry *)config_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"); + config_pos += offsetof(struct mok_variable_config_entry, data) + + mok_entry->data_size; + i += 1; + } + + return EFI_SUCCESS; +err: + warnx("Failed on entry %zu mok.name:\"%s\" mock.name:\"%s\"", i, + mok_entry->name, mock_entry->name); + if ((mok_entry && mock_entry) && (!mok_entry->name[0] || !mock_entry->name[0])) + warnx("Entry is missing in %s variable list.", mok_entry->name[0] ? "expected" : "found"); + + return EFI_INVALID_PARAMETER; +} + +static int +test_mok_mirror(struct test_var *vars, + struct mock_mok_variable_config_entry *configs, + EFI_STATUS expected_status) +{ + EFI_STATUS status; + EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; + int ret = -1; + + status = import_mok_state(NULL); + assert_equal_goto(status, expected_status, err, + "got 0x%016lx, expected 0x%016lx\n", + expected_status); + + test_vars = vars; + + status = check_variables(vars); + if (EFI_ERROR(status)) + goto err; + + 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"); + + status = check_config_table(configs, pos); + if (EFI_ERROR(status)) + goto err; + + 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; + + return ret; +} + +/* + * This tests mirroring of mok variables on fairly optimistic conditions: + * there's enough space for everything, and so we expect to see all the + * RT variables for which we have data mirrored + */ static int -test_mok_mirror_0(void) +test_mok_mirror_with_enough_space(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[] = { + struct test_var test_mok_mirror_with_enough_space_vars[] = { {.guid = SHIM_LOCK_GUID, .name = L"MokList", .must_be_present = true, @@ -166,11 +398,20 @@ test_mok_mirror_0(void) EFI_VARIABLE_RUNTIME_ACCESS, .ops = { NONE, }, }, + {.guid = SHIM_LOCK_GUID, + .name = L"HSIStatus", + .attrs = 0, + .ops = { NONE, }, + }, {.guid = { 0, }, .name = NULL, } }; + /* + * We must see the supplied values of MokListRT, MokListXRT, and + * SbatLevelRT in the config table + */ struct mock_mok_variable_config_entry test_mok_config_table[] = { {.name = "MokListRT", .data_size = sizeof(test_data_efivars_1_MokListRT), @@ -188,6 +429,10 @@ test_mok_mirror_0(void) .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), .data = test_data_efivars_1_MokListTrustedRT }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, {.name = { 0, }, .data_size = 0, .data = NULL, @@ -202,147 +447,214 @@ test_mok_mirror_0(void) 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); + ret = test_mok_mirror(&test_mok_mirror_with_enough_space_vars[0], + test_mok_config_table, + EFI_SUCCESS); - 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; + mock_set_variable_post_hook = NULL; + mock_get_variable_post_hook = NULL; + return ret; +} - list_for_each(pos, &mock_variables) { - struct mock_variable *var; - bool deleted; - bool created; - int gets = 0; +static int +test_mok_mirror_setvar_out_of_resources(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; - 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)); - } + /* + * These sizes are picked specifically so that MokListRT will fail + * to get mirrored with the test data in test-data/efivars-1 + */ + list_t mock_obnoxious_variable_limits; + UINT64 obnoxious_max_var_storage = 0xffe4; + UINT64 obnoxious_remaining_var_storage = 919+0x3c; + UINT64 obnoxious_max_var_size = 919; + + struct mock_variable_limits obnoxious_limits[] = { + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &obnoxious_max_var_storage, + .remaining_var_storage = &obnoxious_remaining_var_storage, + .max_var_size = &obnoxious_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = 0, } + }; - if (tv->must_be_absent) { - assert_goto(found == false, err, - "variable \"%s\" was found.\n", - Str2str(tv->name)); + struct test_var test_mok_mirror_enospc_vars[] = { + /* + * We must to see a BS|NV MokList + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokList", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must *NOT* see a BS|RT MokListRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokListRT", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must see a BS|NV MokListX + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokListX", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must see a BS|RT MokListXRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokListXRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must see a BS|NV SbatLevel + */ + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevel", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must see a BS|RT SbatLevelRT + */ + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevelRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must not see a MokIgnoreDB + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokIgnoreDB", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + /* + * We must not see MokSBState + */ + {.guid = SHIM_LOCK_GUID, + .name = L"MokSBState", + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + /* + * We must not see MokSBStateRT + */ + {.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, } - } + }; - uint8_t *pos = NULL; - for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { - EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + /* + * We must see the supplied values of MokListRT, MokListXRT, and + * SbatLevelRT in the config table + */ + 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 = "MokListTrustedRT", + .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), + .data = test_data_efivars_1_MokListTrustedRT + }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, + {.name = { 0, }, + .data_size = 0, + .data = NULL, + } + }; - if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) - continue; + UINT64 max_storage_sz = 0; + UINT64 max_var_sz = 0; + UINT64 remaining_sz = 0; - pos = (void *)ct->VendorTable; - break; + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; } - assert_nonzero_goto(pos, err, "%p != 0\n"); + mock_load_variables("test-data/efivars-1", mok_rt_vars, true); - 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; + mock_set_variable_post_hook = setvar_post; + mock_get_variable_post_hook = getvar_post; - /* - * 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; + mock_set_usage_limits(&mock_obnoxious_variable_limits, + &obnoxious_limits[0]); - 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); + ret = test_mok_mirror(&test_mok_mirror_enospc_vars[0], + test_mok_config_table, + EFI_OUT_OF_RESOURCES); - /* - * 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"); + mock_set_default_usage_limits(); - 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; @@ -391,7 +703,10 @@ main(void) del_policy_names[j]); mock_variable_delete_attr_policy = delete_policies[j]; - test(test_mok_mirror_0); + test(test_mok_mirror_with_enough_space); + mock_finalize_vars_and_configs(); + + test(test_mok_mirror_setvar_out_of_resources); mock_finalize_vars_and_configs(); if (delete_policies[j] == 0) |
