From d2e13173130b9e8366ee75432618a88ad0c32db5 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Fri, 31 Oct 2025 13:03:59 +0100 Subject: [PATCH 1/9] Add a comment on DATA_LIMIT sizing calculation Signed-off-by: Tu Dinh --- include/handler.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/handler.h b/include/handler.h index 0851836..d3c5244 100644 --- a/include/handler.h +++ b/include/handler.h @@ -35,7 +35,15 @@ #include "efi.h" #define NAME_LIMIT 4096 /* Maximum length of name */ -#define DATA_LIMIT 57344 /* Maximum length of a single variable */ + +/* The following limits must respect the minimum prescribed by WHCP. */ +/* + * Maximum length of a single variable. To ensure that there's sufficient room + * for variables in the communication buffer, SHMEM_PAGES must be high enough to + * cover the data limit + 1 page (4096 bytes) for the variable name + 1 page for + * protocol overhead. + */ +#define DATA_LIMIT 57344 #define TOTAL_LIMIT 131072 /* Maximum total storage */ /* From 1a0bddbfd90430e94debac2b8f1bec3d10a56270 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Fri, 31 Oct 2025 16:24:08 +0100 Subject: [PATCH 2/9] Dynamically allocate command buffers If we grow SHMEM_SIZE any further, we should not allocate it from the stack. Remove SHMEM_SIZE which is no longer needed. Signed-off-by: Tu Dinh --- handler.c | 23 +++++++++++++++++------ include/handler.h | 1 - tools/tool-lib.c | 19 ++++++++++++------- tools/tool-lib.h | 2 ++ tools/varstore-get.c | 7 +++---- tools/varstore-ls.c | 7 +++---- tools/varstore-set.c | 7 +++---- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/handler.c b/handler.c index 37780b0..c77a689 100644 --- a/handler.c +++ b/handler.c @@ -2152,7 +2152,8 @@ static bool set_variable_from_auth(const uint8_t *name, UINTN name_len, const EFI_GUID *guid, const uint8_t *data, off_t data_len, bool append) { - uint8_t buf[SHMEM_SIZE]; + bool ret = false; + uint8_t *cmd_buf = NULL; uint8_t *ptr; EFI_STATUS status; UINT32 attr = ATTR_BRNV_TIME; @@ -2160,7 +2161,13 @@ set_variable_from_auth(const uint8_t *name, UINTN name_len, const EFI_GUID *guid if (append) attr |= EFI_VARIABLE_APPEND_WRITE; - ptr = buf; + cmd_buf = calloc(SHMEM_PAGES, PAGE_SIZE); + if (!cmd_buf) { + ERR("Failed to allocate command buffer\n"); + goto out; + } + + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, name, name_len); @@ -2168,16 +2175,20 @@ set_variable_from_auth(const uint8_t *name, UINTN name_len, const EFI_GUID *guid serialize_data(&ptr, data, data_len); serialize_uint32(&ptr, attr); *ptr = 0; /* at_runtime */ - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status != EFI_SUCCESS) { ERR("Failed to execute auth data: 0x%lx\n", status); - return false; + goto out; } - return true; + ret = true; + +out: + free(cmd_buf); + return ret; } bool diff --git a/include/handler.h b/include/handler.h index d3c5244..c753496 100644 --- a/include/handler.h +++ b/include/handler.h @@ -56,7 +56,6 @@ #define PAGE_SIZE 4096 #define SHMEM_PAGES 16 -#define SHMEM_SIZE (SHMEM_PAGES * PAGE_SIZE) #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) diff --git a/tools/tool-lib.c b/tools/tool-lib.c index 2d7eab5..35c02d0 100644 --- a/tools/tool-lib.c +++ b/tools/tool-lib.c @@ -39,6 +39,8 @@ #include "tool-lib.h" +uint8_t *cmd_buf = NULL; + /* * Prepare a command-line tool for running by initializing state and loading * the VM's variables from the backend. @@ -51,6 +53,10 @@ tool_init(void) secure_boot_enable = false; auth_enforce = false; + cmd_buf = calloc(SHMEM_PAGES, PAGE_SIZE); + if (!cmd_buf) + return false; + if (!db->check_args()) return false; @@ -292,7 +298,6 @@ print_depriv_options(void) bool do_rm(const EFI_GUID *guid, const char *name) { - uint8_t buf[SHMEM_SIZE]; uint8_t *ptr; uint8_t variable_name[NAME_LIMIT]; EFI_STATUS status; @@ -301,7 +306,7 @@ do_rm(const EFI_GUID *guid, const char *name) name_size = parse_name(name, variable_name); - ptr = buf; + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); @@ -309,9 +314,9 @@ do_rm(const EFI_GUID *guid, const char *name) serialize_uintn(&ptr, DATA_LIMIT); *ptr = 0; - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status != EFI_SUCCESS) { print_efi_error(status); @@ -320,7 +325,7 @@ do_rm(const EFI_GUID *guid, const char *name) attr = unserialize_uint32(&ptr); - ptr = buf; + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, variable_name, name_size); @@ -340,9 +345,9 @@ do_rm(const EFI_GUID *guid, const char *name) serialize_uint32(&ptr, attr); *ptr = 0; - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status != EFI_SUCCESS) { print_efi_error(status); diff --git a/tools/tool-lib.h b/tools/tool-lib.h index 46a86d8..ad605f4 100644 --- a/tools/tool-lib.h +++ b/tools/tool-lib.h @@ -75,6 +75,8 @@ } \ break; +extern uint8_t *cmd_buf; + bool tool_init(void); void print_efi_error(EFI_STATUS status); bool parse_guid(EFI_GUID *guid, const char *guid_str); diff --git a/tools/varstore-get.c b/tools/varstore-get.c index 366758a..bc97618 100644 --- a/tools/varstore-get.c +++ b/tools/varstore-get.c @@ -61,7 +61,6 @@ usage(const char *progname) static bool do_get(const char *guid_str, const char *name, bool show_attr) { - uint8_t buf[SHMEM_SIZE]; uint8_t *ptr; uint8_t variable_name[NAME_LIMIT]; EFI_GUID guid; @@ -76,7 +75,7 @@ do_get(const char *guid_str, const char *name, bool show_attr) return false; } - ptr = buf; + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); @@ -84,9 +83,9 @@ do_get(const char *guid_str, const char *name, bool show_attr) serialize_uintn(&ptr, DATA_LIMIT); *ptr = 0; - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status != EFI_SUCCESS) { print_efi_error(status); diff --git a/tools/varstore-ls.c b/tools/varstore-ls.c index 8b28434..9954367 100644 --- a/tools/varstore-ls.c +++ b/tools/varstore-ls.c @@ -79,7 +79,6 @@ print_guid(const EFI_GUID *guid) static bool do_ls(void) { - uint8_t buf[SHMEM_SIZE]; uint8_t name[NAME_LIMIT] = {0}; uint8_t *ptr; EFI_GUID guid = {{0}}; @@ -88,7 +87,7 @@ do_ls(void) EFI_STATUS status; for (;;) { - ptr = buf; + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_GET_NEXT_VARIABLE); serialize_uintn(&ptr, NAME_LIMIT); @@ -96,9 +95,9 @@ do_ls(void) serialize_guid(&ptr, &guid); *ptr = 0; - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status == EFI_NOT_FOUND) break; diff --git a/tools/varstore-set.c b/tools/varstore-set.c index 6185adb..64a66c1 100644 --- a/tools/varstore-set.c +++ b/tools/varstore-set.c @@ -65,7 +65,6 @@ static bool do_set(const char *guid_str, const char *name, const char *attr_str, const char *path) { - uint8_t buf[SHMEM_SIZE]; uint8_t *ptr, *data; uint8_t variable_name[NAME_LIMIT]; EFI_GUID guid; @@ -113,7 +112,7 @@ do_set(const char *guid_str, const char *name, const char *attr_str, } fclose(f); - ptr = buf; + ptr = cmd_buf; serialize_uint32(&ptr, 1); /* version */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, variable_name, name_size); @@ -123,9 +122,9 @@ do_set(const char *guid_str, const char *name, const char *attr_str, serialize_uint32(&ptr, attr); *ptr = 0; - dispatch_command(buf); + dispatch_command(cmd_buf); - ptr = buf; + ptr = cmd_buf; status = unserialize_uintn(&ptr); if (status != EFI_SUCCESS) { print_efi_error(status); From bed15e8f41392075334fdc4eec600f622c39f574 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Fri, 31 Oct 2025 18:27:27 +0100 Subject: [PATCH 3/9] Implement communication protocol v2 The hardcoded SHMEM_PAGES in protocol v1 was insufficient to meet the WHCP System.Fundamentals.Firmware.UEFISecureBoot point 30: Reserved Memory for Windows Secure Boot UEFI Variables. A total of at least 128 KB of non-volatile NVRAM storage memory must be available for NV UEFI variables (authenticated and unauthenticated, BS and RT) used by UEFI Secure Boot and Windows, and the maximum supported variable size must be at least 64 KB. Protocol v2 uses the following communication buffer format: uint32_t version = 2; uint32_t shmem_pages; uint32_t cmd; (command-specific data) Protocol v2 caps the maximum page count to 32, allowing a theoretical maximum DATA_LIMIT of 122880 bytes. However, limit the current v2 DATA_LIMIT to 65536, while keeping the ability to transparently increase this limit later. The introduction of v2 has a few implications: * DATA_LIMIT is now broken into 2 values: a per-protocol limit and a global limit DATA_LIMIT_MAX, imposed on all variables regardless of protocol. * It is now possible to use v2 to set/append to a variable in a way that makes it unreadable from v1 (as it'd be bigger than DATA_LIMIT_V1). As a result, get_variable can no longer assume that current variable size will be smaller than the comm buffer size (TODO: verify safety via test) test_set_variable_resource_limit was adapted for v2 to only insert 1 initial variable, since 2 variables is too big for TOTAL_LIMIT. Signed-off-by: Tu Dinh --- handler.c | 111 ++++- handler_port.c | 33 +- include/handler.h | 36 +- test.c | 959 ++++++++++++++++++++++--------------------- tools/tool-lib.c | 4 +- tools/varstore-get.c | 6 +- tools/varstore-set.c | 2 +- xapidb-lib.c | 2 +- 8 files changed, 653 insertions(+), 500 deletions(-) diff --git a/handler.c b/handler.c index c77a689..cc82612 100644 --- a/handler.c +++ b/handler.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include @@ -283,8 +284,10 @@ do_get_variable(uint8_t *comm_buf) struct efi_variable *l; ptr = comm_buf; - unserialize_uint32(&ptr); /* version */ - unserialize_command(&ptr); + if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { + assert(0); + return; + } name = unserialize_data(&ptr, &name_len, NAME_LIMIT); if (!name) { serialize_result(&comm_buf, name_len == 0 ? EFI_NOT_FOUND : EFI_DEVICE_ERROR); @@ -1586,6 +1589,7 @@ debug_all_variables(const struct efi_variable *l) static void do_set_variable(uint8_t *comm_buf) { + UINT32 version; UINTN name_len, data_len; struct efi_variable *l, *prev = NULL; uint8_t *ptr, *name, *data; @@ -1597,17 +1601,22 @@ do_set_variable(uint8_t *comm_buf) EFI_TIME timestamp; ptr = comm_buf; - unserialize_uint32(&ptr); /* version */ - unserialize_command(&ptr); + if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { + assert(0); + return; + } name = unserialize_data(&ptr, &name_len, NAME_LIMIT); if (!name) { serialize_result(&comm_buf, name_len == 0 ? EFI_INVALID_PARAMETER : EFI_DEVICE_ERROR); return; } unserialize_guid(&ptr, &guid); - data = unserialize_data(&ptr, &data_len, DATA_LIMIT); + data = unserialize_data(&ptr, &data_len, data_limit(version)); if (!data && data_len) { - serialize_result(&comm_buf, data_len > DATA_LIMIT ? EFI_OUT_OF_RESOURCES : EFI_DEVICE_ERROR); + serialize_result(&comm_buf, + data_len > data_limit(version) + ? EFI_OUT_OF_RESOURCES + : EFI_DEVICE_ERROR); free(name); return; } @@ -1757,7 +1766,7 @@ do_set_variable(uint8_t *comm_buf) } } - if (l->data_len + data_len > DATA_LIMIT || + if (l->data_len + data_len > data_limit(version) || get_space_usage() + data_len > TOTAL_LIMIT) { serialize_result(&ptr, EFI_OUT_OF_RESOURCES); goto err; @@ -1923,8 +1932,10 @@ do_get_next_variable(uint8_t *comm_buf) BOOLEAN at_runtime; ptr = comm_buf; - unserialize_uint32(&ptr); /* version */ - unserialize_command(&ptr); + if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { + assert(0); + return; + } avail_len = unserialize_uintn(&ptr); name = unserialize_data(&ptr, &name_len, NAME_LIMIT); if (!name && name_len) { @@ -1978,12 +1989,15 @@ do_get_next_variable(uint8_t *comm_buf) static void do_query_variable_info(uint8_t *comm_buf) { + UINT32 version; uint8_t *ptr; UINT32 attr; ptr = comm_buf; - unserialize_uint32(&ptr); /* version */ - unserialize_command(&ptr); + if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { + assert(0); + return; + } attr = unserialize_uint32(&ptr); ptr = comm_buf; @@ -2000,7 +2014,7 @@ do_query_variable_info(uint8_t *comm_buf) serialize_result(&ptr, EFI_SUCCESS); serialize_uint64(&ptr, TOTAL_LIMIT); serialize_uint64(&ptr, TOTAL_LIMIT - get_space_usage()); - serialize_uint64(&ptr, DATA_LIMIT); + serialize_uint64(&ptr, data_limit(version)); } static void @@ -2023,8 +2037,10 @@ do_notify_sb_failure(uint8_t *comm_buf) called = true; ptr = comm_buf; - unserialize_uint32(&ptr); /* version */ - unserialize_command(&ptr); + if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { + assert(0); + return; + } ret = db->sb_notify(); @@ -2032,19 +2048,69 @@ do_notify_sb_failure(uint8_t *comm_buf) serialize_result(&ptr, ret ? EFI_SUCCESS : EFI_DEVICE_ERROR); } -void dispatch_command(uint8_t *comm_buf) +UINTN data_limit(UINT32 version) { + switch (version) { + case 1: + return DATA_LIMIT_V1; + case 2: + return DATA_LIMIT_V2; + default: + assert(0); + return 0; + } +} + +EFI_STATUS snoop_command(uint8_t **comm_buf, UINT32 *out_version, + UINT32 *out_nr_pages, enum command_t *out_command) { - enum command_t command; + uint8_t *ptr = *comm_buf; UINT32 version; - uint8_t *ptr = comm_buf; + UINT32 nr_pages; + enum command_t command; version = unserialize_uint32(&ptr); - if (version != 1) { + switch (version) { + case 1: + nr_pages = SHMEM_PAGES_V1; + break; + case 2: + nr_pages = unserialize_uint32(&ptr); + if (nr_pages < SHMEM_PAGES_V2_MIN || nr_pages > SHMEM_PAGES_V2_MAX) { + DBG("Bad shmem page count: %u\n", nr_pages); + return EFI_INVALID_PARAMETER; + } + break; + default: DBG("Unknown version: %u\n", version); - return; + return EFI_INVALID_PARAMETER; } command = unserialize_command(&ptr); + + if (out_version) + *out_version = version; + if (out_nr_pages) + *out_nr_pages = nr_pages; + if (out_command) + *out_command = command; + *comm_buf = ptr; + return EFI_SUCCESS; +} + +void dispatch_command(uint8_t *comm_buf) +{ + UINT32 version, nr_pages; + enum command_t command; + uint8_t *ptr = comm_buf; + EFI_STATUS status; + + status = snoop_command(&ptr, &version, &nr_pages, &command); + if (status != EFI_SUCCESS) { + DBG("snoop_command refused version data\n"); + serialize_result(&ptr, EFI_INVALID_PARAMETER); + return; + } + switch (command) { case COMMAND_GET_VARIABLE: DBG("COMMAND_GET_VARIABLE\n"); @@ -2161,14 +2227,15 @@ set_variable_from_auth(const uint8_t *name, UINTN name_len, const EFI_GUID *guid if (append) attr |= EFI_VARIABLE_APPEND_WRITE; - cmd_buf = calloc(SHMEM_PAGES, PAGE_SIZE); + cmd_buf = calloc(SHMEM_PAGES_V2_MAX, PAGE_SIZE); if (!cmd_buf) { ERR("Failed to allocate command buffer\n"); goto out; } ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, name, name_len); serialize_guid(&ptr, guid); @@ -2257,7 +2324,7 @@ load_one_auth_data(const char *path, uint8_t **data_out, off_t *len) * This will be checked later during SetVariable but check it now to avoid * reading a malicously large file into memory. */ - if (st.st_size > DATA_LIMIT) { + if (st.st_size > DATA_LIMIT_MAX) { ERR("Auth file '%s' is too large: %ld\n", path, st.st_size); fclose(f); return false; diff --git a/handler_port.c b/handler_port.c index 4ddbad0..d0cbbb8 100644 --- a/handler_port.c +++ b/handler_port.c @@ -49,23 +49,47 @@ static struct { static void io_port_writel(uint64_t offset, uint64_t size, uint32_t val) { - xen_pfn_t pfns[SHMEM_PAGES]; + xen_pfn_t pfns[SHMEM_PAGES_V2_MAX]; void *shmem; + UINT32 nr_pages; int i; + uint8_t *ptr; + EFI_STATUS status; if (offset != 0 || size != sizeof(uint32_t)) { DBG("Expected size 4, offset 0. Got %" PRIu64 ", %" PRIu64 ".\n", size, offset); return; } - for (i = 0; i < SHMEM_PAGES; i++) + for (i = 0; i < SHMEM_PAGES_V2_MAX; i++) pfns[i] = val + i; DBG("io_port write\n"); + /* Only the first page is wanted for nr_pages detection */ shmem = xenforeignmemory_map(io_info.fmem, io_info.domid, PROT_READ | PROT_WRITE, - SHMEM_PAGES, pfns, NULL); + 1, pfns, NULL); + if (!shmem) { + DBG("map foreign range failed (snoop): %d\n", errno); + return; + } + + ptr = shmem; + /* nr_pages is the only thing we're interested in here */ + status = snoop_command(&ptr, NULL, &nr_pages, NULL); + + xenforeignmemory_unmap(io_info.fmem, shmem, 1); + + if (status != EFI_SUCCESS) { + DBG("snoop_command refused version data\n"); + return; + } + + shmem = xenforeignmemory_map(io_info.fmem, + io_info.domid, + PROT_READ | PROT_WRITE, + nr_pages, pfns, NULL); if (!shmem) { DBG("map foreign range failed: %d\n", errno); return; @@ -73,7 +97,7 @@ io_port_writel(uint64_t offset, uint64_t size, uint32_t val) dispatch_command(shmem); - xenforeignmemory_unmap(io_info.fmem, shmem, SHMEM_PAGES); + xenforeignmemory_unmap(io_info.fmem, shmem, nr_pages); } @@ -85,4 +109,3 @@ setup_handler_io_port(domid_t domid, xenforeignmemory_handle *fmem) { return register_io_port_writel_handler(HANDLER_PORT_ADDRESS, io_port_writel); } - diff --git a/include/handler.h b/include/handler.h index c753496..5702314 100644 --- a/include/handler.h +++ b/include/handler.h @@ -34,17 +34,41 @@ #include "efi.h" +#define PAGE_SIZE 4096 +#define SHMEM_PAGES_V1 16 +/* Consumers shouldn't use this value, but should stick to SHMEM_PAGES_V2_MAX */ +#define SHMEM_PAGES_V2_MIN 18 +#define SHMEM_PAGES_V2_MAX 32 + #define NAME_LIMIT 4096 /* Maximum length of name */ -/* The following limits must respect the minimum prescribed by WHCP. */ /* * Maximum length of a single variable. To ensure that there's sufficient room * for variables in the communication buffer, SHMEM_PAGES must be high enough to * cover the data limit + 1 page (4096 bytes) for the variable name + 1 page for * protocol overhead. */ -#define DATA_LIMIT 57344 -#define TOTAL_LIMIT 131072 /* Maximum total storage */ +#define DATA_LIMIT(shmem_pages) (((shmem_pages) - 2) * PAGE_SIZE) + +/* The following limits must respect the minimum prescribed by WHCP. */ + +#define DATA_LIMIT_V1 DATA_LIMIT(SHMEM_PAGES_V1) +/* + * For now, DATA_LIMIT_V2 and SHMEM_PAGES_V2_MIN are coupled: DATA_LIMIT_V2 is + * calculated from SHMEM_PAGES_V2_MIN, and one can't be increased without + * increasing the other. + * + * However, there's reserved headroom in SHMEM_PAGES_V2_MAX for later silently + * increasing DATA_LIMIT_V2; consumers should stick to SHMEM_PAGES_V2_MAX and + * not worry about this. + */ +#define DATA_LIMIT_V2 DATA_LIMIT(SHMEM_PAGES_V2_MIN) + +/* For non-protocol uses */ +#define DATA_LIMIT_MAX DATA_LIMIT_V2 + +/* Maximum total storage */ +#define TOTAL_LIMIT 131072 /* * A single variable takes up a minimum number of bytes. @@ -54,9 +78,6 @@ #define VARIABLE_SIZE_OVERHEAD 128 #define MAX_VARIABLE_COUNT (TOTAL_LIMIT / VARIABLE_SIZE_OVERHEAD) -#define PAGE_SIZE 4096 -#define SHMEM_PAGES 16 - #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) enum command_t { @@ -81,6 +102,9 @@ struct efi_variable { extern struct efi_variable *var_list; +UINTN data_limit(UINT32 version); +EFI_STATUS snoop_command(uint8_t **comm_buf, UINT32 *out_version, + UINT32 *out_nr_pages, enum command_t *out_command); void dispatch_command(uint8_t *comm_buf); bool setup_crypto(void); bool setup_variables(void); diff --git a/test.c b/test.c index 4de7e30..9d8733c 100644 --- a/test.c +++ b/test.c @@ -38,8 +38,19 @@ static char *save_name = "test.dat"; const enum log_level log_level = LOG_LVL_ERROR; +/* Multi-version */ +#define MULTI_TEST(test_name) \ + static void test_name(UINT32 version); \ + static void test_name ## _v1(void) { \ + test_name(1); \ + } \ + static void test_name ## _v2(void) { \ + test_name(2); \ + } \ + static void test_name(UINT32 version) + /* The communication buffer. */ -static uint8_t buf[16 * 4096]; +static uint8_t buf[SHMEM_PAGES_V2_MAX * PAGE_SIZE]; /* Wide char support */ @@ -348,11 +359,28 @@ static void reset_vars(void) var_list = NULL; } -static void call_get_variable(const dstring *name, const EFI_GUID *guid, - UINTN avail, BOOLEAN at_runtime) +static void serialize_buf_version(uint8_t **ptr, UINT32 version) +{ + switch (version) + { + case 1: + serialize_uint32(ptr, 1); + break; + case 2: + serialize_uint32(ptr, 2); + serialize_uint32(ptr, SHMEM_PAGES_V2_MAX); + break; + default: + assert(0); + } +} + +static void call_get_variable(UINT32 version, const dstring *name, + const EFI_GUID *guid, UINTN avail, + BOOLEAN at_runtime) { uint8_t *ptr = buf; - serialize_uint32(&ptr, 1); + serialize_buf_version(&ptr, version); serialize_uint32(&ptr, (UINT32)COMMAND_GET_VARIABLE); serialize_data(&ptr, (uint8_t *)name->data, dstring_data_size(name)); serialize_guid(&ptr, guid); @@ -362,7 +390,7 @@ static void call_get_variable(const dstring *name, const EFI_GUID *guid, dispatch_command(buf); } -static EFI_STATUS call_get_variable_data(const dstring *name, +static EFI_STATUS call_get_variable_data(UINT32 version, const dstring *name, const EFI_GUID *guid, UINTN avail, BOOLEAN at_runtime, uint8_t **data, UINTN *len) @@ -370,7 +398,7 @@ static EFI_STATUS call_get_variable_data(const dstring *name, uint8_t *ptr = buf; EFI_STATUS status; - call_get_variable(name, guid, avail, at_runtime); + call_get_variable(version, name, guid, avail, at_runtime); status = unserialize_uintn(&ptr); unserialize_uint32(&ptr); /* attr */ @@ -378,24 +406,25 @@ static EFI_STATUS call_get_variable_data(const dstring *name, return status; } -static void call_query_variable_info(void) +static void call_query_variable_info(UINT32 version) { uint8_t *ptr = buf; - serialize_uint32(&ptr, 1); + serialize_buf_version(&ptr, version); serialize_uint32(&ptr, (UINT32)COMMAND_QUERY_VARIABLE_INFO); serialize_uint32(&ptr, 0); dispatch_command(buf); } -static void call_get_next_variable(UINTN avail, const dstring *name, - const EFI_GUID *guid, BOOLEAN at_runtime) +static void call_get_next_variable(UINT32 version, UINTN avail, + const dstring *name, const EFI_GUID *guid, + BOOLEAN at_runtime) { uint8_t *ptr = buf; size_t len = name ? dstring_data_size(name) : 0; const uint8_t *data = (uint8_t *)(name ? name->data : NULL); - serialize_uint32(&ptr, 1); + serialize_buf_version(&ptr, version); serialize_uint32(&ptr, (UINT32)COMMAND_GET_NEXT_VARIABLE); serialize_uintn(&ptr, avail); serialize_data(&ptr, data, len); @@ -405,14 +434,14 @@ static void call_get_next_variable(UINTN avail, const dstring *name, dispatch_command(buf); } -static void call_set_variable(const dstring *name, const EFI_GUID *guid, - const uint8_t *data, UINTN data_len, - UINT32 attr, BOOLEAN at_runtime) +static void call_set_variable(UINT32 version, const dstring *name, + const EFI_GUID *guid, const uint8_t *data, + UINTN data_len, UINT32 attr, BOOLEAN at_runtime) { uint8_t *ptr = buf; size_t name_size = dstring_data_size(name); - serialize_uint32(&ptr, 1); + serialize_buf_version(&ptr, version); serialize_uint32(&ptr, (UINT32)COMMAND_SET_VARIABLE); serialize_data(&ptr, (uint8_t *)name->data, name_size); serialize_guid(&ptr, guid); @@ -427,7 +456,7 @@ static void call_set_variable(const dstring *name, const EFI_GUID *guid, * This calls SetVariable and checks the result against expected. In the * failing case, the line number provided reported. */ -static EFI_STATUS setVariable_check_line(const dstring *name, +static EFI_STATUS setVariable_check_line(UINT32 version, const dstring *name, const EFI_GUID *guid, const uint8_t *data, UINTN len, UINT32 attr, EFI_STATUS expected, @@ -437,7 +466,7 @@ static EFI_STATUS setVariable_check_line(const dstring *name, EFI_STATUS status; char *nice_name; - call_set_variable(name, guid, data, len, attr, 0); + call_set_variable(version, name, guid, data, len, attr, 0); ptr = buf; status = unserialize_uintn(&ptr); @@ -449,12 +478,12 @@ static EFI_STATUS setVariable_check_line(const dstring *name, return status; } -#define sv_check(_name, _guid, _data, _len, _attr, _expected) \ - setVariable_check_line(_name, _guid, _data, _len, _attr, \ +#define sv_check(_version, _name, _guid, _data, _len, _attr, _expected) \ + setVariable_check_line(_version, _name, _guid, _data, _len, _attr, \ _expected, __LINE__); -#define sv_ok(_name, _guid, _data, _len, _attr) \ - setVariable_check_line(_name, _guid, _data, _len, _attr, \ +#define sv_ok(_version, _name, _guid, _data, _len, _attr) \ + setVariable_check_line(_version, _name, _guid, _data, _len, _attr, \ EFI_SUCCESS, __LINE__); /* @@ -679,7 +708,7 @@ static size_t sign(uint8_t **signed_buf, const dstring *varname, return auth_size + data_size; } -static void sign_and_check_(const dstring *varname, const EFI_GUID *vendor_guid, +static void sign_and_check_(UINT32 version, const dstring *varname, const EFI_GUID *vendor_guid, UINT32 attributes, const EFI_TIME *timestamp, const uint8_t *data, size_t data_size, const struct sign_details *sd, EFI_STATUS expected, @@ -690,23 +719,24 @@ static void sign_and_check_(const dstring *varname, const EFI_GUID *vendor_guid, len = sign(&sign_buffer, varname, vendor_guid, attributes, timestamp, data, data_size, sd); - setVariable_check_line(varname, vendor_guid, sign_buffer, len, + setVariable_check_line(version, varname, vendor_guid, sign_buffer, len, attributes, expected, line); free(sign_buffer); } -#define sign_and_check(_name, _vend, _attr, _time, _data, _size, _sig, _e_d) \ - sign_and_check_(_name, _vend, _attr, _time, _data, _size, _sig, _e_d, __LINE__) +#define sign_and_check(_version, _name, _vend, _attr, _time, _data, _size, _sig, _e_d) \ + sign_and_check_(_version, _name, _vend, _attr, _time, _data, _size, _sig, _e_d, __LINE__) /* * This function checks the variable's data is as expected. * The expected data is provided, such that it can be compared */ -#define check_variable_data(_name, _guid, _avai, _runtime, _expected, _len) \ - check_variable_data_(_name, _guid, _avai, _runtime, _expected, _len, __LINE__) +#define check_variable_data(_version, _name, _guid, _avai, _runtime, _expected, _len) \ + check_variable_data_(_version, _name, _guid, _avai, _runtime, _expected, _len, __LINE__) -static void check_variable_data_(const dstring *name, const EFI_GUID *guid, +static void check_variable_data_(UINT32 version, const dstring *name, + const EFI_GUID *guid, UINTN avail, BOOLEAN at_runtime, const uint8_t *expected_data, UINTN expected_len, int line) @@ -717,7 +747,8 @@ static void check_variable_data_(const dstring *name, const EFI_GUID *guid, UINTN len; char *nice_name = get_dstring_pretty(name); - status = call_get_variable_data(name, guid, avail, at_runtime, &ret_data, &len); + status = call_get_variable_data(version, name, guid, avail, at_runtime, + &ret_data, &len); vsd_assert_status("get_variable(\"%s\") at %d", status, ==, 0, nice_name, line); vsd_assert_cmpuint("data length for \"%s\" at %d", expected_len, ==, len, @@ -730,7 +761,7 @@ static void check_variable_data_(const dstring *name, const EFI_GUID *guid, free(nice_name); } -static void test_get_variable_no_name(void) +MULTI_TEST(test_get_variable_no_name) { uint8_t *ptr; EFI_STATUS status; @@ -739,7 +770,7 @@ static void test_get_variable_no_name(void) reset_vars(); /* An empty name should not be found. */ - call_get_variable(empty, &nullguid, BSIZ, 0); + call_get_variable(version, empty, &nullguid, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); @@ -747,7 +778,7 @@ static void test_get_variable_no_name(void) free_dstring(empty); } -static void test_get_variable_long_name(void) +MULTI_TEST(test_get_variable_long_name) { uint8_t *ptr; EFI_STATUS status; @@ -758,43 +789,43 @@ static void test_get_variable_long_name(void) memset(bigname->data, 42, dstring_data_size(bigname)); /* Test the maximum variable name length. */ - call_get_variable(bigname, &nullguid, BSIZ, 0); + call_get_variable(version, bigname, &nullguid, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_DEVICE_ERROR); free_dstring(bigname); } -static void test_get_variable_not_found(void) +MULTI_TEST(test_get_variable_not_found) { uint8_t *ptr; EFI_STATUS status; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); /* Name is correct, guid is wrong */ - call_get_variable(tname2, &tguid4, BSIZ, 0); + call_get_variable(version, tname2, &tguid4, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Name is wrong, guid is correct */ - call_get_variable(tname4, &tguid2, BSIZ, 0); + call_get_variable(version, tname4, &tguid2, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Boot service only variable cannot be found at runtime */ - call_get_variable(tname2, &tguid2, BSIZ, 1); + call_get_variable(version, tname2, &tguid2, BSIZ, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); } -static void test_get_variable_found(void) +MULTI_TEST(test_get_variable_found) { uint8_t *ptr; uint8_t *data; @@ -803,12 +834,12 @@ static void test_get_variable_found(void) EFI_STATUS status; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); /* Variable is correctly retrieved. */ - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -820,7 +851,7 @@ static void test_get_variable_found(void) free(data); /* Runtime variable can be found at runtime */ - call_get_variable(tname2, &tguid2, BSIZ, 1); + call_get_variable(version, tname2, &tguid2, BSIZ, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -832,7 +863,7 @@ static void test_get_variable_found(void) free(data); /* Variable is correctly retrieved. */ - call_get_variable(tname3, &tguid3, BSIZ, 0); + call_get_variable(version, tname3, &tguid3, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -844,20 +875,20 @@ static void test_get_variable_found(void) free(data); } -static void test_get_variable_too_small(void) +MULTI_TEST(test_get_variable_too_small) { uint8_t *ptr; UINTN data_len; EFI_STATUS status; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* * If the output buffer is too small, check that the correct size is * returned. */ - call_get_variable(tname1, &tguid1, sizeof(tdata1) - 1, 0); + call_get_variable(version, tname1, &tguid1, sizeof(tdata1) - 1, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_BUFFER_TOO_SMALL); @@ -865,7 +896,7 @@ static void test_get_variable_too_small(void) g_assert_cmpuint(data_len, ==, sizeof(tdata1)); } -static void test_query_variable_info(void) +MULTI_TEST(test_query_variable_info) { uint8_t *ptr; EFI_STATUS status; @@ -882,18 +913,18 @@ static void test_query_variable_info(void) memset(longname->data, 'a', dstring_data_size(longname)); /* Check the defined limits with no variables. */ - call_query_variable_info(); + call_query_variable_info(version); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); g_assert_cmpuint(TOTAL_LIMIT, ==, unserialize_uintn(&ptr)); g_assert_cmpuint(TOTAL_LIMIT, ==, unserialize_uintn(&ptr)); - g_assert_cmpuint(DATA_LIMIT, ==, unserialize_uintn(&ptr)); + g_assert_cmpuint(data_limit(version), ==, unserialize_uintn(&ptr)); - sv_ok(longname, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, longname, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* Inserting a variable updates the limits correctly. */ - call_query_variable_info(); + call_query_variable_info(version); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -901,11 +932,11 @@ static void test_query_variable_info(void) g_assert_cmpuint(TOTAL_LIMIT - dstring_data_size(longname) - sizeof(tdata1) - VARIABLE_SIZE_OVERHEAD, ==, unserialize_uintn(&ptr)); - g_assert_cmpuint(DATA_LIMIT, ==, unserialize_uintn(&ptr)); + g_assert_cmpuint(data_limit(version), ==, unserialize_uintn(&ptr)); /* Updating a variable updates the limits correctly. */ - sv_ok(longname, &tguid1, tdata2, sizeof(tdata2), ATTR_B); - call_query_variable_info(); + sv_ok(version, longname, &tguid1, tdata2, sizeof(tdata2), ATTR_B); + call_query_variable_info(version); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -913,12 +944,12 @@ static void test_query_variable_info(void) g_assert_cmpuint(TOTAL_LIMIT - dstring_data_size(longname) - sizeof(tdata2) - VARIABLE_SIZE_OVERHEAD, ==, unserialize_uintn(&ptr)); - g_assert_cmpuint(DATA_LIMIT, ==, unserialize_uintn(&ptr)); + g_assert_cmpuint(data_limit(version), ==, unserialize_uintn(&ptr)); /* Appending to a variable updates the limits correctly. */ - sv_ok(longname, &tguid1, tdata1, sizeof(tdata1), + sv_ok(version, longname, &tguid1, tdata1, sizeof(tdata1), ATTR_B | EFI_VARIABLE_APPEND_WRITE); - call_query_variable_info(); + call_query_variable_info(version); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -926,21 +957,21 @@ static void test_query_variable_info(void) g_assert_cmpuint(TOTAL_LIMIT - dstring_data_size(longname) - sizeof(tdata2) - sizeof(tdata1) - VARIABLE_SIZE_OVERHEAD, ==, unserialize_uintn(&ptr)); - g_assert_cmpuint(DATA_LIMIT, ==, unserialize_uintn(&ptr)); + g_assert_cmpuint(data_limit(version), ==, unserialize_uintn(&ptr)); /* Deleting a variable updates the limits correctly. */ - sv_ok(longname, &tguid1, NULL, 0, ATTR_B); - call_query_variable_info(); + sv_ok(version, longname, &tguid1, NULL, 0, ATTR_B); + call_query_variable_info(version); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); g_assert_cmpuint(TOTAL_LIMIT, ==, unserialize_uintn(&ptr)); g_assert_cmpuint(TOTAL_LIMIT, ==, unserialize_uintn(&ptr)); - g_assert_cmpuint(DATA_LIMIT, ==, unserialize_uintn(&ptr)); + g_assert_cmpuint(data_limit(version), ==, unserialize_uintn(&ptr)); free_dstring(longname); } -static void test_get_next_variable_empty(void) +MULTI_TEST(test_get_next_variable_empty) { uint8_t *ptr; EFI_STATUS status; @@ -948,13 +979,13 @@ static void test_get_next_variable_empty(void) reset_vars(); /* No variables */ - call_get_next_variable(BSIZ, NULL, &nullguid, 0); + call_get_next_variable(version, BSIZ, NULL, &nullguid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); } -static void test_get_next_variable_long_name(void) +MULTI_TEST(test_get_next_variable_long_name) { uint8_t *ptr; EFI_STATUS status; @@ -965,14 +996,14 @@ static void test_get_next_variable_long_name(void) memset(tmp_name->data, 42, dstring_data_size(tmp_name)); /* Input name exceeds the limit */ - call_get_next_variable(BSIZ, tmp_name, &nullguid, 0); + call_get_next_variable(version, BSIZ, tmp_name, &nullguid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_DEVICE_ERROR); free_dstring(tmp_name); } -static void test_get_next_variable_only_runtime(void) +MULTI_TEST(test_get_next_variable_only_runtime) { uint8_t *ptr, *data; UINTN data_len; @@ -985,13 +1016,13 @@ static void test_get_next_variable_only_runtime(void) */ reset_vars(); - sv_ok(tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); + sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); - call_get_next_variable(BSIZ, NULL, &nullguid, 1); + call_get_next_variable(version, BSIZ, NULL, &nullguid, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1003,7 +1034,7 @@ static void test_get_next_variable_only_runtime(void) g_assert(!memcmp(&guid, &tguid4, GUID_LEN)); free(data); - call_get_next_variable(BSIZ, tname4, &guid, 1); + call_get_next_variable(version, BSIZ, tname4, &guid, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1014,26 +1045,26 @@ static void test_get_next_variable_only_runtime(void) g_assert(!memcmp(&guid, &tguid2, GUID_LEN)); free(data); - call_get_next_variable(BSIZ, tname2, &guid, 1); + call_get_next_variable(version, BSIZ, tname2, &guid, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); } -static void test_get_next_variable_too_small(void) +MULTI_TEST(test_get_next_variable_too_small) { uint8_t *ptr; UINTN data_len; EFI_STATUS status; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* * If the output buffer is too small, check that the correct size is * returned. */ - call_get_next_variable(0, NULL, &nullguid, 0); + call_get_next_variable(version, 0, NULL, &nullguid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_BUFFER_TOO_SMALL); @@ -1041,7 +1072,7 @@ static void test_get_next_variable_too_small(void) g_assert_cmpuint(data_len, ==, dstring_data_size(tname1) + sizeof(CHAR16)); } -static void test_get_next_variable_no_match(void) +MULTI_TEST(test_get_next_variable_no_match) { uint8_t *ptr, *data; UINTN data_len; @@ -1049,11 +1080,11 @@ static void test_get_next_variable_no_match(void) EFI_GUID guid; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); /* First variable is retrieved successfully. */ - call_get_next_variable(BSIZ, NULL, &nullguid, 0); + call_get_next_variable(version, BSIZ, NULL, &nullguid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1064,13 +1095,13 @@ static void test_get_next_variable_no_match(void) g_assert(!memcmp(&guid, &tguid2, GUID_LEN)); /* Check when an incorrect name is passed in. */ - call_get_next_variable(BSIZ, tname4, &guid, 0); + call_get_next_variable(version, BSIZ, tname4, &guid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Check when an incorrect guid is passed in. */ - call_get_next_variable(BSIZ, tname2, &tguid4, 0); + call_get_next_variable(version, BSIZ, tname2, &tguid4, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); @@ -1078,7 +1109,7 @@ static void test_get_next_variable_no_match(void) free(data); } -static void test_get_next_variable_all(void) +MULTI_TEST(test_get_next_variable_all) { uint8_t *ptr, *data; UINTN data_len; @@ -1091,13 +1122,13 @@ static void test_get_next_variable_all(void) */ reset_vars(); - sv_ok(tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); + sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); - call_get_next_variable(BSIZ, NULL, &nullguid, 0); + call_get_next_variable(version, BSIZ, NULL, &nullguid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1107,7 +1138,7 @@ static void test_get_next_variable_all(void) unserialize_guid(&ptr, &guid); g_assert(!memcmp(&guid, &tguid3, GUID_LEN)); - call_get_next_variable(BSIZ, tname3, &guid, 0); + call_get_next_variable(version, BSIZ, tname3, &guid, 0); free(data); ptr = buf; status = unserialize_uintn(&ptr); @@ -1119,7 +1150,7 @@ static void test_get_next_variable_all(void) g_assert(!memcmp(&guid, &tguid4, GUID_LEN)); free(data); - call_get_next_variable(BSIZ, tname4, &guid, 0); + call_get_next_variable(version, BSIZ, tname4, &guid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1129,7 +1160,7 @@ static void test_get_next_variable_all(void) unserialize_guid(&ptr, &guid); g_assert(!memcmp(&guid, &tguid1, GUID_LEN)); - call_get_next_variable(BSIZ, tname1, &guid, 0); + call_get_next_variable(version, BSIZ, tname1, &guid, 0); free(data); ptr = buf; @@ -1142,7 +1173,7 @@ static void test_get_next_variable_all(void) g_assert(!memcmp(&guid, &tguid2, GUID_LEN)); free(data); - call_get_next_variable(BSIZ, tname2, &guid, 0); + call_get_next_variable(version, BSIZ, tname2, &guid, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1152,14 +1183,14 @@ static void test_get_next_variable_all(void) unserialize_guid(&ptr, &guid); g_assert(!memcmp(&guid, &tguid5, GUID_LEN)); - call_get_next_variable(BSIZ, tname5, &guid, 0); + call_get_next_variable(version, BSIZ, tname5, &guid, 0); free(data); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); } -static void test_set_variable_attr(void) +MULTI_TEST(test_set_variable_attr) { uint8_t *ptr; EFI_STATUS status; @@ -1168,39 +1199,39 @@ static void test_set_variable_attr(void) setup_variables(); /* hardware error record is not supported */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B | EFI_VARIABLE_HARDWARE_ERROR_RECORD, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* authenticated write access is not supported */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_UNSUPPORTED); /* runtime without boottime access is invalid */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_R, 0); + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_R, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Setting boottime variables at runtime is not supported */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B, 1); + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Set a volatile variable at runtime fails */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_BR, 1); + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_BR, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Set a variable at runtime without runtime access fails */ - call_set_variable(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B, 1); + call_set_variable(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); @@ -1210,13 +1241,13 @@ static void test_set_variable_attr(void) * EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS attribute are set in a * SetVariable() call, then the firmware must return EFI_INVALID_PARAMETER. */ - sign_and_check(tname2, &tguid1, + sign_and_check(version, tname2, &tguid1, ATTR_B_TIME | EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS, &test_timea, tdata1, sizeof(tdata1), &sign_testPK, EFI_INVALID_PARAMETER); } -static void test_set_variable_set(void) +MULTI_TEST(test_set_variable_set) { uint8_t *ptr, *data; UINTN data_len; @@ -1226,20 +1257,20 @@ static void test_set_variable_set(void) reset_vars(); /* Basic SetVariable usage. */ - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); /* Set an NV variable at runtime */ - call_set_variable(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BRNV, 1); + call_set_variable(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BRNV, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); /* Set an NV variable at boottime */ - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); /* Access boottime variable at boottime */ - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1251,7 +1282,7 @@ static void test_set_variable_set(void) free(data); /* Access BR variable at boottime */ - call_get_variable(tname2, &tguid2, BSIZ, 0); + call_get_variable(version, tname2, &tguid2, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1263,7 +1294,7 @@ static void test_set_variable_set(void) free(data); /* Access runtime variable at runtime */ - call_get_variable(tname2, &tguid2, BSIZ, 1); + call_get_variable(version, tname2, &tguid2, BSIZ, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1275,7 +1306,7 @@ static void test_set_variable_set(void) free(data); /* Access runtime variable at runtime */ - call_get_variable(tname3, &tguid3, BSIZ, 1); + call_get_variable(version, tname3, &tguid3, BSIZ, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1287,7 +1318,7 @@ static void test_set_variable_set(void) free(data); /* Access NV runtime variable at runtime */ - call_get_variable(tname3, &tguid3, BSIZ, 1); + call_get_variable(version, tname3, &tguid3, BSIZ, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1299,7 +1330,7 @@ static void test_set_variable_set(void) free(data); /* Access NV boottime variable at boottime */ - call_get_variable(tname4, &tguid4, BSIZ, 0); + call_get_variable(version, tname4, &tguid4, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1311,7 +1342,7 @@ static void test_set_variable_set(void) free(data); } -static void test_set_variable_update(void) +MULTI_TEST(test_set_variable_update) { uint8_t *ptr, *data; UINTN data_len; @@ -1321,15 +1352,15 @@ static void test_set_variable_update(void) reset_vars(); /* Insert a variable... */ - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* ... and check it can be updated */ - sv_ok(tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_B); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); + sv_ok(version, tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_B); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); /* Check the update worked. */ - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1341,25 +1372,25 @@ static void test_set_variable_update(void) free(data); /* Cannot change attributes */ - call_set_variable(tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_BR, 0); + call_set_variable(version, tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_BR, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Updating a volatile variable at runtime fails */ - call_set_variable(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR, 1); + call_set_variable(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_WRITE_PROTECTED); /* Updating a variable at runtime without runtime access fails */ - call_set_variable(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV, 1); + call_set_variable(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); } -static void test_set_variable_append(void) +MULTI_TEST(test_set_variable_append) { uint8_t *ptr, *data; UINTN data_len; @@ -1369,19 +1400,19 @@ static void test_set_variable_append(void) reset_vars(); /* Insert some variables */ - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV); /* Append 0 bytes must not delete the variable */ - sv_ok(tname1, &tguid1, NULL, 0, ATTR_B | EFI_VARIABLE_APPEND_WRITE); + sv_ok(version, tname1, &tguid1, NULL, 0, ATTR_B | EFI_VARIABLE_APPEND_WRITE); /* Append data to the variable */ - sv_ok(tname1, &tguid1, tdata2, sizeof(tdata2), + sv_ok(version, tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_B | EFI_VARIABLE_APPEND_WRITE); /* Verify the contents are a concatenation */ - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1394,34 +1425,34 @@ static void test_set_variable_append(void) free(data); /* Appending to a volatile variable at runtime fails */ - call_set_variable(tname3, &tguid3, tdata3, sizeof(tdata3), + call_set_variable(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR | EFI_VARIABLE_APPEND_WRITE, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_WRITE_PROTECTED); /* Appending to a variable at runtime without runtime access fails */ - call_set_variable(tname4, &tguid4, tdata4, sizeof(tdata4), + call_set_variable(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BNV | EFI_VARIABLE_APPEND_WRITE, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); } -static void test_set_variable_delete(void) +MULTI_TEST(test_set_variable_delete) { uint8_t *ptr; EFI_STATUS status; reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); - sv_ok(tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_BNV); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BR); + sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_BNV); /* Deleting a non-existent variable at boottime fails (by setting no data) */ - call_set_variable(tname4, &tguid4, NULL, 0, ATTR_B, 0); + call_set_variable(version, tname4, &tguid4, NULL, 0, ATTR_B, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); @@ -1430,13 +1461,13 @@ static void test_set_variable_delete(void) * Deleting a non-existent variable at boottime fails (by setting no * access attributes) */ - call_set_variable(tname4, &tguid4, tdata4, sizeof(tdata4), 0, 0); + call_set_variable(version, tname4, &tguid4, tdata4, sizeof(tdata4), 0, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Deleting a non-existent variable at runtime fails (by setting no data) */ - call_set_variable(tname4, &tguid4, NULL, 0, ATTR_BRNV, 0); + call_set_variable(version, tname4, &tguid4, NULL, 0, ATTR_BRNV, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); @@ -1445,106 +1476,108 @@ static void test_set_variable_delete(void) * Deleting a non-existent variable at runtime fails (by setting no access * attributes) */ - call_set_variable(tname4, &tguid4, tdata4, sizeof(tdata4), 0, 0); + call_set_variable(version, tname4, &tguid4, tdata4, sizeof(tdata4), 0, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Delete by setting no data */ - sv_ok(tname1, &tguid1, NULL, 0, ATTR_B); + sv_ok(version, tname1, &tguid1, NULL, 0, ATTR_B); /* Verify it is gone */ - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Delete by setting no access attributes */ - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), 0); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), 0); /* Verify it is gone */ - call_get_variable(tname2, &tguid2, BSIZ, 0); + call_get_variable(version, tname2, &tguid2, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Deleting a volatile variable at runtime fails */ - call_set_variable(tname3, &tguid3, NULL, 0, ATTR_BR, 1); + call_set_variable(version, tname3, &tguid3, NULL, 0, ATTR_BR, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_WRITE_PROTECTED); /* Deleting a variable at runtime without runtime access fails */ - call_set_variable(tname5, &tguid5, NULL, 0, ATTR_B, 1); + call_set_variable(version, tname5, &tguid5, NULL, 0, ATTR_B, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); /* Deleting a variable at runtime by setting attributes to 0 succeeds */ - sv_ok(tname5, &tguid5, NULL, 0, ATTR_BNV); /* Remove old variable */ + sv_ok(version, tname5, &tguid5, NULL, 0, ATTR_BNV); /* Remove old variable */ /* Insert it with different attr */ - sv_ok(tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_BRNV); + sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_BRNV); /* Then delete it at runtime */ - call_set_variable(tname5, &tguid5, tdata5, sizeof(tdata5), 0, 1); + call_set_variable(version, tname5, &tguid5, tdata5, sizeof(tdata5), 0, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); } -static void test_set_variable_resource_limit(void) +MULTI_TEST(test_set_variable_resource_limit) { uint8_t *ptr; EFI_STATUS status; - UINTN remaining; + UINTN remaining = TOTAL_LIMIT; uint8_t tmp[TOTAL_LIMIT] = {0}; reset_vars(); /* Check per-variable limit */ - call_set_variable(tname1, &tguid1, tmp, DATA_LIMIT + 1, ATTR_B, 0); + call_set_variable(version, tname1, &tguid1, tmp, data_limit(version) + 1, ATTR_B, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_OUT_OF_RESOURCES); - sv_ok(tname1, &tguid1, tmp, DATA_LIMIT, ATTR_B); + sv_ok(version, tname1, &tguid1, tmp, data_limit(version), ATTR_B); /* Cannot exceed DATA_LIMIT by appending */ - call_set_variable(tname1, &tguid1, tmp, 1, ATTR_B | EFI_VARIABLE_APPEND_WRITE, 0); + call_set_variable(version, tname1, &tguid1, tmp, 1, ATTR_B | EFI_VARIABLE_APPEND_WRITE, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_OUT_OF_RESOURCES); - sv_ok(tname4, &tguid4, tmp, DATA_LIMIT, ATTR_B); + remaining -= data_limit(version) + dstring_data_size(tname1) + VARIABLE_SIZE_OVERHEAD; + if (version == 1) { + sv_ok(version, tname4, &tguid4, tmp, data_limit(version), ATTR_B); + remaining -= data_limit(version) + dstring_data_size(tname4) + VARIABLE_SIZE_OVERHEAD; + } /* Use all the remaining space */ - remaining = TOTAL_LIMIT - 2 * DATA_LIMIT - dstring_data_size(tname1) - - dstring_data_size(tname4) - - dstring_data_size(tname2) - 3 * VARIABLE_SIZE_OVERHEAD; - sv_ok(tname2, &tguid2, tmp, remaining, ATTR_B); + remaining -= dstring_data_size(tname2) + VARIABLE_SIZE_OVERHEAD; + sv_ok(version, tname2, &tguid2, tmp, remaining, ATTR_B); /* Cannot use any more space with a new variable */ - call_set_variable(tname3, &tguid3, tmp, 1, ATTR_B, 0); + call_set_variable(version, tname3, &tguid3, tmp, 1, ATTR_B, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_OUT_OF_RESOURCES); /* Cannot use any more by appending */ - call_set_variable(tname1, &tguid1, tmp, 1, ATTR_B | EFI_VARIABLE_APPEND_WRITE, 0); + call_set_variable(version, tname1, &tguid1, tmp, 1, ATTR_B | EFI_VARIABLE_APPEND_WRITE, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_OUT_OF_RESOURCES); /* Cannot use any more by replacing */ - call_set_variable(tname2, &tguid2, tmp, remaining + 1, ATTR_B, 0); + call_set_variable(version, tname2, &tguid2, tmp, remaining + 1, ATTR_B, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_OUT_OF_RESOURCES); /* Can update without exceeding the limit */ - sv_ok(tname1, &tguid1, tmp, DATA_LIMIT, ATTR_B); + sv_ok(version, tname1, &tguid1, tmp, data_limit(version), ATTR_B); } -static void test_set_variable_many_vars(void) +MULTI_TEST(test_set_variable_many_vars) { uint8_t *ptr; EFI_STATUS status; @@ -1560,7 +1593,7 @@ static void test_set_variable_many_vars(void) /* Set more variables than are allowed based on the variable "overhead". */ for (i = 0; i < count + 1; i++) { sprintf(name, "%04d", i); - call_set_variable(dname, &tguid1, &tmp, 1, ATTR_B, 0); + call_set_variable(version, dname, &tguid1, &tmp, 1, ATTR_B, 0); ptr = buf; status = unserialize_uintn(&ptr); if (i == count) @@ -1571,7 +1604,7 @@ static void test_set_variable_many_vars(void) free_dstring(dname); } -static void test_set_variable_non_volatile(void) +MULTI_TEST(test_set_variable_non_volatile) { uint8_t *ptr, *data; EFI_STATUS status; @@ -1580,15 +1613,15 @@ static void test_set_variable_non_volatile(void) remove(save_name); reset_vars(); - sv_ok(tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_BNV); - sv_ok(tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); - sv_ok(tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BRNV); - sv_ok(tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); + sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_BNV); + sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); + sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BRNV); + sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); reset_vars(); db->init(); - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1599,7 +1632,7 @@ static void test_set_variable_non_volatile(void) g_assert(!memcmp(tdata1, data, data_len)); free(data); - call_get_variable(tname3, &tguid3, BSIZ, 0); + call_get_variable(version, tname3, &tguid3, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1610,23 +1643,23 @@ static void test_set_variable_non_volatile(void) g_assert(!memcmp(tdata3, data, data_len)); free(data); - call_get_variable(tname2, &tguid2, BSIZ, 0); + call_get_variable(version, tname2, &tguid2, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); - call_get_variable(tname4, &tguid4, BSIZ, 0); + call_get_variable(version, tname4, &tguid4, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); /* Update, reload & check */ - sv_ok(tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_BNV); + sv_ok(version, tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_BNV); reset_vars(); db->init(); - call_get_variable(tname1, &tguid1, BSIZ, 0); + call_get_variable(version, tname1, &tguid1, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1638,7 +1671,7 @@ static void test_set_variable_non_volatile(void) free(data); /* Append, reload & check */ - call_set_variable(tname3, &tguid3, tdata4, sizeof(tdata4), + call_set_variable(version, tname3, &tguid3, tdata4, sizeof(tdata4), ATTR_BRNV | EFI_VARIABLE_APPEND_WRITE, 1); ptr = buf; status = unserialize_uintn(&ptr); @@ -1647,7 +1680,7 @@ static void test_set_variable_non_volatile(void) reset_vars(); db->init(); - call_get_variable(tname3, &tguid3, BSIZ, 0); + call_get_variable(version, tname3, &tguid3, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1660,7 +1693,7 @@ static void test_set_variable_non_volatile(void) free(data); /* Delete, reload & check */ - call_set_variable(tname3, &tguid3, NULL, 0, ATTR_BRNV, 1); + call_set_variable(version, tname3, &tguid3, NULL, 0, ATTR_BRNV, 1); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1668,13 +1701,13 @@ static void test_set_variable_non_volatile(void) reset_vars(); db->init(); - call_get_variable(tname3, &tguid3, BSIZ, 0); + call_get_variable(version, tname3, &tguid3, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_NOT_FOUND); } -static void test_set_variable_special_vars(void) +MULTI_TEST(test_set_variable_special_vars) { int i; dstring *vars[] = {auditMode_name, deployedMode_name, setupMode_name, @@ -1693,16 +1726,16 @@ static void test_set_variable_special_vars(void) /* Check that special variables cannot be set/appended to. */ for (j = 0; j < ARRAY_SIZE(attrs); j++) { - sv_check(vars[i], &gEfiGlobalVariableGuid, + sv_check(version, vars[i], &gEfiGlobalVariableGuid, tdata1, sizeof(tdata1), attrs[j], EFI_WRITE_PROTECTED); } /* Check that special variables cannot be removed. */ - sv_check(vars[i], &gEfiGlobalVariableGuid, + sv_check(version, vars[i], &gEfiGlobalVariableGuid, NULL, 0, ATTR_BR, EFI_WRITE_PROTECTED); /* Check that special variables exist and their attr are correct. */ - call_get_variable(vars[i], &gEfiGlobalVariableGuid, BSIZ, 0); + call_get_variable(version, vars[i], &gEfiGlobalVariableGuid, BSIZ, 0); status = unserialize_uintn(&ptr); /* status */ g_assert_cmpuint(status, ==, EFI_SUCCESS); @@ -1711,7 +1744,7 @@ static void test_set_variable_special_vars(void) } /* AuditMode must always be 0 in our implementation. */ - check_variable_data(auditMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, auditMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); /* @@ -1722,7 +1755,7 @@ static void test_set_variable_special_vars(void) */ } -static void test_set_variable_mor(void) +MULTI_TEST(test_set_variable_mor) { uint8_t data[] = {0, 0}; uint8_t expected = 0; @@ -1732,96 +1765,96 @@ static void test_set_variable_mor(void) setup_mor_variables(); /* Check initial value of morControl is 0 */ - check_variable_data(morControl_name, &morControlGuid, BSIZ, 0, + check_variable_data(version, morControl_name, &morControlGuid, BSIZ, 0, &expected, 1); /* Storing more than 1 byte in morControl fails */ - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 2, ATTR_BRNV, EFI_INVALID_PARAMETER); /* Deleting morControl fails */ - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 0, ATTR_BRNV, EFI_INVALID_PARAMETER); - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 1, 0, EFI_INVALID_PARAMETER); /* Storing with invalid attributes in morControl fails */ - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 1, ATTR_B, EFI_INVALID_PARAMETER); - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 1, ATTR_BRNV | EFI_VARIABLE_APPEND_WRITE, EFI_INVALID_PARAMETER); /* Setting reserved bits in morControl fails */ data[0] = 0x2e; - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 1, ATTR_BRNV, EFI_INVALID_PARAMETER); /* Setting allowed bits in morControl works */ data[0] = 0x11; - sv_ok(morControl_name, &morControlGuid, + sv_ok(version, morControl_name, &morControlGuid, data, 1, ATTR_BRNV); /* Check morControl is now set */ expected = 0x11; - check_variable_data(morControl_name, &morControlGuid, BSIZ, 0, + check_variable_data(version, morControl_name, &morControlGuid, BSIZ, 0, &expected, 1); /* Check morControlLock is 0 (unlocked) at startup */ expected = 0; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Storing something other than 1 or 8 bytes in morControlLock fails */ data[0] = 0; - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 2, ATTR_BRNV, EFI_INVALID_PARAMETER); /* Deleting morControlLock fails */ - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 0, ATTR_BRNV, EFI_WRITE_PROTECTED); - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, 0, EFI_WRITE_PROTECTED); /* Storing with invalid attributes in morControlLock fails */ - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_B, EFI_INVALID_PARAMETER); - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV | EFI_VARIABLE_APPEND_WRITE, EFI_INVALID_PARAMETER); /* Unlock when already unlocked is a no-op */ - sv_ok(morControlLock_name, &morControlLockGuid, + sv_ok(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV); /* Storing an invalid value in morControlLock fails */ data[0] = 2; - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV, EFI_INVALID_PARAMETER); /* Locking without a key succeeds */ data[0] = 1; - sv_ok(morControlLock_name, &morControlLockGuid, + sv_ok(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV); /* morControlLock is now 1 (locked) */ expected = 1; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Setting morControl when locked fails */ - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, data, 1, ATTR_BRNV, EFI_WRITE_PROTECTED); /* Locking when already locked fails */ - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV, EFI_ACCESS_DENIED); /* Unlocking when locked without a key fails */ data[0] = 0; - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 1, ATTR_BRNV, EFI_ACCESS_DENIED); } -static void test_set_variable_mor_key(void) +MULTI_TEST(test_set_variable_mor_key) { uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8}; uint8_t mor_control_data = 1; @@ -1833,70 +1866,70 @@ static void test_set_variable_mor_key(void) /* morControlLock is initialized to 0 (unlocked) */ expected = 0; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Setting morControl when unlocked succeeds */ - sv_ok(morControl_name, &morControlGuid, + sv_ok(version, morControl_name, &morControlGuid, &mor_control_data, 1, ATTR_BRNV); /* Locking with a key succeeds */ - sv_ok(morControlLock_name, &morControlLockGuid, + sv_ok(version, morControlLock_name, &morControlLockGuid, data, 8, ATTR_BRNV); /* morControlLock is now 2 (locked with key) */ expected = 2; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Setting morControl when locked fails */ - sv_check(morControl_name, &morControlGuid, + sv_check(version, morControl_name, &morControlGuid, &mor_control_data, 1, ATTR_BRNV, EFI_WRITE_PROTECTED); /* Unlocking with the correct key works */ - sv_ok(morControlLock_name, &morControlLockGuid, + sv_ok(version, morControlLock_name, &morControlLockGuid, data, 8, ATTR_BRNV); /* morControlLock is now 0 (unlocked) */ expected = 0; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Setting morControl when unlocked succeeds again */ mor_control_data = 0; - sv_ok(morControl_name, &morControlGuid, + sv_ok(version, morControl_name, &morControlGuid, &mor_control_data, 1, ATTR_BRNV); /* Relock it */ - sv_ok(morControlLock_name, &morControlLockGuid, + sv_ok(version, morControlLock_name, &morControlLockGuid, data, 8, ATTR_BRNV); /* Unlocking with an incorrect key fails */ data[0] = 0; - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 8, ATTR_BRNV, EFI_ACCESS_DENIED); /* morControlLock is now 1 (locked without key) */ expected = 1; - check_variable_data(morControlLock_name, &morControlLockGuid, BSIZ, 0, + check_variable_data(version, morControlLock_name, &morControlLockGuid, BSIZ, 0, &expected, 1); /* Unlocking when locked without key fails */ data[0] = 1; - sv_check(morControlLock_name, &morControlLockGuid, + sv_check(version, morControlLock_name, &morControlLockGuid, data, 8, ATTR_BRNV, EFI_ACCESS_DENIED); } -static void set_usermode(void) +static void set_usermode(UINT32 version) { /* Move into user mode by enrolling Platform Key. */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timea, (uint8_t *)certPK, certPK_len, &sign_testPK, EFI_SUCCESS); } -static void test_use_bad_digest(void) +MULTI_TEST(test_use_bad_digest) { /* * Attempt bad digest @@ -1909,11 +1942,11 @@ static void test_use_bad_digest(void) reset_vars(); setup_variables(); - sign_and_check(tname1, &tguid1, ATTR_B_TIME, &test_timea, tdata1, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timea, tdata1, sizeof(tdata1), &sign_bad_digest, EFI_SECURITY_VIOLATION); } -static void test_set_secure_variable(void) +static void test_set_secure_variable(UINT32 version) { const char tosign_data[] = "testdata"; const char tosign2_data[] = "|appended"; @@ -1925,25 +1958,25 @@ static void test_set_secure_variable(void) int len; /* 1. Create a EFI_VARIABLE_AUTHENTICATION_2 variable */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME, &test_timea, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timea, (uint8_t *)tosign_data, strlen((char *)tosign_data), &sign_testPK, EFI_SUCCESS); - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_data, strlen(tosign_data)); /* 2. try append */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME | EFI_VARIABLE_APPEND_WRITE, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME | EFI_VARIABLE_APPEND_WRITE, &test_timeb, (uint8_t *)tosign2_data, strlen(tosign2_data), &sign_testPK, EFI_SUCCESS); - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended_data, strlen(tosign_appended_data)); /* 3. Try append with older time stamp (is meant to work!) */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME | EFI_VARIABLE_APPEND_WRITE, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME | EFI_VARIABLE_APPEND_WRITE, &test_timea, (uint8_t *)tosign3_data, strlen(tosign3_data), &sign_testPK, EFI_SUCCESS); - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended2_data, strlen(tosign_appended2_data)); @@ -1951,10 +1984,10 @@ static void test_set_secure_variable(void) * Try updating, at time b. This shouldn't work. (appends with bad times, * don't update the time */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME, &test_timeb, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timeb, (uint8_t *)tosign3_data, strlen(tosign3_data), &sign_testPK, EFI_SECURITY_VIOLATION); - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended2_data, strlen(tosign_appended2_data)); @@ -1968,22 +2001,22 @@ static void test_set_secure_variable(void) ATTR_B_TIME, &test_timec, (uint8_t *)tosign2_data, strlen(tosign2_data), &sign_testPK); assert(len); - sv_check(tname1, &tguid1, sign_buffer, len, ATTR_B_TIME | + sv_check(version, tname1, &tguid1, sign_buffer, len, ATTR_B_TIME | EFI_VARIABLE_APPEND_WRITE, EFI_SECURITY_VIOLATION); free(sign_buffer); /* check it's the same */ - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended2_data, strlen(tosign_appended2_data)); /* 4. Try updating with wrong key */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME, &test_timec, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timec, (uint8_t *)tosign2_data, strlen(tosign2_data), &sign_certB, EFI_SECURITY_VIOLATION); /* check it's the same */ - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended2_data, strlen(tosign_appended2_data)); @@ -1991,21 +2024,21 @@ static void test_set_secure_variable(void) * Check authenticated variables cannot be deleted by unsetting attriubtes * (2.7A page 248) */ - sign_and_check(tname1, &tguid1, 0, &test_timec, + sign_and_check(version, tname1, &tguid1, 0, &test_timec, NULL, 0, &sign_testPK, EFI_INVALID_PARAMETER); /* Check variable is the same */ - check_variable_data(tname1, &tguid1, BSIZ, 0, + check_variable_data(version, tname1, &tguid1, BSIZ, 0, (uint8_t *)tosign_appended2_data, strlen(tosign_appended2_data)); /* try deleting */ - sign_and_check(tname1, &tguid1, ATTR_B_TIME, &test_timec, NULL, 0, + sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timec, NULL, 0, &sign_testPK, EFI_SUCCESS); } -static void test_secure_set_variable_setupmode(void) +MULTI_TEST(test_secure_set_variable_setupmode) { reset_vars(); setup_variables(); @@ -2016,35 +2049,35 @@ static void test_secure_set_variable_setupmode(void) * operating in setup mode. */ - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - test_set_secure_variable(); + test_set_secure_variable(version); } -static void test_secure_set_variable_usermode(void) +MULTI_TEST(test_secure_set_variable_usermode) { reset_vars(); setup_variables(); /* Check we're in setup mode */ - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - set_usermode(); + set_usermode(version); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - test_set_secure_variable(); + test_set_secure_variable(version); } -static void test_secure_set_PK(void) +MULTI_TEST(test_secure_set_PK) { EFI_SIGNATURE_LIST *joint_cert; size_t joint_len; @@ -2054,77 +2087,77 @@ static void test_secure_set_PK(void) reset_vars(); setup_variables(); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); /* try cert, signed by someone unknown. Should be no mode change. */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timea, (uint8_t *)certPK, certPK_len, &sign_certB, EFI_SECURITY_VIOLATION); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); /* try setting PK, with wrong attributes */ - sv_check(PK_name, &gEfiGlobalVariableGuid, (uint8_t *)certPK, certPK_len, + sv_check(version, PK_name, &gEfiGlobalVariableGuid, (uint8_t *)certPK, certPK_len, ATTR_BRNV, EFI_INVALID_PARAMETER); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, &test_timea, (uint8_t *)certPK, certPK_len, &sign_certB, EFI_NOT_FOUND); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_B_TIME | EFI_VARIABLE_RUNTIME_ACCESS, &test_timea, (uint8_t *)certPK, certPK_len, &sign_certB, EFI_INVALID_PARAMETER); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_B_TIME | EFI_VARIABLE_NON_VOLATILE, &test_timea, (uint8_t *)certPK, certPK_len, &sign_certB, EFI_INVALID_PARAMETER); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, &test_timea, (uint8_t *)certPK, certPK_len, &sign_certB, EFI_INVALID_PARAMETER); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); /* Set PK, self signed - should move to user mode */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timea, (uint8_t *)certPK, certPK_len, &sign_testPK, EFI_SUCCESS); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); /* new cert, signed by self */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, (uint8_t *)certB, certB_len, &sign_certB, EFI_SECURITY_VIOLATION); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); /* @@ -2136,40 +2169,40 @@ static void test_secure_set_PK(void) uint8_t *buf = calloc(i, 1); assert(buf); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, buf, i, &sign_testPK, EFI_INVALID_PARAMETER); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); memcpy(buf, certB, i); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, buf, i, &sign_testPK, EFI_INVALID_PARAMETER); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); free(buf); } /* Try appending a second cert - should not work */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, &test_timeb, (uint8_t *)certB, certB_len, &sign_testPK, EFI_INVALID_PARAMETER); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); /* Try setting two keys in one write, two lists */ @@ -2181,7 +2214,7 @@ static void test_secure_set_PK(void) memcpy(ptr, certB, certB_len); joint_len = certPK_len + certB_len; - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, (uint8_t *)joint_cert, joint_len, &sign_testPK, EFI_INVALID_PARAMETER); @@ -2191,7 +2224,7 @@ static void test_secure_set_PK(void) char *cert_list[] = {"testPK.pem", "testcertB.pem", NULL}; read_x509_list_into_CertList(cert_list, &joint_cert, &joint_len); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, (uint8_t *)joint_cert, joint_len, &sign_testPK, EFI_INVALID_PARAMETER); @@ -2199,40 +2232,40 @@ static void test_secure_set_PK(void) free(joint_cert); /* new cert, signed by previous */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, (uint8_t *)certB, certB_len, &sign_testPK, EFI_SUCCESS); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); /* delete it, with first (should fail) */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timec, NULL, 0, &sign_testPK, EFI_SECURITY_VIOLATION); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); /* delete it, with second */ - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timec, NULL, 0, &sign_certB, EFI_SUCCESS); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - sign_and_check(PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, PK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_timeb, NULL, 0, &sign_testPK, EFI_NOT_FOUND); } -static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, +static void sig_db_check(UINT32 version, const dstring *key_db, const EFI_GUID *guid, EFI_TIME *time, bool setup) { uint8_t *ptr, *data, *data_ptr; @@ -2245,32 +2278,32 @@ static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, uint8_t *securemode = setup ? (uint8_t *)"\0" : (uint8_t *)"\1"; /* Check SetupMode and SecureBoot vars are as expected */ - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, setupmode, 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, deployedmode, 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, securemode, 1); - sv_check(key_db, guid, (uint8_t *)certA, certA_len, + sv_check(version, key_db, guid, (uint8_t *)certA, certA_len, ATTR_BRNV, EFI_INVALID_PARAMETER); - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, time, (uint8_t *)certA, certA_len, &sign_certB, EFI_NOT_FOUND); - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, ATTR_B_TIME | EFI_VARIABLE_RUNTIME_ACCESS, time, (uint8_t *)certA, certA_len, &sign_certB, EFI_INVALID_PARAMETER); - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, ATTR_B_TIME | EFI_VARIABLE_NON_VOLATILE, time, (uint8_t *)certA, certA_len, &sign_certB, EFI_INVALID_PARAMETER); - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, time, (uint8_t *)certA, certA_len, @@ -2285,13 +2318,13 @@ static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, uint8_t *buf = calloc(i, 1); assert(buf); - sign_and_check(key_db, guid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, buf, i, &sign_testPK, EFI_INVALID_PARAMETER); memcpy(buf, certA, i); - sign_and_check(key_db, guid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, buf, i, &sign_testPK, EFI_INVALID_PARAMETER); @@ -2299,56 +2332,56 @@ static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, } /* try a valid signed cert, but not signed by PK */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certB, certB_len, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certB, certB_len, &sign_certB, setup ? EFI_SUCCESS : EFI_SECURITY_VIOLATION); if (setup) { time->Second++; - check_variable_data(key_db, guid, BSIZ, 0, (uint8_t *)certB, certB_len); + check_variable_data(version, key_db, guid, BSIZ, 0, (uint8_t *)certB, certB_len); } /* try a cert, signed by someone unknown. */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certA, certA_len, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certA, certA_len, &sign_certB, setup ? EFI_SUCCESS : EFI_SECURITY_VIOLATION); if (setup) { time->Second++; - check_variable_data(key_db, guid, BSIZ, 0, (uint8_t *)certA, certA_len); + check_variable_data(version, key_db, guid, BSIZ, 0, (uint8_t *)certA, certA_len); } /* SetupMode and SecureBoot vars should not have changed */ - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, setupmode, 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, deployedmode, 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, securemode, 1); if (!setup) { /* try a valid cert */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certA, certA_len, &sign_testPK, EFI_SUCCESS); time->Second++; - check_variable_data(key_db, guid, BSIZ, 0, (uint8_t *)certA, certA_len); + check_variable_data(version, key_db, guid, BSIZ, 0, (uint8_t *)certA, certA_len); } /* SetupMode and SecureBoot vars should not have changed */ - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, setupmode, 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, deployedmode, 1); - check_variable_data(secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, secureBoot_name, &gEfiGlobalVariableGuid, BSIZ, 0, securemode, 1); /* Try appending another */ - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, time, (uint8_t *)certB, certB_len, &sign_certB, setup ? EFI_SUCCESS : EFI_SECURITY_VIOLATION); if (!setup) - sign_and_check(key_db, guid, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, time, (uint8_t *)certB, certB_len, &sign_testPK, EFI_SUCCESS); @@ -2356,7 +2389,7 @@ static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, time->Second++; /* Check it */ - call_get_variable(key_db, guid, certA_len + certB_len, 0); + call_get_variable(version, key_db, guid, certA_len + certB_len, 0); ptr = buf; status = unserialize_uintn(&ptr); vsd_assert_status("status", status, ==, EFI_SUCCESS); @@ -2368,21 +2401,21 @@ static void sig_db_check(const dstring *key_db, const EFI_GUID *guid, assert_cmpmem(data_ptr, data_len - certA_len, certB, certB_len); /* Delete */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_certB, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_certB, setup ? EFI_SUCCESS : EFI_SECURITY_VIOLATION); if (!setup) - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_testPK, EFI_SUCCESS); /* Try deleting again */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_testPK, EFI_NOT_FOUND); free(data); } -static void sig_db_check_multi(const dstring *key_db, const EFI_GUID *guid, +static void sig_db_check_multi(UINT32 version, const dstring *key_db, const EFI_GUID *guid, EFI_TIME *time) { EFI_SIGNATURE_LIST *combined_cert; @@ -2400,7 +2433,7 @@ static void sig_db_check_multi(const dstring *key_db, const EFI_GUID *guid, temp = *first_data; *first_data = -1; - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)combined_cert, combined_len, &sign_testPK, EFI_INVALID_PARAMETER); @@ -2412,18 +2445,18 @@ static void sig_db_check_multi(const dstring *key_db, const EFI_GUID *guid, temp = *second_data; *second_data = -1; - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)combined_cert, combined_len, &sign_testPK, EFI_INVALID_PARAMETER); *second_data = temp; - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)combined_cert, combined_len, &sign_testPK, EFI_SUCCESS); free(combined_cert); } -static void test_secure_set_KEK_setupmode(void) +MULTI_TEST(test_secure_set_KEK_setupmode) { uint8_t *ptr, *data, *data_ptr; EFI_STATUS status; @@ -2434,27 +2467,27 @@ static void test_secure_set_KEK_setupmode(void) reset_vars(); setup_variables(); - sig_db_check(KEK_name, &gEfiGlobalVariableGuid, &test_time, true); + sig_db_check(version, KEK_name, &gEfiGlobalVariableGuid, &test_time, true); /* new cert, signed by self */ - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certB, certB_len, &sign_certB, EFI_SUCCESS); test_time.Second++; - call_get_variable(KEK_name, &gEfiGlobalVariableGuid, BSIZ, 0); + call_get_variable(version, KEK_name, &gEfiGlobalVariableGuid, BSIZ, 0); ptr = buf; status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); /* Try appending a second cert */ - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, &test_time, (uint8_t *)certA, certA_len, &sign_testPK, EFI_SUCCESS); test_time.Second++; - call_get_variable(KEK_name, &gEfiGlobalVariableGuid, certA_len + certB_len, 0); + call_get_variable(version, KEK_name, &gEfiGlobalVariableGuid, certA_len + certB_len, 0); ptr = buf; status = unserialize_uintn(&ptr); vsd_assert_status("status", status, ==, EFI_SUCCESS); @@ -2468,16 +2501,16 @@ static void test_secure_set_KEK_setupmode(void) free(data); } -static void test_secure_set_KEK_usermode(void) +MULTI_TEST(test_secure_set_KEK_usermode) { EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; reset_vars(); setup_variables(); - set_usermode(); + set_usermode(version); - sig_db_check(KEK_name, &gEfiGlobalVariableGuid, &test_time, false); - sig_db_check_multi(KEK_name, &gEfiGlobalVariableGuid, &test_time); + sig_db_check(version, KEK_name, &gEfiGlobalVariableGuid, &test_time, false); + sig_db_check_multi(version, KEK_name, &gEfiGlobalVariableGuid, &test_time); } /* @@ -2486,7 +2519,7 @@ static void test_secure_set_KEK_usermode(void) * perform an append of EFI_SIGNATURE_DATA values that are already part of * the existing variable value. */ -static void test_duplicate_db(const dstring *key_db, const EFI_GUID *guid, +static void test_duplicate_db(UINT32 version, const dstring *key_db, const EFI_GUID *guid, EFI_TIME *time, bool setup) { uint8_t *ptr, *data, *data_ptr; @@ -2494,21 +2527,21 @@ static void test_duplicate_db(const dstring *key_db, const EFI_GUID *guid, UINTN data_len; UINT32 attr; - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, (uint8_t *)certA, certA_len, &sign_testPK, EFI_SUCCESS); time->Second++; - sign_and_check(key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, time, (uint8_t *)certB, certB_len, &sign_testPK, EFI_SUCCESS); time->Second++; - sign_and_check(key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, time, (uint8_t *)certA, certA_len, &sign_testPK, EFI_SUCCESS); /* Check DB - should be two items */ - call_get_variable(key_db, guid, certA_len * 2 + certB_len, 0); + call_get_variable(version, key_db, guid, certA_len * 2 + certB_len, 0); ptr = buf; status = unserialize_uintn(&ptr); vsd_assert_status("status", status, ==, EFI_SUCCESS); @@ -2520,50 +2553,50 @@ static void test_duplicate_db(const dstring *key_db, const EFI_GUID *guid, assert_cmpmem(data_ptr, data_len - certA_len, certB, certB_len); /* Check timestamp was updated */ - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_testPK, setup ? EFI_SUCCESS : EFI_SECURITY_VIOLATION); time->Second++; if (!setup) - sign_and_check(key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, + sign_and_check(version, key_db, guid, ATTR_BRNV_TIME, time, NULL, 0, &sign_testPK, EFI_SUCCESS); free(data); } -static void test_secure_set_db__setupmode(const dstring *key_db) +static void test_secure_set_db__setupmode(UINT32 version, const dstring *key_db) { EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; reset_vars(); setup_variables(); - check_variable_data(setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\1", 1); - check_variable_data(deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, + check_variable_data(version, deployedMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, (uint8_t *)"\0", 1); - sig_db_check(key_db, &gEfiImageSecurityDatabaseGuid, &test_time, true); + sig_db_check(version, key_db, &gEfiImageSecurityDatabaseGuid, &test_time, true); - test_duplicate_db(key_db, &gEfiImageSecurityDatabaseGuid, &test_time, true); + test_duplicate_db(version, key_db, &gEfiImageSecurityDatabaseGuid, &test_time, true); } -static void test_secure_set_db_setupmode(void) +MULTI_TEST(test_secure_set_db_setupmode) { - test_secure_set_db__setupmode(db_name); + test_secure_set_db__setupmode(version, db_name); } -static void test_secure_set_dbx_setupmode(void) +MULTI_TEST(test_secure_set_dbx_setupmode) { - test_secure_set_db__setupmode(dbx_name); + test_secure_set_db__setupmode(version, dbx_name); } -static void test_secure_set_dbt_setupmode(void) +MULTI_TEST(test_secure_set_dbt_setupmode) { - test_secure_set_db__setupmode(dbt_name); + test_secure_set_db__setupmode(version, dbt_name); } -static void test_secure_set_db__usermode(const dstring *key_db) +static void test_secure_set_db__usermode(UINT32 version, const dstring *key_db) { EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; EFI_SIGNATURE_LIST *combined_cert; @@ -2572,40 +2605,40 @@ static void test_secure_set_db__usermode(const dstring *key_db) reset_vars(); setup_variables(); - set_usermode(); + set_usermode(version); - sig_db_check(key_db, &gEfiImageSecurityDatabaseGuid, &test_time, false); - sig_db_check_multi(key_db, &gEfiImageSecurityDatabaseGuid, &test_time); + sig_db_check(version, key_db, &gEfiImageSecurityDatabaseGuid, &test_time, false); + sig_db_check_multi(version, key_db, &gEfiImageSecurityDatabaseGuid, &test_time); test_time.Second++; /* * Previous checked PK owner can add db key * Now check a KEK key can be used. */ - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certB, certB_len, &sign_testPK, EFI_SUCCESS); - sign_and_check(key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certA, certA_len, &sign_certB, EFI_SUCCESS); test_time.Second++; /* Try it as KEK second key (two lists) */ - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certPK, certPK_len, &sign_testPK, EFI_SUCCESS); - sign_and_check(key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certA, certA_len, &sign_certB, EFI_SECURITY_VIOLATION); - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME | EFI_VARIABLE_APPEND_WRITE, &test_time, (uint8_t *)certB, certB_len, &sign_testPK, EFI_SUCCESS); - sign_and_check(key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certA, certA_len, &sign_certB, EFI_SUCCESS); @@ -2613,35 +2646,41 @@ static void test_secure_set_db__usermode(const dstring *key_db) /* Try it as KEK second key (one list) */ read_x509_list_into_CertList(cert_list, &combined_cert, &combined_len); - sign_and_check(KEK_name, &gEfiGlobalVariableGuid, + sign_and_check(version, KEK_name, &gEfiGlobalVariableGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)combined_cert, combined_len, &sign_testPK, EFI_SUCCESS); - sign_and_check(key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, + sign_and_check(version, key_db, &gEfiImageSecurityDatabaseGuid, ATTR_BRNV_TIME, &test_time, (uint8_t *)certA, certA_len, &sign_certB, EFI_SUCCESS); test_time.Second++; - test_duplicate_db(key_db, &gEfiImageSecurityDatabaseGuid, &test_time, false); + test_duplicate_db(version, key_db, &gEfiImageSecurityDatabaseGuid, &test_time, false); free(combined_cert); } -static void test_secure_set_db_usermode(void) +MULTI_TEST(test_secure_set_db_usermode) { - test_secure_set_db__usermode(db_name); + test_secure_set_db__usermode(version, db_name); } -static void test_secure_set_dbx_usermode(void) +MULTI_TEST(test_secure_set_dbx_usermode) { - test_secure_set_db__usermode(dbx_name); + test_secure_set_db__usermode(version, dbx_name); } -static void test_secure_set_dbt_usermode(void) +MULTI_TEST(test_secure_set_dbt_usermode) { - test_secure_set_db__usermode(dbt_name); + test_secure_set_db__usermode(version, dbt_name); } +#define ADD_MULTI_TEST(test_name, test_func) \ + do { \ + g_test_add_func(test_name "_v1", test_func ## _v1); \ + g_test_add_func(test_name "_v2", test_func ## _v2); \ + } while (0); + int main(int argc, char **argv) { int r; @@ -2651,79 +2690,79 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - g_test_add_func("/test/get_variable/no_name", - test_get_variable_no_name); - g_test_add_func("/test/get_variable/long_name", - test_get_variable_long_name); - g_test_add_func("/test/get_variable/not_found", - test_get_variable_not_found); - g_test_add_func("/test/get_variable/found", - test_get_variable_found); - g_test_add_func("/test/get_variable/too_small", - test_get_variable_too_small); - g_test_add_func("/test/query_variable_info", - test_query_variable_info); - g_test_add_func("/test/get_next_variable/empty", - test_get_next_variable_empty); - g_test_add_func("/test/get_next_variable/long_name", - test_get_next_variable_long_name); - g_test_add_func("/test/get_next_variable/only_runtime", - test_get_next_variable_only_runtime); - g_test_add_func("/test/get_next_variable/too_small", - test_get_next_variable_too_small); - g_test_add_func("/test/get_next_variable/no_match", - test_get_next_variable_no_match); - g_test_add_func("/test/get_next_variable/all", - test_get_next_variable_all); - g_test_add_func("/test/set_variable/attr", - test_set_variable_attr); - g_test_add_func("/test/set_variable/set", - test_set_variable_set); - g_test_add_func("/test/set_variable/update", - test_set_variable_update); - g_test_add_func("/test/set_variable/append", - test_set_variable_append); - g_test_add_func("/test/set_variable/delete", - test_set_variable_delete); - g_test_add_func("/test/set_variable/resource_limit", - test_set_variable_resource_limit); - g_test_add_func("/test/set_variable/many_vars", - test_set_variable_many_vars); - g_test_add_func("/test/set_variable/non_volatile", - test_set_variable_non_volatile); - g_test_add_func("/test/set_variable/special_vars", - test_set_variable_special_vars); - g_test_add_func("/test/set_variable/mor", - test_set_variable_mor); - g_test_add_func("/test/set_variable/mor_key", - test_set_variable_mor_key); - - g_test_add_func("/test/secure_set_variable/use_bad_digest", - test_use_bad_digest); - g_test_add_func("/test/secure_set_variable/setupmode", - test_secure_set_variable_setupmode); - g_test_add_func("/test/secure_set_variable/PK", - test_secure_set_PK); - g_test_add_func("/test/secure_set_variable/usermode", - test_secure_set_variable_usermode); - g_test_add_func("/test/secure_set_variable/KEK/setupmode", - test_secure_set_KEK_setupmode); - g_test_add_func("/test/secure_set_variable/KEK/usermode", - test_secure_set_KEK_usermode); - - g_test_add_func("/test/secure_set_variable/DB/setupmode", - test_secure_set_db_setupmode); - g_test_add_func("/test/secure_set_variable/DBX/setupmode", - test_secure_set_dbx_setupmode); - g_test_add_func("/test/secure_set_variable/DBT/setupmode", - test_secure_set_dbt_setupmode); - - g_test_add_func("/test/secure_set_variable/DB/usermode", - test_secure_set_db_usermode); - g_test_add_func("/test/secure_set_variable/DBX/usermode", - test_secure_set_dbx_usermode); - g_test_add_func("/test/secure_set_variable/DBT/usermode", - test_secure_set_dbt_usermode); + ADD_MULTI_TEST("/test/get_variable/no_name", + test_get_variable_no_name); + ADD_MULTI_TEST("/test/get_variable/long_name", + test_get_variable_long_name); + ADD_MULTI_TEST("/test/get_variable/not_found", + test_get_variable_not_found); + ADD_MULTI_TEST("/test/get_variable/found", + test_get_variable_found); + ADD_MULTI_TEST("/test/get_variable/too_small", + test_get_variable_too_small); + ADD_MULTI_TEST("/test/query_variable_info", + test_query_variable_info); + ADD_MULTI_TEST("/test/get_next_variable/empty", + test_get_next_variable_empty); + ADD_MULTI_TEST("/test/get_next_variable/long_name", + test_get_next_variable_long_name); + ADD_MULTI_TEST("/test/get_next_variable/only_runtime", + test_get_next_variable_only_runtime); + ADD_MULTI_TEST("/test/get_next_variable/too_small", + test_get_next_variable_too_small); + ADD_MULTI_TEST("/test/get_next_variable/no_match", + test_get_next_variable_no_match); + ADD_MULTI_TEST("/test/get_next_variable/all", + test_get_next_variable_all); + ADD_MULTI_TEST("/test/set_variable/attr", + test_set_variable_attr); + ADD_MULTI_TEST("/test/set_variable/set", + test_set_variable_set); + ADD_MULTI_TEST("/test/set_variable/update", + test_set_variable_update); + ADD_MULTI_TEST("/test/set_variable/append", + test_set_variable_append); + ADD_MULTI_TEST("/test/set_variable/delete", + test_set_variable_delete); + ADD_MULTI_TEST("/test/set_variable/resource_limit", + test_set_variable_resource_limit); + ADD_MULTI_TEST("/test/set_variable/many_vars", + test_set_variable_many_vars); + ADD_MULTI_TEST("/test/set_variable/non_volatile", + test_set_variable_non_volatile); + ADD_MULTI_TEST("/test/set_variable/special_vars", + test_set_variable_special_vars); + ADD_MULTI_TEST("/test/set_variable/mor", + test_set_variable_mor); + ADD_MULTI_TEST("/test/set_variable/mor_key", + test_set_variable_mor_key); + + ADD_MULTI_TEST("/test/secure_set_variable/use_bad_digest", + test_use_bad_digest); + ADD_MULTI_TEST("/test/secure_set_variable/setupmode", + test_secure_set_variable_setupmode); + ADD_MULTI_TEST("/test/secure_set_variable/PK", + test_secure_set_PK); + ADD_MULTI_TEST("/test/secure_set_variable/usermode", + test_secure_set_variable_usermode); + ADD_MULTI_TEST("/test/secure_set_variable/KEK/setupmode", + test_secure_set_KEK_setupmode); + ADD_MULTI_TEST("/test/secure_set_variable/KEK/usermode", + test_secure_set_KEK_usermode); + + ADD_MULTI_TEST("/test/secure_set_variable/DB/setupmode", + test_secure_set_db_setupmode); + ADD_MULTI_TEST("/test/secure_set_variable/DBX/setupmode", + test_secure_set_dbx_setupmode); + ADD_MULTI_TEST("/test/secure_set_variable/DBT/setupmode", + test_secure_set_dbt_setupmode); + + ADD_MULTI_TEST("/test/secure_set_variable/DB/usermode", + test_secure_set_db_usermode); + ADD_MULTI_TEST("/test/secure_set_variable/DBX/usermode", + test_secure_set_dbx_usermode); + ADD_MULTI_TEST("/test/secure_set_variable/DBT/usermode", + test_secure_set_dbt_usermode); r = g_test_run(); free_globals(); diff --git a/tools/tool-lib.c b/tools/tool-lib.c index 35c02d0..63ab14b 100644 --- a/tools/tool-lib.c +++ b/tools/tool-lib.c @@ -53,7 +53,7 @@ tool_init(void) secure_boot_enable = false; auth_enforce = false; - cmd_buf = calloc(SHMEM_PAGES, PAGE_SIZE); + cmd_buf = calloc(SHMEM_PAGES_V1, PAGE_SIZE); if (!cmd_buf) return false; @@ -311,7 +311,7 @@ do_rm(const EFI_GUID *guid, const char *name) serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, guid); - serialize_uintn(&ptr, DATA_LIMIT); + serialize_uintn(&ptr, DATA_LIMIT_V1); *ptr = 0; dispatch_command(cmd_buf); diff --git a/tools/varstore-get.c b/tools/varstore-get.c index bc97618..8629519 100644 --- a/tools/varstore-get.c +++ b/tools/varstore-get.c @@ -80,7 +80,7 @@ do_get(const char *guid_str, const char *name, bool show_attr) serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, &guid); - serialize_uintn(&ptr, DATA_LIMIT); + serialize_uintn(&ptr, DATA_LIMIT_V1); *ptr = 0; dispatch_command(cmd_buf); @@ -109,13 +109,13 @@ do_get(const char *guid_str, const char *name, bool show_attr) uint8_t *data; UINTN data_len; - data = unserialize_data(&ptr, &data_len, DATA_LIMIT); + data = unserialize_data(&ptr, &data_len, DATA_LIMIT_V1); if (!data) { if (data_len == 0) { /* The variable is empty - nothing to write out. */ return true; } else { - ERR("Data too large: %lu > %u\n", data_len, DATA_LIMIT); + ERR("Data too large: %lu > %u\n", data_len, DATA_LIMIT_V1); return false; } } diff --git a/tools/varstore-set.c b/tools/varstore-set.c index 64a66c1..1dfec24 100644 --- a/tools/varstore-set.c +++ b/tools/varstore-set.c @@ -93,7 +93,7 @@ do_set(const char *guid_str, const char *name, const char *attr_str, ERR("Failed to open %s\n", path); return false; } - if (fstat(fileno(f), &st) == -1 || st.st_size > DATA_LIMIT) { + if (fstat(fileno(f), &st) == -1 || st.st_size > DATA_LIMIT_V1) { printf("Invalid file size\n"); fclose(f); return false; diff --git a/xapidb-lib.c b/xapidb-lib.c index 91e7e43..64e40ec 100644 --- a/xapidb-lib.c +++ b/xapidb-lib.c @@ -556,7 +556,7 @@ unserialize_variables(uint8_t **buf, size_t count, size_t rem) rem -= l->name_len; l->data = unserialize_data(buf, &l->data_len, - rem < DATA_LIMIT ? rem : DATA_LIMIT); + rem < DATA_LIMIT_MAX ? rem : DATA_LIMIT_MAX); if (!l->data) goto invalid; rem -= l->data_len; From 51d63f5045388a6e7b3e9e801ea4916a31c22e27 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Fri, 31 Oct 2025 20:14:20 +0100 Subject: [PATCH 4/9] tools: Use protocol v2 Signed-off-by: Tu Dinh --- tools/tool-lib.c | 10 ++++++---- tools/varstore-get.c | 9 +++++---- tools/varstore-ls.c | 3 ++- tools/varstore-set.c | 5 +++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/tools/tool-lib.c b/tools/tool-lib.c index 63ab14b..765e007 100644 --- a/tools/tool-lib.c +++ b/tools/tool-lib.c @@ -53,7 +53,7 @@ tool_init(void) secure_boot_enable = false; auth_enforce = false; - cmd_buf = calloc(SHMEM_PAGES_V1, PAGE_SIZE); + cmd_buf = calloc(SHMEM_PAGES_V2_MAX, PAGE_SIZE); if (!cmd_buf) return false; @@ -307,11 +307,12 @@ do_rm(const EFI_GUID *guid, const char *name) name_size = parse_name(name, variable_name); ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, guid); - serialize_uintn(&ptr, DATA_LIMIT_V1); + serialize_uintn(&ptr, DATA_LIMIT_V2); *ptr = 0; dispatch_command(cmd_buf); @@ -326,7 +327,8 @@ do_rm(const EFI_GUID *guid, const char *name) attr = unserialize_uint32(&ptr); ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, guid); diff --git a/tools/varstore-get.c b/tools/varstore-get.c index 8629519..0d113e1 100644 --- a/tools/varstore-get.c +++ b/tools/varstore-get.c @@ -76,11 +76,12 @@ do_get(const char *guid_str, const char *name, bool show_attr) } ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_GET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, &guid); - serialize_uintn(&ptr, DATA_LIMIT_V1); + serialize_uintn(&ptr, DATA_LIMIT_V2); *ptr = 0; dispatch_command(cmd_buf); @@ -109,13 +110,13 @@ do_get(const char *guid_str, const char *name, bool show_attr) uint8_t *data; UINTN data_len; - data = unserialize_data(&ptr, &data_len, DATA_LIMIT_V1); + data = unserialize_data(&ptr, &data_len, DATA_LIMIT_V2); if (!data) { if (data_len == 0) { /* The variable is empty - nothing to write out. */ return true; } else { - ERR("Data too large: %lu > %u\n", data_len, DATA_LIMIT_V1); + ERR("Data too large: %lu > %u\n", data_len, DATA_LIMIT_V2); return false; } } diff --git a/tools/varstore-ls.c b/tools/varstore-ls.c index 9954367..28f20c1 100644 --- a/tools/varstore-ls.c +++ b/tools/varstore-ls.c @@ -88,7 +88,8 @@ do_ls(void) for (;;) { ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_GET_NEXT_VARIABLE); serialize_uintn(&ptr, NAME_LIMIT); serialize_data(&ptr, name, size); diff --git a/tools/varstore-set.c b/tools/varstore-set.c index 1dfec24..c29c76c 100644 --- a/tools/varstore-set.c +++ b/tools/varstore-set.c @@ -93,7 +93,7 @@ do_set(const char *guid_str, const char *name, const char *attr_str, ERR("Failed to open %s\n", path); return false; } - if (fstat(fileno(f), &st) == -1 || st.st_size > DATA_LIMIT_V1) { + if (fstat(fileno(f), &st) == -1 || st.st_size > DATA_LIMIT_V2) { printf("Invalid file size\n"); fclose(f); return false; @@ -113,7 +113,8 @@ do_set(const char *guid_str, const char *name, const char *attr_str, fclose(f); ptr = cmd_buf; - serialize_uint32(&ptr, 1); /* version */ + serialize_uint32(&ptr, 2); /* version */ + serialize_uint32(&ptr, SHMEM_PAGES_V2_MAX); /* nr_pages */ serialize_uint32(&ptr, COMMAND_SET_VARIABLE); serialize_data(&ptr, variable_name, name_size); serialize_guid(&ptr, &guid); From a94771bc21616779af0dcc6ef0938e6090b62a54 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Mon, 3 Nov 2025 12:13:06 +0100 Subject: [PATCH 5/9] test: Dynamically allocate buffer based on version This lets valgrind precisely check buffer accesses based on the protocol version being used. Signed-off-by: Tu Dinh --- test.c | 98 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/test.c b/test.c index 9d8733c..c6e6b4a 100644 --- a/test.c +++ b/test.c @@ -50,7 +50,8 @@ const enum log_level log_level = LOG_LVL_ERROR; static void test_name(UINT32 version) /* The communication buffer. */ -static uint8_t buf[SHMEM_PAGES_V2_MAX * PAGE_SIZE]; +static uint8_t *buf = NULL; +static UINT32 buf_pages = 0; /* Wide char support */ @@ -344,6 +345,14 @@ static void free_globals(void) free(certPK); } +static void reset_buf(size_t nr_pages) +{ + free(buf); + buf = calloc(nr_pages, PAGE_SIZE); + assert(buf); + buf_pages = nr_pages; +} + static void reset_vars(void) { struct efi_variable *l, *tmp; @@ -359,6 +368,21 @@ static void reset_vars(void) var_list = NULL; } +static inline void reset_test(UINT32 version) { + switch (version) { + case 1: + reset_buf(SHMEM_PAGES_V1); + break; + case 2: + reset_buf(SHMEM_PAGES_V2_MAX); + break; + default: + assert(0); + } + + reset_vars(); +} + static void serialize_buf_version(uint8_t **ptr, UINT32 version) { switch (version) @@ -368,7 +392,7 @@ static void serialize_buf_version(uint8_t **ptr, UINT32 version) break; case 2: serialize_uint32(ptr, 2); - serialize_uint32(ptr, SHMEM_PAGES_V2_MAX); + serialize_uint32(ptr, buf_pages); break; default: assert(0); @@ -767,7 +791,7 @@ MULTI_TEST(test_get_variable_no_name) EFI_STATUS status; dstring *empty = alloc_dstring(""); - reset_vars(); + reset_test(version); /* An empty name should not be found. */ call_get_variable(version, empty, &nullguid, BSIZ, 0); @@ -784,7 +808,7 @@ MULTI_TEST(test_get_variable_long_name) EFI_STATUS status; dstring *bigname; - reset_vars(); + reset_test(version); bigname = alloc_dstring_unset(NAME_LIMIT / sizeof(uint16_t) + 1); memset(bigname->data, 42, dstring_data_size(bigname)); @@ -801,7 +825,7 @@ MULTI_TEST(test_get_variable_not_found) uint8_t *ptr; EFI_STATUS status; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); @@ -833,7 +857,7 @@ MULTI_TEST(test_get_variable_found) UINT32 attr; EFI_STATUS status; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_B); @@ -881,7 +905,7 @@ MULTI_TEST(test_get_variable_too_small) UINTN data_len; EFI_STATUS status; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* @@ -902,7 +926,7 @@ MULTI_TEST(test_query_variable_info) EFI_STATUS status; dstring *longname; - reset_vars(); + reset_test(version); /* * Use a long variable name to ensure the variable is larger than the @@ -976,7 +1000,7 @@ MULTI_TEST(test_get_next_variable_empty) uint8_t *ptr; EFI_STATUS status; - reset_vars(); + reset_test(version); /* No variables */ call_get_next_variable(version, BSIZ, NULL, &nullguid, 0); @@ -991,7 +1015,7 @@ MULTI_TEST(test_get_next_variable_long_name) EFI_STATUS status; dstring *tmp_name; - reset_vars(); + reset_test(version); tmp_name = alloc_dstring_unset(NAME_LIMIT / sizeof(uint16_t) + 1); memset(tmp_name->data, 42, dstring_data_size(tmp_name)); @@ -1015,7 +1039,7 @@ MULTI_TEST(test_get_next_variable_only_runtime) * Only runtime variables should be returned at runtime. */ - reset_vars(); + reset_test(version); sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); @@ -1057,7 +1081,7 @@ MULTI_TEST(test_get_next_variable_too_small) UINTN data_len; EFI_STATUS status; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); /* @@ -1079,7 +1103,7 @@ MULTI_TEST(test_get_next_variable_no_match) EFI_STATUS status; EFI_GUID guid; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); @@ -1121,7 +1145,7 @@ MULTI_TEST(test_get_next_variable_all) * At boot time, all variables should be retrieved. */ - reset_vars(); + reset_test(version); sv_ok(version, tname5, &tguid5, tdata5, sizeof(tdata5), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_BR); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); @@ -1195,7 +1219,7 @@ MULTI_TEST(test_set_variable_attr) uint8_t *ptr; EFI_STATUS status; - reset_vars(); + reset_test(version); setup_variables(); /* hardware error record is not supported */ @@ -1254,7 +1278,7 @@ MULTI_TEST(test_set_variable_set) EFI_STATUS status; UINT32 attr; - reset_vars(); + reset_test(version); /* Basic SetVariable usage. */ sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); @@ -1349,7 +1373,7 @@ MULTI_TEST(test_set_variable_update) EFI_STATUS status; UINT32 attr; - reset_vars(); + reset_test(version); /* Insert a variable... */ sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); @@ -1397,7 +1421,7 @@ MULTI_TEST(test_set_variable_append) EFI_STATUS status; UINT32 attr; - reset_vars(); + reset_test(version); /* Insert some variables */ sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); @@ -1444,7 +1468,7 @@ MULTI_TEST(test_set_variable_delete) uint8_t *ptr; EFI_STATUS status; - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_B); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); @@ -1529,7 +1553,7 @@ MULTI_TEST(test_set_variable_resource_limit) UINTN remaining = TOTAL_LIMIT; uint8_t tmp[TOTAL_LIMIT] = {0}; - reset_vars(); + reset_test(version); /* Check per-variable limit */ call_set_variable(version, tname1, &tguid1, tmp, data_limit(version) + 1, ATTR_B, 0); @@ -1588,7 +1612,7 @@ MULTI_TEST(test_set_variable_many_vars) const int count = TOTAL_LIMIT / (VARIABLE_SIZE_OVERHEAD + dstring_data_size(dname) + 1); - reset_vars(); + reset_test(version); /* Set more variables than are allowed based on the variable "overhead". */ for (i = 0; i < count + 1; i++) { @@ -1612,13 +1636,13 @@ MULTI_TEST(test_set_variable_non_volatile) UINT32 attr; remove(save_name); - reset_vars(); + reset_test(version); sv_ok(version, tname1, &tguid1, tdata1, sizeof(tdata1), ATTR_BNV); sv_ok(version, tname2, &tguid2, tdata2, sizeof(tdata2), ATTR_B); sv_ok(version, tname3, &tguid3, tdata3, sizeof(tdata3), ATTR_BRNV); sv_ok(version, tname4, &tguid4, tdata4, sizeof(tdata4), ATTR_BR); - reset_vars(); + reset_test(version); db->init(); call_get_variable(version, tname1, &tguid1, BSIZ, 0); @@ -1656,7 +1680,7 @@ MULTI_TEST(test_set_variable_non_volatile) /* Update, reload & check */ sv_ok(version, tname1, &tguid1, tdata2, sizeof(tdata2), ATTR_BNV); - reset_vars(); + reset_test(version); db->init(); call_get_variable(version, tname1, &tguid1, BSIZ, 0); @@ -1677,7 +1701,7 @@ MULTI_TEST(test_set_variable_non_volatile) status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); - reset_vars(); + reset_test(version); db->init(); call_get_variable(version, tname3, &tguid3, BSIZ, 0); @@ -1698,7 +1722,7 @@ MULTI_TEST(test_set_variable_non_volatile) status = unserialize_uintn(&ptr); g_assert_cmpuint(status, ==, EFI_SUCCESS); - reset_vars(); + reset_test(version); db->init(); call_get_variable(version, tname3, &tguid3, BSIZ, 0); @@ -1715,7 +1739,7 @@ MULTI_TEST(test_set_variable_special_vars) UINT32 attrs[] = {ATTR_BR, ATTR_BR | EFI_VARIABLE_APPEND_WRITE, ATTR_BRNV, ATTR_BRNV_TIME, 0}; - reset_vars(); + reset_test(version); setup_variables(); for (i = 0; i < ARRAY_SIZE(vars); i++) { @@ -1760,7 +1784,7 @@ MULTI_TEST(test_set_variable_mor) uint8_t data[] = {0, 0}; uint8_t expected = 0; - reset_vars(); + reset_test(version); setup_variables(); setup_mor_variables(); @@ -1860,7 +1884,7 @@ MULTI_TEST(test_set_variable_mor_key) uint8_t mor_control_data = 1; uint8_t expected; - reset_vars(); + reset_test(version); setup_variables(); setup_mor_variables(); @@ -1939,7 +1963,7 @@ MULTI_TEST(test_use_bad_digest) * algorithm of SHA-256 is accepted. */ - reset_vars(); + reset_test(version); setup_variables(); sign_and_check(version, tname1, &tguid1, ATTR_B_TIME, &test_timea, tdata1, @@ -2040,7 +2064,7 @@ static void test_set_secure_variable(UINT32 version) MULTI_TEST(test_secure_set_variable_setupmode) { - reset_vars(); + reset_test(version); setup_variables(); /* @@ -2058,7 +2082,7 @@ MULTI_TEST(test_secure_set_variable_setupmode) MULTI_TEST(test_secure_set_variable_usermode) { - reset_vars(); + reset_test(version); setup_variables(); /* Check we're in setup mode */ @@ -2084,7 +2108,7 @@ MULTI_TEST(test_secure_set_PK) uint8_t *ptr; size_t i; - reset_vars(); + reset_test(version); setup_variables(); check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, @@ -2464,7 +2488,7 @@ MULTI_TEST(test_secure_set_KEK_setupmode) UINT32 attr; EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; - reset_vars(); + reset_test(version); setup_variables(); sig_db_check(version, KEK_name, &gEfiGlobalVariableGuid, &test_time, true); @@ -2505,7 +2529,7 @@ MULTI_TEST(test_secure_set_KEK_usermode) { EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; - reset_vars(); + reset_test(version); setup_variables(); set_usermode(version); @@ -2568,7 +2592,7 @@ static void test_secure_set_db__setupmode(UINT32 version, const dstring *key_db) { EFI_TIME test_time = {2018, 6, 20, 13, 38, 0, 0, 0, 0, 0, 0}; - reset_vars(); + reset_test(version); setup_variables(); check_variable_data(version, setupMode_name, &gEfiGlobalVariableGuid, BSIZ, 0, @@ -2603,7 +2627,7 @@ static void test_secure_set_db__usermode(UINT32 version, const dstring *key_db) size_t combined_len; char *cert_list[] = {"testPK.pem", "testcertB.pem", NULL}; - reset_vars(); + reset_test(version); setup_variables(); set_usermode(version); From 121f88ac82b1fd57a1631bddef4b0c51173650f0 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Mon, 3 Nov 2025 13:11:59 +0100 Subject: [PATCH 6/9] Return attributes in GetVariable() on buffer error UEFI 2.8 and newer specifies that GetVariable() returns variable attributes even if the EFI_BUFFER_TOO_SMALL error is encountered. Return this value in v2 protocol. Note that in v2, in the EFI_BUFFER_TOO_SMALL case, `attributes` comes after `data_len` to be compatible with v1. Signed-off-by: Tu Dinh --- handler.c | 5 ++++- test.c | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/handler.c b/handler.c index cc82612..41a7e0c 100644 --- a/handler.c +++ b/handler.c @@ -277,6 +277,7 @@ internal_get_variable(const uint8_t *name, UINTN name_len, const EFI_GUID *guid, static void do_get_variable(uint8_t *comm_buf) { + UINT32 version; uint8_t *ptr, *name; EFI_GUID guid; UINTN name_len, data_len; @@ -284,7 +285,7 @@ do_get_variable(uint8_t *comm_buf) struct efi_variable *l; ptr = comm_buf; - if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { + if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { assert(0); return; } @@ -310,6 +311,8 @@ do_get_variable(uint8_t *comm_buf) if (data_len < l->data_len) { serialize_result(&ptr, EFI_BUFFER_TOO_SMALL); serialize_uintn(&ptr, l->data_len); + if (version >= 2) + serialize_uint32(&ptr, l->attributes); } else { serialize_result(&ptr, EFI_SUCCESS); serialize_uint32(&ptr, l->attributes); diff --git a/test.c b/test.c index c6e6b4a..b451011 100644 --- a/test.c +++ b/test.c @@ -903,6 +903,7 @@ MULTI_TEST(test_get_variable_too_small) { uint8_t *ptr; UINTN data_len; + UINT32 attr; EFI_STATUS status; reset_test(version); @@ -918,6 +919,11 @@ MULTI_TEST(test_get_variable_too_small) g_assert_cmpuint(status, ==, EFI_BUFFER_TOO_SMALL); data_len = unserialize_uintn(&ptr); g_assert_cmpuint(data_len, ==, sizeof(tdata1)); + + if (version >= 2) { + attr = unserialize_uint32(&ptr); + g_assert_cmpuint(attr, ==, ATTR_B); + } } MULTI_TEST(test_query_variable_info) From 54c5a1a28e92cf17c4ecb70543a5818a4a6738b8 Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Mon, 3 Nov 2025 13:36:37 +0100 Subject: [PATCH 7/9] test: Don't read data on variable get failure Signed-off-by: Tu Dinh --- test.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test.c b/test.c index b451011..bd86382 100644 --- a/test.c +++ b/test.c @@ -425,8 +425,20 @@ static EFI_STATUS call_get_variable_data(UINT32 version, const dstring *name, call_get_variable(version, name, guid, avail, at_runtime); status = unserialize_uintn(&ptr); - unserialize_uint32(&ptr); /* attr */ - *data = unserialize_data(&ptr, len, BSIZ); + switch (status) { + case EFI_SUCCESS: + unserialize_uint32(&ptr); /* attr */ + *data = unserialize_data(&ptr, len, BSIZ); + break; + case EFI_BUFFER_TOO_SMALL: + *data = NULL; + *len = unserialize_uintn(&ptr); + break; + default: + *data = NULL; + *len = 0; + break; + } return status; } From 2f39b30207b5cd90505bec991a5072f78ff5648a Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Mon, 3 Nov 2025 14:43:44 +0100 Subject: [PATCH 8/9] Bounds-check data_len against buffer size Previously, any data_len passed to GetVariable() would be accepted, as there exists only one single DATA_LIMIT. Now that buffer size is somewhat decoupled from variable size, check data_len as if it's untrusted. Fully decoupling buffer and variable size would require further work and potentially another version bump. Add a basic test for nr_pages in v2. Signed-off-by: Tu Dinh --- handler.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++-------- test.c | 58 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 10 deletions(-) diff --git a/handler.c b/handler.c index 41a7e0c..17e08bd 100644 --- a/handler.c +++ b/handler.c @@ -199,6 +199,43 @@ get_space_usage(void) return total; } +static UINTN +get_buf_avail(UINT32 nr_pages, uint8_t *comm_buf, uint8_t *ptr) +{ + ptrdiff_t offset = (ptrdiff_t)ptr - (ptrdiff_t)comm_buf; + ptrdiff_t bufsize = (ptrdiff_t)nr_pages * PAGE_SIZE; + + if (offset < 0 || offset > bufsize) { + assert(0); + return 0; + } + + return (UINTN)(bufsize - offset); +} + +static inline uint8_t * +unserialize_data_checked(UINT32 nr_pages, uint8_t *comm_buf, + uint8_t **ptr, UINTN *len, UINTN limit) +{ + uint8_t *data; + UINTN avail; + + *len = unserialize_uintn(ptr); + avail = get_buf_avail(nr_pages, comm_buf, *ptr); + + if (*len > limit || *len == 0 || *len > avail) + return NULL; + + data = malloc(*len); + if (!data) + return NULL; + + memcpy(data, *ptr, *len); + *ptr += *len; + + return data; +} + /* A limited version of SetVariable for internal use. */ EFI_STATUS internal_set_variable(const uint8_t *name, UINTN name_len, const EFI_GUID *guid, @@ -277,19 +314,20 @@ internal_get_variable(const uint8_t *name, UINTN name_len, const EFI_GUID *guid, static void do_get_variable(uint8_t *comm_buf) { - UINT32 version; + UINT32 version, nr_pages; uint8_t *ptr, *name; EFI_GUID guid; - UINTN name_len, data_len; + UINTN name_len, data_len, data_avail; BOOLEAN at_runtime; struct efi_variable *l; ptr = comm_buf; - if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { + if (snoop_command(&ptr, &version, &nr_pages, NULL) != EFI_SUCCESS) { assert(0); return; } - name = unserialize_data(&ptr, &name_len, NAME_LIMIT); + name = unserialize_data_checked(nr_pages, comm_buf, + &ptr, &name_len, NAME_LIMIT); if (!name) { serialize_result(&comm_buf, name_len == 0 ? EFI_NOT_FOUND : EFI_DEVICE_ERROR); return; @@ -299,6 +337,17 @@ do_get_variable(uint8_t *comm_buf) at_runtime = unserialize_boolean(&ptr); ptr = comm_buf; + data_avail = get_buf_avail(nr_pages, comm_buf, ptr) - sizeof(EFI_STATUS) - + sizeof(UINT32); + /* + * DataSize is not firmware-controlled, and callers are not aware of comm + * buffer size. Therefore, we can't return an error code even if DataSize is + * larger than the actual available buffer size; all we can do is to limit + * data_len accordingly. + */ + if (data_len > data_avail) + data_len = data_avail; + l = var_list; while (l) { if (l->name_len == name_len && @@ -1592,7 +1641,7 @@ debug_all_variables(const struct efi_variable *l) static void do_set_variable(uint8_t *comm_buf) { - UINT32 version; + UINT32 version, nr_pages; UINTN name_len, data_len; struct efi_variable *l, *prev = NULL; uint8_t *ptr, *name, *data; @@ -1604,17 +1653,19 @@ do_set_variable(uint8_t *comm_buf) EFI_TIME timestamp; ptr = comm_buf; - if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { + if (snoop_command(&ptr, &version, &nr_pages, NULL) != EFI_SUCCESS) { assert(0); return; } - name = unserialize_data(&ptr, &name_len, NAME_LIMIT); + name = unserialize_data_checked(nr_pages, comm_buf, + &ptr, &name_len, NAME_LIMIT); if (!name) { serialize_result(&comm_buf, name_len == 0 ? EFI_INVALID_PARAMETER : EFI_DEVICE_ERROR); return; } unserialize_guid(&ptr, &guid); - data = unserialize_data(&ptr, &data_len, data_limit(version)); + data = unserialize_data_checked(nr_pages, comm_buf, + &ptr, &data_len, data_limit(version)); if (!data && data_len) { serialize_result(&comm_buf, data_len > data_limit(version) @@ -1928,6 +1979,7 @@ do_set_variable(uint8_t *comm_buf) static void do_get_next_variable(uint8_t *comm_buf) { + UINT32 nr_pages; UINTN name_len, avail_len; uint8_t *ptr, *name; struct efi_variable *l; @@ -1935,12 +1987,13 @@ do_get_next_variable(uint8_t *comm_buf) BOOLEAN at_runtime; ptr = comm_buf; - if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { + if (snoop_command(&ptr, NULL, &nr_pages, NULL) != EFI_SUCCESS) { assert(0); return; } avail_len = unserialize_uintn(&ptr); - name = unserialize_data(&ptr, &name_len, NAME_LIMIT); + name = unserialize_data_checked(nr_pages, comm_buf, + &ptr, &name_len, NAME_LIMIT); if (!name && name_len) { serialize_result(&comm_buf, EFI_DEVICE_ERROR); return; diff --git a/test.c b/test.c index bd86382..ec70a06 100644 --- a/test.c +++ b/test.c @@ -1962,6 +1962,60 @@ MULTI_TEST(test_set_variable_mor_key) data, 8, ATTR_BRNV, EFI_ACCESS_DENIED); } +static void test_set_variable_v2_downgrade(void) +{ + EFI_STATUS status; + uint8_t tmp[DATA_LIMIT_MAX] = {0}; + uint8_t *ptr; + + reset_test(2); + + sv_ok(2, tname1, &tguid1, tmp, DATA_LIMIT_V2, ATTR_B); + + reset_buf(SHMEM_PAGES_V1); + + call_get_variable(1, tname1, &tguid1, DATA_LIMIT_V1, 1); + + /* Whatever it does here, it shouldn't crash. */ + + ptr = buf; + status = unserialize_uintn(&ptr); /* status */ + g_assert_cmpuint(status, !=, EFI_SUCCESS); +} + +static void test_set_variable_v2_short_buffer(void) +{ + UINT32 nr_pages; + EFI_STATUS status; + uint8_t tmp[DATA_LIMIT_V2] = {0}; + uint8_t *ptr = buf; + + reset_test(2); + + sv_ok(2, tname1, &tguid1, tmp, DATA_LIMIT_V2, ATTR_B); + + for (nr_pages = 1; nr_pages < SHMEM_PAGES_V2_MIN; nr_pages++) { + reset_buf(nr_pages); + call_get_variable(2, tname1, &tguid1, DATA_LIMIT_V2, 0); + ptr = buf; + status = unserialize_uintn(&ptr); /* status */ + g_assert_cmpuint(status, ==, EFI_INVALID_PARAMETER); + } + /* + * TODO: For now all nr_pages values within range are valid for containing + * a variable of size DATA_LIMIT_V2. If DATA_LIMIT_V2 is increased, full + * serialization bounds check would be needed. + */ + for (nr_pages = SHMEM_PAGES_V2_MIN; nr_pages <= SHMEM_PAGES_V2_MAX; + nr_pages++) { + reset_buf(nr_pages); + call_get_variable(2, tname1, &tguid1, DATA_LIMIT_V2, 0); + ptr = buf; + status = unserialize_uintn(&ptr); /* status */ + g_assert_cmpuint(status, ==, EFI_SUCCESS); + } +} + static void set_usermode(UINT32 version) { /* Move into user mode by enrolling Platform Key. */ @@ -2778,6 +2832,10 @@ int main(int argc, char **argv) test_set_variable_mor); ADD_MULTI_TEST("/test/set_variable/mor_key", test_set_variable_mor_key); + g_test_add_func("/test/set_variable/v2_downgrade", + test_set_variable_v2_downgrade); + g_test_add_func("/test/set_variable/v2_short_buffer", + test_set_variable_v2_short_buffer); ADD_MULTI_TEST("/test/secure_set_variable/use_bad_digest", test_use_bad_digest); From aaeda598c3d9cd0d23281761bfeed3c6b111d60b Mon Sep 17 00:00:00 2001 From: Tu Dinh Date: Tue, 4 Nov 2025 16:13:13 +0100 Subject: [PATCH 9/9] Read the command header only once Signed-off-by: Tu Dinh --- handler.c | 96 +++++++++++++++++++++++++---------------------- handler_port.c | 7 ++-- include/handler.h | 4 ++ 3 files changed, 60 insertions(+), 47 deletions(-) diff --git a/handler.c b/handler.c index 17e08bd..6e4e5cf 100644 --- a/handler.c +++ b/handler.c @@ -312,9 +312,9 @@ internal_get_variable(const uint8_t *name, UINTN name_len, const EFI_GUID *guid, } static void -do_get_variable(uint8_t *comm_buf) +do_get_variable(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command) { - UINT32 version, nr_pages; uint8_t *ptr, *name; EFI_GUID guid; UINTN name_len, data_len, data_avail; @@ -322,10 +322,7 @@ do_get_variable(uint8_t *comm_buf) struct efi_variable *l; ptr = comm_buf; - if (snoop_command(&ptr, &version, &nr_pages, NULL) != EFI_SUCCESS) { - assert(0); - return; - } + skip_command(&ptr, version, nr_pages, command); name = unserialize_data_checked(nr_pages, comm_buf, &ptr, &name_len, NAME_LIMIT); if (!name) { @@ -1639,9 +1636,9 @@ debug_all_variables(const struct efi_variable *l) #endif static void -do_set_variable(uint8_t *comm_buf) +do_set_variable(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command) { - UINT32 version, nr_pages; UINTN name_len, data_len; struct efi_variable *l, *prev = NULL; uint8_t *ptr, *name, *data; @@ -1653,10 +1650,7 @@ do_set_variable(uint8_t *comm_buf) EFI_TIME timestamp; ptr = comm_buf; - if (snoop_command(&ptr, &version, &nr_pages, NULL) != EFI_SUCCESS) { - assert(0); - return; - } + skip_command(&ptr, version, nr_pages, command); name = unserialize_data_checked(nr_pages, comm_buf, &ptr, &name_len, NAME_LIMIT); if (!name) { @@ -1977,9 +1971,9 @@ do_set_variable(uint8_t *comm_buf) } static void -do_get_next_variable(uint8_t *comm_buf) +do_get_next_variable(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command) { - UINT32 nr_pages; UINTN name_len, avail_len; uint8_t *ptr, *name; struct efi_variable *l; @@ -1987,10 +1981,7 @@ do_get_next_variable(uint8_t *comm_buf) BOOLEAN at_runtime; ptr = comm_buf; - if (snoop_command(&ptr, NULL, &nr_pages, NULL) != EFI_SUCCESS) { - assert(0); - return; - } + skip_command(&ptr, version, nr_pages, command); avail_len = unserialize_uintn(&ptr); name = unserialize_data_checked(nr_pages, comm_buf, &ptr, &name_len, NAME_LIMIT); @@ -2043,17 +2034,14 @@ do_get_next_variable(uint8_t *comm_buf) } static void -do_query_variable_info(uint8_t *comm_buf) +do_query_variable_info(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command) { - UINT32 version; uint8_t *ptr; UINT32 attr; ptr = comm_buf; - if (snoop_command(&ptr, &version, NULL, NULL) != EFI_SUCCESS) { - assert(0); - return; - } + skip_command(&ptr, version, nr_pages, command); attr = unserialize_uint32(&ptr); ptr = comm_buf; @@ -2074,7 +2062,8 @@ do_query_variable_info(uint8_t *comm_buf) } static void -do_notify_sb_failure(uint8_t *comm_buf) +do_notify_sb_failure(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command) { uint8_t *ptr; bool ret; @@ -2093,10 +2082,7 @@ do_notify_sb_failure(uint8_t *comm_buf) called = true; ptr = comm_buf; - if (snoop_command(&ptr, NULL, NULL, NULL) != EFI_SUCCESS) { - assert(0); - return; - } + skip_command(&ptr, version, nr_pages, command); ret = db->sb_notify(); @@ -2153,40 +2139,45 @@ EFI_STATUS snoop_command(uint8_t **comm_buf, UINT32 *out_version, return EFI_SUCCESS; } -void dispatch_command(uint8_t *comm_buf) +void skip_command(uint8_t **ptr, UINT32 version, UINT32 nr_pages, + enum command_t command) +{ + (void) nr_pages; + (void) command; + + unserialize_uint32(ptr); + if (version == 2) + unserialize_uint32(ptr); + unserialize_command(ptr); +} + +void dispatch_snooped_command(uint8_t *comm_buf, UINT32 version, + UINT32 nr_pages, enum command_t command) { - UINT32 version, nr_pages; - enum command_t command; uint8_t *ptr = comm_buf; - EFI_STATUS status; - status = snoop_command(&ptr, &version, &nr_pages, &command); - if (status != EFI_SUCCESS) { - DBG("snoop_command refused version data\n"); - serialize_result(&ptr, EFI_INVALID_PARAMETER); - return; - } + skip_command(&ptr, version, nr_pages, command); switch (command) { case COMMAND_GET_VARIABLE: DBG("COMMAND_GET_VARIABLE\n"); - do_get_variable(comm_buf); + do_get_variable(comm_buf, version, nr_pages, command); break; case COMMAND_SET_VARIABLE: DBG("COMMAND_SET_VARIABLE\n"); - do_set_variable(comm_buf); + do_set_variable(comm_buf, version, nr_pages, command); break; case COMMAND_GET_NEXT_VARIABLE: DBG("COMMAND_GET_NEXT_VARIABLE\n"); - do_get_next_variable(comm_buf); + do_get_next_variable(comm_buf, version, nr_pages, command); break; case COMMAND_QUERY_VARIABLE_INFO: DBG("COMMAND_QUERY_VARIABLE_INFO\n"); - do_query_variable_info(comm_buf); + do_query_variable_info(comm_buf, version, nr_pages, command); break; case COMMAND_NOTIFY_SB_FAILURE: DBG("COMMAND_NOTIFY_SB_FAILURE\n"); - do_notify_sb_failure(comm_buf); + do_notify_sb_failure(comm_buf, version, nr_pages, command); break; default: DBG("Unknown command\n"); @@ -2194,6 +2185,23 @@ void dispatch_command(uint8_t *comm_buf) }; } +void dispatch_command(uint8_t *comm_buf) +{ + UINT32 version, nr_pages; + enum command_t command; + uint8_t *ptr = comm_buf; + EFI_STATUS status; + + status = snoop_command(&ptr, &version, &nr_pages, &command); + if (status != EFI_SUCCESS) { + ptr = comm_buf; + serialize_result(&ptr, status); + return; + } + + dispatch_snooped_command(comm_buf, version, nr_pages, command); +} + bool setup_crypto(void) { diff --git a/handler_port.c b/handler_port.c index d0cbbb8..f859510 100644 --- a/handler_port.c +++ b/handler_port.c @@ -51,7 +51,8 @@ io_port_writel(uint64_t offset, uint64_t size, uint32_t val) { xen_pfn_t pfns[SHMEM_PAGES_V2_MAX]; void *shmem; - UINT32 nr_pages; + UINT32 version, nr_pages; + enum command_t command; int i; uint8_t *ptr; EFI_STATUS status; @@ -77,7 +78,7 @@ io_port_writel(uint64_t offset, uint64_t size, uint32_t val) ptr = shmem; /* nr_pages is the only thing we're interested in here */ - status = snoop_command(&ptr, NULL, &nr_pages, NULL); + status = snoop_command(&ptr, &version, &nr_pages, &command); xenforeignmemory_unmap(io_info.fmem, shmem, 1); @@ -95,7 +96,7 @@ io_port_writel(uint64_t offset, uint64_t size, uint32_t val) return; } - dispatch_command(shmem); + dispatch_snooped_command(shmem, version, nr_pages, command); xenforeignmemory_unmap(io_info.fmem, shmem, nr_pages); diff --git a/include/handler.h b/include/handler.h index 5702314..0f0105c 100644 --- a/include/handler.h +++ b/include/handler.h @@ -105,6 +105,10 @@ extern struct efi_variable *var_list; UINTN data_limit(UINT32 version); EFI_STATUS snoop_command(uint8_t **comm_buf, UINT32 *out_version, UINT32 *out_nr_pages, enum command_t *out_command); +void skip_command(uint8_t **comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command); +void dispatch_snooped_command(uint8_t *comm_buf, UINT32 version, UINT32 nr_pages, + enum command_t command); void dispatch_command(uint8_t *comm_buf); bool setup_crypto(void); bool setup_variables(void);