From 89ac875e161e5f592f33bf61cb0a2bc58c62951c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 18 Feb 2021 16:47:47 -0500 Subject: tpm: minor cleanup: use EV_IPL not 0xd This does two things: - consolidates all our TPM event type #defines to one place - uses EV_IPL instead of hard-coding 0xd Signed-off-by: Peter Jones --- include/tpm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/tpm.h b/include/tpm.h index d5245875..cab1939a 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -6,7 +6,6 @@ #include #define TPM_ALG_SHA 0x00000004 -#define EV_IPL 0x0000000d EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, const CHAR8 *description); @@ -176,6 +175,7 @@ typedef struct efi_tpm2_protocol efi_tpm2_protocol_t; typedef UINT32 TCG_EVENTTYPE; +#define EV_IPL 0x0000000d #define EV_EFI_EVENT_BASE ((TCG_EVENTTYPE) 0x80000000) #define EV_EFI_VARIABLE_DRIVER_CONFIG (EV_EFI_EVENT_BASE + 1) #define EV_EFI_VARIABLE_BOOT (EV_EFI_EVENT_BASE + 2) -- cgit v1.2.3 From 173ee6b0d8fc31faa961d5ca286bc40371412829 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Feb 2021 13:55:55 -0500 Subject: Move the coverity and scan-build makefiles out of the top directory I'm adding even more of this stuff, and it's feeling pretty cluttered, so this moves the scan-build and coverity makefiles into include/, where we'll see them less. Signed-off-by: Peter Jones --- Make.coverity | 45 --------------------------------------------- Make.scan-build | 17 ----------------- Makefile | 4 ++-- include/coverity.mk | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/scan-build.mk | 17 +++++++++++++++++ 5 files changed, 64 insertions(+), 64 deletions(-) delete mode 100644 Make.coverity delete mode 100644 Make.scan-build create mode 100644 include/coverity.mk create mode 100644 include/scan-build.mk (limited to 'include') diff --git a/Make.coverity b/Make.coverity deleted file mode 100644 index a897aa0a..00000000 --- a/Make.coverity +++ /dev/null @@ -1,45 +0,0 @@ -COV_EMAIL=$(call get-config,coverity.email) -COV_TOKEN=$(call get-config,coverity.token) -COV_URL=$(call get-config,coverity.url) -COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 - -cov-int : clean-shim-objs - make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a - cov-build --dir cov-int make $(DASHJ) all - -cov-int-all : clean - cov-build --dir cov-int make $(DASHJ) all - -cov-clean : - @rm -vf $(NAME)-coverity-*.tar.* - @if [ -d cov-int ]; then rm -rf cov-int && echo "removed 'cov-int'"; fi - -cov-file : | $(COV_FILE) - -$(COV_FILE) : | cov-int - tar caf $@ cov-int - -cov-upload : - @if [ -n "$(COV_URL)" ] && \ - [ -n "$(COV_TOKEN)" ] && \ - [ -n "$(COV_EMAIL)" ] ; \ - then \ - echo curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ - curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ - else \ - echo Coverity output is in $(COV_FILE) ; \ - fi - -coverity : | cov-test -coverity : cov-int cov-file cov-upload - -coverity-all : | cov-test -coverity-all : cov-int-all cov-file cov-upload - -clean : | cov-clean - -COV_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions cov-build 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") - -cov-test : ; $(if $(findstring /,$(COV_BUILD)),,$(error cov-build not found)) - -.PHONY : coverity cov-upload cov-clean cov-file cov-test diff --git a/Make.scan-build b/Make.scan-build deleted file mode 100644 index 7697cb89..00000000 --- a/Make.scan-build +++ /dev/null @@ -1,17 +0,0 @@ -SCAN_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions scan-build 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") - -scan-test : ; $(if $(findstring /,$(SCAN_BUILD)),,$(error scan-build not found)) - -scan-clean : - @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi - -scan-build : | scan-test -scan-build : clean-shim-objs - make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a - scan-build -o scan-results make $(DASHJ) CC=clang all - -scan-build-all : | scan-test -scan-build-all : clean - scan-build -o scan-results make $(DASHJ) CC=clang all - -.PHONY : scan-build scan-clean diff --git a/Makefile b/Makefile index 0cd45b91..7609df58 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ VPATH = $(TOPDIR) include $(TOPDIR)/Make.defaults include $(TOPDIR)/Make.rules -include $(TOPDIR)/Make.coverity -include $(TOPDIR)/Make.scan-build +include $(TOPDIR)/include/coverity.mk +include $(TOPDIR)/include/scan-build.mk TARGETS = $(SHIMNAME) TARGETS += $(SHIMNAME).debug $(MMNAME).debug $(FBNAME).debug diff --git a/include/coverity.mk b/include/coverity.mk new file mode 100644 index 00000000..a897aa0a --- /dev/null +++ b/include/coverity.mk @@ -0,0 +1,45 @@ +COV_EMAIL=$(call get-config,coverity.email) +COV_TOKEN=$(call get-config,coverity.token) +COV_URL=$(call get-config,coverity.url) +COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 + +cov-int : clean-shim-objs + make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a + cov-build --dir cov-int make $(DASHJ) all + +cov-int-all : clean + cov-build --dir cov-int make $(DASHJ) all + +cov-clean : + @rm -vf $(NAME)-coverity-*.tar.* + @if [ -d cov-int ]; then rm -rf cov-int && echo "removed 'cov-int'"; fi + +cov-file : | $(COV_FILE) + +$(COV_FILE) : | cov-int + tar caf $@ cov-int + +cov-upload : + @if [ -n "$(COV_URL)" ] && \ + [ -n "$(COV_TOKEN)" ] && \ + [ -n "$(COV_EMAIL)" ] ; \ + then \ + echo curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ + curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ + else \ + echo Coverity output is in $(COV_FILE) ; \ + fi + +coverity : | cov-test +coverity : cov-int cov-file cov-upload + +coverity-all : | cov-test +coverity-all : cov-int-all cov-file cov-upload + +clean : | cov-clean + +COV_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions cov-build 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") + +cov-test : ; $(if $(findstring /,$(COV_BUILD)),,$(error cov-build not found)) + +.PHONY : coverity cov-upload cov-clean cov-file cov-test diff --git a/include/scan-build.mk b/include/scan-build.mk new file mode 100644 index 00000000..7697cb89 --- /dev/null +++ b/include/scan-build.mk @@ -0,0 +1,17 @@ +SCAN_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions scan-build 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") + +scan-test : ; $(if $(findstring /,$(SCAN_BUILD)),,$(error scan-build not found)) + +scan-clean : + @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi + +scan-build : | scan-test +scan-build : clean-shim-objs + make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a + scan-build -o scan-results make $(DASHJ) CC=clang all + +scan-build-all : | scan-test +scan-build-all : clean + scan-build -o scan-results make $(DASHJ) CC=clang all + +.PHONY : scan-build scan-clean -- cgit v1.2.3 From d27c33b2fa544f89433e99f92122d21eaa06861d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Feb 2021 13:23:39 -0500 Subject: Make 'make fanalyzer' targets Signed-off-by: Peter Jones --- Make.defaults | 2 ++ Makefile | 1 + include/fanalyzer.mk | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 include/fanalyzer.mk (limited to 'include') diff --git a/Make.defaults b/Make.defaults index 1fa2bd5f..799a549d 100644 --- a/Make.defaults +++ b/Make.defaults @@ -91,6 +91,8 @@ endif CFLAGS = -ggdb $(OPTIMIZATIONS) -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Wsign-compare -Werror -fno-builtin \ -Werror=sign-compare -ffreestanding -std=gnu89 \ + $(if $(filter-out undefined,$(FANALYZER)),-fanalyzer,) \ + $(if $(filter-out undefined,$(COLOR)),-fdiagnostics-color=always,) \ -I$(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \ "-DDEFAULT_LOADER=L\"$(DEFAULT_LOADER)\"" \ "-DDEFAULT_LOADER_CHAR=\"$(DEFAULT_LOADER)\"" \ diff --git a/Makefile b/Makefile index 7609df58..a6807768 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ include $(TOPDIR)/Make.defaults include $(TOPDIR)/Make.rules include $(TOPDIR)/include/coverity.mk include $(TOPDIR)/include/scan-build.mk +include $(TOPDIR)/include/fanalyzer.mk TARGETS = $(SHIMNAME) TARGETS += $(SHIMNAME).debug $(MMNAME).debug $(FBNAME).debug diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk new file mode 100644 index 00000000..1018d1da --- /dev/null +++ b/include/fanalyzer.mk @@ -0,0 +1,27 @@ +GCC_BINARY ?= $(shell x=$$(which --skip-alias --skip-functions gcc 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") + +fanalyzer-test : ; $(if $(findstring /,$(GCC_BINARY)),,$(error gcc not found)) + +define prop +$(if $(filter-out undefined,$(origin $(1))),$(1)=$($1),) +endef + +MAKEARGS := \ + $(call prop,ARCH) \ + $(call prop,COLOR) \ + $(call prop,CROSS_COMPILE) + +fanalyzer : | fanalyzer-test +fanalyzer : clean-shim-objs fanalyzer-build + +fanalyzer-build : + make CC=gcc $(MAKEARGS) $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a + make CC=gcc $(MAKEARGS) FANALYZER=true all + +fanalyzer-all : | fanalyzer-test +fanalyzer-all : clean fanalyzer-build-all + +fanalyzer-build-all : + make CC=gcc $(MAKEARGS) FANALYZER=true all + +.PHONY : fanalyzer fanalyzer-build fanalyzer-all fanalyzer-build-all fanalyzer-clean -- cgit v1.2.3 From c1722924cee57e1eb27cad656baf079bf809b8f6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 22 Feb 2021 12:03:53 -0500 Subject: compiler.h: fix a typo and add some more function attribute macros This fixes the ifndef guard on NONNULL and __CONCAT3 and adds definitions for: - __CONCAT() for a##b with the intermediate tokenization step - ALLOCFUNC for __malloc__ - DEPRECATED for __deprecated__ - PURE for __pure__ - RETURNS_NONNULL for __nonnull__ Signed-off-by: Peter Jones --- .clang-format | 4 +++- include/compiler.h | 31 +++++++++++++++++++++++++++++-- mok.c | 6 +++--- shim.h | 2 -- 4 files changed, 35 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/.clang-format b/.clang-format index 5e84eda1..8e78b9bc 100644 --- a/.clang-format +++ b/.clang-format @@ -137,7 +137,9 @@ SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false Standard: c++03 -StatementMacros: [] +StatementMacros: + - ALLOCFUNC + - NONNULL TabWidth: 8 UseCRLF: false UseTab: AlignWithSpaces diff --git a/include/compiler.h b/include/compiler.h index 4e44840d..3cabd09c 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -3,6 +3,24 @@ #ifndef COMPILER_H_ #define COMPILER_H_ +/* + * These are special ones that get our unit tests in trouble with the + * compiler optimizer dropping out tests... + */ +#ifdef NONNULL +# undef NONNULL +#endif +#ifdef RETURNS_NONNULL +# undef RETURNS_NONNULL +#endif +#ifdef SHIM_UNIT_TEST +# define NONNULL(first, args...) +# define RETURNS_NONNULL +#else +# define NONNULL(first, args...) __attribute__((__nonnull__(first, ## args))) +# define RETURNS_NONNULL __attribute__((__returns_nonnull__)) +#endif + #ifndef UNUSED #define UNUSED __attribute__((__unused__)) #endif @@ -12,6 +30,9 @@ #ifndef PUBLIC #define PUBLIC __attribute__((__visibility__ ("default"))) #endif +#ifndef DEPRECATED +#define DEPRECATED __attribute__((__deprecated__)) +#endif #ifndef DESTRUCTOR #define DESTRUCTOR __attribute__((destructor)) #endif @@ -21,12 +42,15 @@ #ifndef ALIAS #define ALIAS(x) __attribute__((weak, alias (#x))) #endif -#ifndef NONNULL +#ifndef ALLOCFUNC +#define ALLOCFUNC(dealloc, dealloc_arg) __attribute__((__malloc__(dealloc, dealloc_arg))) #endif -#define NONNULL(first, args...) __attribute__((__nonnull__(first, ## args))) #ifndef PRINTF #define PRINTF(first, args...) __attribute__((__format__(printf, first, ## args))) #endif +#ifndef PURE +#define PURE __attribute__((__pure__)) +#endif #ifndef FLATTEN #define FLATTEN __attribute__((__flatten__)) #endif @@ -56,6 +80,9 @@ #endif #ifndef __CONCAT +#define __CONCAT(a, b) a ## b +#endif +#ifndef __CONCAT3 #define __CONCAT3(a, b, c) a ## b ## c #endif #ifndef CAT diff --git a/mok.c b/mok.c index 4b935a49..ac0276ec 100644 --- a/mok.c +++ b/mok.c @@ -250,7 +250,7 @@ struct mok_state_variable mok_state_variables[] = { #define should_mirror_addend(v) (((v)->categorize_addend) && ((v)->categorize_addend(v) != VENDOR_ADDEND_NONE)) -static inline BOOLEAN nonnull(1) +static inline BOOLEAN NONNULL(1) should_mirror_build_cert(struct mok_state_variable *v) { return (v->build_cert && v->build_cert_size && @@ -530,7 +530,7 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, } -static EFI_STATUS nonnull(1) +static EFI_STATUS NONNULL(1) mirror_one_mok_variable(struct mok_state_variable *v, BOOLEAN only_first) { @@ -840,7 +840,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, * Mirror a variable if it has an rtname, and preserve any * EFI_SECURITY_VIOLATION status at the same time. */ -static EFI_STATUS nonnull(1) +static EFI_STATUS NONNULL(1) maybe_mirror_one_mok_variable(struct mok_state_variable *v, EFI_STATUS ret, BOOLEAN only_first) { diff --git a/shim.h b/shim.h index 44c01555..90d9e89c 100644 --- a/shim.h +++ b/shim.h @@ -33,8 +33,6 @@ #include #include -#define nonnull(...) __attribute__((__nonnull__(__VA_ARGS__))) - #ifdef __x86_64__ #ifndef DEFAULT_LOADER #define DEFAULT_LOADER L"\\grubx64.efi" -- cgit v1.2.3 From c7bb10cf154aa2b84fd0c11f24caa5068aba5834 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 22 Feb 2021 12:07:56 -0500 Subject: Tidy up our string primitives... This adds constraints some of our compilers can check to all of our string primitives, as well as adding implementations of: - strdup - strdup - stpcpy - stpcpy - strnchrnul - strchrnul with a limit - strntoken - a tokenizer Signed-off-by: Peter Jones --- include/str.h | 147 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 126 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/str.h b/include/str.h index a6fbfefd..0c34256d 100644 --- a/include/str.h +++ b/include/str.h @@ -3,7 +3,13 @@ #ifndef SHIM_STR_H #define SHIM_STR_H -static inline __attribute__((unused)) unsigned long +#ifdef SHIM_UNIT_TEST +#pragma GCC diagnostic error "-Wnonnull-compare" +#else +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + +static inline UNUSED NONNULL(1) unsigned long strnlena(const CHAR8 *s, unsigned long n) { unsigned long i; @@ -13,9 +19,7 @@ strnlena(const CHAR8 *s, unsigned long n) return i; } -static inline -__attribute__((unused)) -CHAR8 * +static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) CHAR8 * strncpya(CHAR8 *dest, const CHAR8 *src, unsigned long n) { unsigned long i; @@ -28,9 +32,7 @@ strncpya(CHAR8 *dest, const CHAR8 *src, unsigned long n) return dest; } -static inline -__attribute__((unused)) -CHAR8 * +static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) CHAR8 * strcata(CHAR8 *dest, const CHAR8 *src) { unsigned long dest_len = strlena(dest); @@ -43,16 +45,24 @@ strcata(CHAR8 *dest, const CHAR8 *src) return dest; } -static inline -__attribute__((unused)) -CHAR8 * -strndupa(const CHAR8 * const src, const UINTN srcmax) +static inline UNUSED NONNULL(1) CHAR8 * +strdup(const CHAR8 * const src) { UINTN len; CHAR8 *news = NULL; - if (!src || !srcmax) - return news; + len = strlena(src); + news = AllocateZeroPool(len + 1); + if (news) + strncpya(news, src, len); + return news; +} + +static inline UNUSED NONNULL(1) CHAR8 * +strndupa(const CHAR8 * const src, const UINTN srcmax) +{ + UINTN len; + CHAR8 *news = NULL; len = strnlena(src, srcmax); news = AllocateZeroPool(len + 1); @@ -61,9 +71,17 @@ strndupa(const CHAR8 * const src, const UINTN srcmax) return news; } -static inline -__attribute__((unused)) -CHAR8 * +static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) char * +stpcpy(char *dest, const char * const src) +{ + size_t i = 0; + for (i = 0; src[i]; i++) + dest[i] = src[i]; + dest[i] = '\000'; + return &dest[i]; +} + +static inline UNUSED CHAR8 * translate_slashes(CHAR8 *out, const char *str) { int i; @@ -83,21 +101,18 @@ translate_slashes(CHAR8 *out, const char *str) return out; } -static inline UNUSED CHAR8 * +static inline UNUSED RETURNS_NONNULL NONNULL(1) CHAR8 * strchrnula(const CHAR8 *s, int c) { unsigned int i; - if (s == NULL) - return NULL; - for (i = 0; s[i] != '\000' && s[i] != c; i++) ; return (CHAR8 *)&s[i]; } -static inline UNUSED CHAR8 * +static inline UNUSED NONNULL(1) CHAR8 * strchra(const CHAR8 *s, int c) { const CHAR8 *s1; @@ -109,4 +124,94 @@ strchra(const CHAR8 *s, int c) return (CHAR8 *)s1; } +static inline UNUSED RETURNS_NONNULL NONNULL(1) char * +strnchrnul(const char *s, size_t max, int c) +{ + unsigned int i; + + if (!s || !max) + return (char *)s; + + for (i = 0; i < max && s[i] != '\0' && s[i] != c; i++) + ; + + if (i == max) + i--; + + return (char *)&s[i]; +} + +/** + * strntoken: tokenize a string, with a limit + * str: your string (will be modified) + * max: maximum number of bytes to ever touch + * delims: string of one character delimeters, any of which will tokenize + * *token: the token we're passing back (must be a pointer to NULL initially) + * state: a pointer to one char of state for between calls + * + * Ensure that both token and state are preserved across calls. Do: + * char state = 0; + * char *token = NULL; + * for (...) { + * valid = strntoken(...) + * not: + * char state = 0; + * for (...) { + * char *token = NULL; + * valid = strntoken(...) + * + * - it will not test bytes beyond str[max-1] + * - it will not set *token to an address beyond &str[max-1] + * - it will set *token to &str[max-1] without testing &str[max-2] for + * &str[max-1] == str + * - sequences of multiple delimeters will result in empty (pointer to '\0') + * tokens. + * - it expects you to update str and max on successive calls. + * + * return: + * true means it hasn't tested str[max-1] yet and token is valid + * false means it got to a NUL or str[max-1] and token is invalid + */ +static inline UNUSED NONNULL(1, 3, 4) int +strntoken(char *str, size_t max, const char *delims, char **token, char *state) +{ + char *tokend; + const char *delim; + int isdelim = 0; + int state_is_delim = 0; + + if (!str || !max || !delims || !token || !state) + return 0; + + tokend = &str[max-1]; + if (!str || max == 0 || !delims || !token) + return 0; + + /* + * the very special case of "" with max=1, where we have no prior + * state to let us know this is the same as right after a delim + */ + if (*token == NULL && max == 1 && *str == '\0') { + state_is_delim = 1; + } + + for (delim = delims; *delim; delim++) { + char *tmp = NULL; + if (*token && *delim == *state) + state_is_delim = 1; + tmp = strnchrnul(str, max, *delim); + if (tmp < tokend) + tokend = tmp; + if (*tokend == *delim) + isdelim = 1; + } + *token = str; + if (isdelim) { + *state = *tokend; + *tokend = '\0'; + return 1; + } + return state_is_delim; +} + #endif /* SHIM_STR_H */ -- cgit v1.2.3 From 82ce86a4bdd17581bcb42dd78245b5d961bf99bb Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 13:23:55 -0500 Subject: Add a list_size() primitive This adds list_size(), which tells us how many elements are in a list. Signed-off-by: Peter Jones --- include/list.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/list.h b/include/list.h index ad87b45b..1d36e163 100644 --- a/include/list.h +++ b/include/list.h @@ -102,5 +102,16 @@ list_del(struct list_head *entry) for (pos = (head)->prev, n = pos->prev; pos != (head); \ pos = n, n = pos->prev) +static inline size_t +list_size(struct list_head *entry) +{ + list_t *pos; + size_t i = 0; + list_for_each(pos, entry) { + i++; + } + return i; +} + #endif /* !LIST_H_ */ // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 1a6f9b807d2fad469035b25156cac94afda28751 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 22 Feb 2021 13:21:43 -0500 Subject: Move is_utf8_bom() to str.h This moves is_utf8_bom() to str.h, and also adds two #defines, UTF8_BOM and UTF8_BOM_SIZE. Signed-off-by: Peter Jones --- include/str.h | 11 +++++++++++ sbat.c | 10 +--------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/str.h b/include/str.h index 0c34256d..72f87b75 100644 --- a/include/str.h +++ b/include/str.h @@ -214,4 +214,15 @@ strntoken(char *str, size_t max, const char *delims, char **token, char *state) return state_is_delim; } +#define UTF8_BOM { 0xef, 0xbb, 0xbf } +#define UTF8_BOM_SIZE 3 + +static inline UNUSED NONNULL(1) BOOLEAN +is_utf8_bom(CHAR8 *buf, size_t bufsize) +{ + unsigned char bom[] = UTF8_BOM; + + return CompareMem(buf, bom, MIN(UTF8_BOM_SIZE, bufsize)) == 0; +} + #endif /* SHIM_STR_H */ diff --git a/sbat.c b/sbat.c index 446bed1a..bf1d1167 100644 --- a/sbat.c +++ b/sbat.c @@ -219,14 +219,6 @@ verify_sbat(size_t n, struct sbat_entry **entries) return efi_status; } -static BOOLEAN -is_utf8_bom(CHAR8 *buf, size_t bufsize) -{ - unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; - - return CompareMem(buf, bom, MIN(sizeof(bom), bufsize)) == 0; -} - static struct sbat_var * new_entry(const CHAR8 *comp_name, const CHAR8 *comp_gen) { @@ -278,7 +270,7 @@ parse_sbat_var(list_t *entries) CHAR8 *start = (CHAR8 *)data; CHAR8 *end = (CHAR8 *)data + datasize; if (is_utf8_bom(start, datasize)) - start += 3; + start += UTF8_BOM_SIZE; dprint(L"SBAT variable data:\n"); -- cgit v1.2.3 From 46adaaf3cf7cc853790343dd7f14a42c80078b28 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 14 Feb 2021 14:48:10 -0500 Subject: includes: include all gnu-efi includes at one place. Signed-off-by: Peter Jones --- include/errors.h | 2 -- include/guid.h | 2 -- include/ip4config2.h | 2 -- include/ip6config.h | 2 -- include/tpm.h | 2 -- shim.h | 3 ++- version.c.in | 2 ++ version.h | 2 -- 8 files changed, 4 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/errors.h b/include/errors.h index 1c6cf528..67d821e0 100644 --- a/include/errors.h +++ b/include/errors.h @@ -3,8 +3,6 @@ #ifndef SHIM_ERRORS_H #define SHIM_ERRORS_H -#include - #ifndef EFI_INCOMPATIBLE_VERSION #define EFI_INCOMPATIBLE_VERSION EFIERR(25) #endif diff --git a/include/guid.h b/include/guid.h index 114e8707..07a19a91 100644 --- a/include/guid.h +++ b/include/guid.h @@ -3,8 +3,6 @@ #ifndef SHIM_GUID_H #define SHIM_GUID_H -#include - extern EFI_GUID BDS_GUID; extern EFI_GUID GV_GUID; extern EFI_GUID SIG_DB; diff --git a/include/ip4config2.h b/include/ip4config2.h index 0955bc26..8fd8dfcb 100644 --- a/include/ip4config2.h +++ b/include/ip4config2.h @@ -9,8 +9,6 @@ #ifndef SHIM_IP4CONFIG2_H #define SHIM_IP4CONFIG2_H -#include - typedef struct _EFI_IP4_CONFIG2_PROTOCOL EFI_IP4_CONFIG2_PROTOCOL; diff --git a/include/ip6config.h b/include/ip6config.h index 8d9025b7..58cef532 100644 --- a/include/ip6config.h +++ b/include/ip6config.h @@ -8,8 +8,6 @@ #ifndef SHIM_IP6CONFIG_H #define SHIM_IP6CONFIG_H -#include - typedef struct _EFI_IP6_CONFIG_PROTOCOL EFI_IP6_CONFIG_PROTOCOL; /// diff --git a/include/tpm.h b/include/tpm.h index cab1939a..877d4f93 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -3,8 +3,6 @@ #ifndef SHIM_TPM_H #define SHIM_TPM_H -#include - #define TPM_ALG_SHA 0x00000004 EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, diff --git a/shim.h b/shim.h index 90d9e89c..b97cd407 100644 --- a/shim.h +++ b/shim.h @@ -29,6 +29,8 @@ #include #include #undef uefi_call_wrapper +#include +#include #include #include @@ -146,7 +148,6 @@ #include "include/tpm.h" #include "include/ucs2.h" #include "include/variables.h" -#include "include/sbat.h" #include "version.h" diff --git a/version.c.in b/version.c.in index 71509cdf..134acc0c 100644 --- a/version.c.in +++ b/version.c.in @@ -1,5 +1,7 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent +#include + #include "version.h" CHAR8 shim_version[] __attribute__((section (".data.ident"))) = diff --git a/version.h b/version.h index 254fea49..26d57ffe 100644 --- a/version.h +++ b/version.h @@ -3,8 +3,6 @@ #ifndef _SHIM_VERSION_H #define _SHIM_VERSION_H 1 -#include - extern CHAR8 shim_version[]; #endif /* SHIM_VERSION_H */ -- cgit v1.2.3 From 6eef250a82b0fe65c39b4c05f49035473d4bce0a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 10:19:29 -0500 Subject: Add a stand-alone CSV parser. This adds a simple to use, one-function-call CSV parser that takes a blob of data and gives you a linked list with an array of values. Signed-off-by: Peter Jones --- Makefile | 2 +- csv.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/str.h | 31 +++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 csv.c (limited to 'include') diff --git a/Makefile b/Makefile index a6807768..5dc745b3 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o +OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer ORIG_SOURCES = shim.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o diff --git a/csv.c b/csv.c new file mode 100644 index 00000000..3c821414 --- /dev/null +++ b/csv.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * csv.c - CSV parser + */ + +#include "shim.h" + +void NONNULL(1, 3, 4) +parse_csv_line(char * line, size_t max, size_t *n_columns, const char *columns[]) +{ + char *next = line; + size_t n = 0, new_n = n; + const char * const delims = ","; + char state = 0; + char *token = NULL; + + bool valid = true; + for (n = 0; n < *n_columns; n++) { + + if (valid) { + valid = strntoken(next, max, delims, &token, &state); + } + if (valid) { + next += strlena(token) + 1; + max -= strlena(token) + 1; + columns[n] = token; + new_n = n + 1; + } else { + columns[n] = NULL; + continue; + } + } + *n_columns = new_n; +} + +void +free_csv_list(list_t *list) +{ + list_t *pos = NULL, *tmp = NULL; + list_for_each_safe(pos, tmp, list) { + struct csv_row *row; + + row = list_entry(pos, struct csv_row, list); + list_del(&row->list); + FreePool(row); + } +} + +EFI_STATUS +parse_csv_data(char *data, char *data_end, size_t n_columns, list_t *list) +{ + EFI_STATUS efi_status = EFI_OUT_OF_RESOURCES; + char delims[] = "\r\n"; + char *line = data; + size_t max = 0; + char *end = data_end; + + if (!data || !end || end <= data || !n_columns || !list) + return EFI_INVALID_PARAMETER; + + max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); + + if (line && is_utf8_bom(line, max)) + line += UTF8_BOM_SIZE; + + while (line && line <= data_end) { + size_t entrysz = sizeof(char *) * n_columns + sizeof(struct csv_row); + struct csv_row *entry; + size_t m_columns = n_columns; + char *delim; + bool found = true; + + end = data_end; + max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); + while (max && found) { + found = false; + for (delim = &delims[0]; max && *delim; delim++) { + if (line[0] == *delim) { + line++; + max--; + found = true; + } + } + } + for (delim = &delims[0]; *delim; delim++) { + char *tmp = strnchrnul(line, max, *delim); + if (tmp < end) + end = tmp; + } + max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); + *end = '\0'; + + if (line == data_end || max == 0) { + line = end + 1; + continue; + } + + entry = AllocateZeroPool(entrysz); + if (!entry) { + efi_status = EFI_OUT_OF_RESOURCES; + goto err_oom; + } + + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, list); + + for (delim = &delims[0]; *delim; delim++) { + char *tmp = strnchrnul((const char *)line, max, *delim); + if (tmp < end) + end = tmp; + } + + parse_csv_line(line, max, &m_columns, (const char **)entry->columns); + entry->n_columns = m_columns; + line = end + 1; + } + + return EFI_SUCCESS; +err_oom: + free_csv_list(list); + return efi_status; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/include/str.h b/include/str.h index 72f87b75..91f05dc6 100644 --- a/include/str.h +++ b/include/str.h @@ -225,4 +225,35 @@ is_utf8_bom(CHAR8 *buf, size_t bufsize) return CompareMem(buf, bom, MIN(UTF8_BOM_SIZE, bufsize)) == 0; } +/** + * parse CSV data from data to end. + * *data points to the first byte of the data + * end points to a NUL byte at the end of the data + * n_columns number of columns per entry + * list the list head we're adding to + * + * On success, list will be populated with individually allocate a list of + * struct csv_list objects, with one column per entry of the "columns" array, + * filled left to right with up to n_columns elements, or NULL when a csv line + * does not have enough elements. + * + * Note that the data will be modified; all comma, linefeed, and newline + * characters will be set to '\000'. Additionally, consecutive linefeed and + * newline characters will not result in rows in the results. + * + * On failure, list will be empty and all entries on it will have been freed, + * using free_csv_list(), whether they were there before calling + * parse_csv_data or not. + */ + +struct csv_row { + list_t list; /* this is a linked list */ + size_t n_columns; /* this is how many columns are actually populated */ + char *columns[0]; /* these are pointers to columns */ +}; + +EFI_STATUS parse_csv_data(char *data, char *end, size_t n_columns, + list_t *list); +void free_csv_list(list_t *list); + #endif /* SHIM_STR_H */ -- cgit v1.2.3 From 6168b48258b8fd0c66be42a09e5612e41c908408 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 10:23:38 -0500 Subject: SBAT: make our sbat section parser use the csv parser This makes the .sbat section parser use parse_csv_data(). It also re-names a couple of the structs, because they were still too easy to get lost in. Signed-off-by: Peter Jones --- include/sbat.h | 11 ++-- pe.c | 6 +- sbat.c | 175 +++++++++++++++++++++++++-------------------------------- 3 files changed, 88 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index ffde202d..7aec3b78 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -18,7 +18,7 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); -struct sbat_entry { +struct sbat_section_entry { const CHAR8 *component_name; const CHAR8 *component_generation; const CHAR8 *vendor_name; @@ -26,11 +26,14 @@ struct sbat_entry { const CHAR8 *vendor_version; const CHAR8 *vendor_url; }; +#define SBAT_SECTION_COLUMNS (sizeof (struct sbat_section_entry) / sizeof(CHAR8 *)) -EFI_STATUS parse_sbat(char *sbat_base, size_t sbat_size, size_t *sbats, struct sbat_entry ***sbat); -void cleanup_sbat_entries(size_t n, struct sbat_entry **entries); +EFI_STATUS +parse_sbat_section(char *section_base, size_t section_size, size_t *n, + struct sbat_section_entry ***entriesp); +void cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries); -EFI_STATUS verify_sbat(size_t n, struct sbat_entry **entries); +EFI_STATUS verify_sbat(size_t n, struct sbat_section_entry **entries); #endif /* !SBAT_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/pe.c b/pe.c index e4e7133d..9da87530 100644 --- a/pe.c +++ b/pe.c @@ -829,7 +829,7 @@ handle_sbat(char *SBATBase, size_t SBATSize) unsigned int i; EFI_STATUS efi_status; size_t n; - struct sbat_entry **entries = NULL; + struct sbat_section_entry **entries = NULL; char *sbat_data; size_t sbat_size; @@ -850,7 +850,7 @@ handle_sbat(char *SBATBase, size_t SBATSize) CopyMem(sbat_data, SBATBase, SBATSize); sbat_data[SBATSize] = '\0'; - efi_status = parse_sbat(sbat_data, sbat_size, &n, &entries); + efi_status = parse_sbat_section(sbat_data, sbat_size, &n, &entries); if (EFI_ERROR(efi_status)) { perror(L"Could not parse .sbat section data: %r\n", efi_status); goto err; @@ -869,7 +869,7 @@ handle_sbat(char *SBATBase, size_t SBATSize) efi_status = verify_sbat(n, entries); - cleanup_sbat_entries(n, entries); + cleanup_sbat_section_entries(n, entries); err: FreePool(sbat_data); diff --git a/sbat.c b/sbat.c index bf1d1167..9a61007d 100644 --- a/sbat.c +++ b/sbat.c @@ -25,51 +25,96 @@ get_sbat_field(CHAR8 *current, CHAR8 *end, const CHAR8 **field, char delim) } EFI_STATUS -parse_sbat_entry(CHAR8 **current, CHAR8 *end, struct sbat_entry **sbat_entry) +parse_sbat_section(char *section_base, size_t section_size, + size_t *n_entries, + struct sbat_section_entry ***entriesp) { - struct sbat_entry *entry = NULL; + struct sbat_section_entry *entry = NULL, **entries; + EFI_STATUS efi_status = EFI_SUCCESS; + list_t csv, *pos = NULL; + char * end = section_base + section_size - 1; + size_t allocsz = 0; + size_t n; + char *strtab; - entry = AllocateZeroPool(sizeof(*entry)); - if (!entry) - return EFI_OUT_OF_RESOURCES; + if (!section_base || !section_size || !n_entries || !entriesp) + return EFI_INVALID_PARAMETER; - *current = get_sbat_field(*current, end, &entry->component_name, ','); - if (!entry->component_name) - goto error; + INIT_LIST_HEAD(&csv); - *current = get_sbat_field(*current, end, &entry->component_generation, - ','); - if (!entry->component_generation) - goto error; + efi_status = + parse_csv_data(section_base, end, SBAT_SECTION_COLUMNS, &csv); + if (EFI_ERROR(efi_status)) { + return efi_status; + } - *current = get_sbat_field(*current, end, &entry->vendor_name, ','); - if (!entry->vendor_name) - goto error; + n = 0; + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; - *current = - get_sbat_field(*current, end, &entry->vendor_package_name, ','); - if (!entry->vendor_package_name) - goto error; + row = list_entry(pos, struct csv_row, list); - *current = get_sbat_field(*current, end, &entry->vendor_version, ','); - if (!entry->vendor_version) - goto error; + if (row->n_columns < SBAT_SECTION_COLUMNS) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } - *current = get_sbat_field(*current, end, &entry->vendor_url, '\n'); - if (!entry->vendor_url) - goto error; + allocsz += sizeof(struct sbat_section_entry *); + allocsz += sizeof(struct sbat_section_entry); + for (i = 0; i < row->n_columns; i++) { + if (row->columns[i][0] == '\000') { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } + allocsz += strlena(row->columns[i]) + 1; + } + n++; + } - *sbat_entry = entry; + strtab = AllocateZeroPool(allocsz); + if (!strtab) { + efi_status = EFI_OUT_OF_RESOURCES; + goto err; + } - return EFI_SUCCESS; + entries = (struct sbat_section_entry **)strtab; + strtab += sizeof(struct sbat_section_entry *) * n; + entry = (struct sbat_section_entry *)strtab; + strtab += sizeof(struct sbat_section_entry) * n; + n = 0; + + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; + const char **ptrs[] = { + &entry->component_name, + &entry->component_generation, + &entry->vendor_name, + &entry->vendor_package_name, + &entry->vendor_version, + &entry->vendor_url, + }; -error: - FreePool(entry); - return EFI_INVALID_PARAMETER; + + row = list_entry(pos, struct csv_row, list); + for (i = 0; i < row->n_columns; i++) { + *(ptrs[i]) = strtab; + strtab = stpcpy(strtab, row->columns[i]) + 1; + } + entries[n] = entry; + entry++; + n++; + } + *entriesp = entries; + *n_entries = n; +err: + free_csv_list(&csv); + return efi_status; } void -cleanup_sbat_entries(size_t n, struct sbat_entry **entries) +cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries) { size_t i; @@ -86,71 +131,7 @@ cleanup_sbat_entries(size_t n, struct sbat_entry **entries) } EFI_STATUS -parse_sbat(char *sbat_base, size_t sbat_size, size_t *sbats, struct sbat_entry ***sbat) -{ - CHAR8 *current = (CHAR8 *)sbat_base; - CHAR8 *end = (CHAR8 *)sbat_base + sbat_size; - EFI_STATUS efi_status = EFI_SUCCESS; - struct sbat_entry *entry = NULL; - struct sbat_entry **entries; - size_t i = 0; - size_t pages = 1; - size_t n = PAGE_SIZE / sizeof(*entry); - - if (!sbat_base || sbat_size == 0 || !sbats || !sbat) - return EFI_INVALID_PARAMETER; - - if (current == end) - return EFI_INVALID_PARAMETER; - - *sbats = 0; - *sbat = 0; - - entries = AllocateZeroPool(pages * PAGE_SIZE); - if (!entries) - return EFI_OUT_OF_RESOURCES; - - do { - entry = NULL; - efi_status = parse_sbat_entry(¤t, end, &entry); - if (EFI_ERROR(efi_status)) - goto error; - - if (end < current) { - efi_status = EFI_INVALID_PARAMETER; - goto error; - } - - if (i >= n) { - struct sbat_entry **new_entries; - unsigned int osize = PAGE_SIZE * pages; - unsigned int nsize = osize + PAGE_SIZE; - - new_entries = ReallocatePool(entries, osize, nsize); - if (!new_entries) { - efi_status = EFI_OUT_OF_RESOURCES; - goto error; - } - entries = new_entries; - ZeroMem(&entries[i], PAGE_SIZE); - pages += 1; - n = nsize / sizeof(entry); - } - entries[i++] = entry; - } while (entry && current && *current != '\0'); - - *sbats = i; - *sbat = entries; - - return efi_status; -error: - perror(L"Failed to parse SBAT data: %r\n", efi_status); - cleanup_sbat_entries(i, entries); - return efi_status; -} - -EFI_STATUS -verify_single_entry(struct sbat_entry *entry, struct sbat_var *sbat_var_entry) +verify_single_entry(struct sbat_section_entry *entry, struct sbat_var *sbat_var_entry) { UINT16 sbat_gen, sbat_var_gen; @@ -194,7 +175,7 @@ cleanup_sbat_var(list_t *entries) } EFI_STATUS -verify_sbat(size_t n, struct sbat_entry **entries) +verify_sbat(size_t n, struct sbat_section_entry **entries) { unsigned int i; list_t *pos = NULL; -- cgit v1.2.3 From 73322ba087d10d06b0656816bf4b7ba80b02c751 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 10:25:00 -0500 Subject: SBAT: make our SBAT variable parser use the CSV parser This makes our SBAT variable parser use the generic CSV parser, and also changes its API slightly to produce a more testable intermediate interface. Signed-off-by: Peter Jones --- include/sbat.h | 11 ++- sbat.c | 218 +++++++++++++++++++++++++-------------------------------- 2 files changed, 107 insertions(+), 122 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 7aec3b78..c3e96179 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -8,13 +8,22 @@ extern UINTN _sbat, _esbat; -struct sbat_var { +struct sbat_var_entry { const CHAR8 *component_name; const CHAR8 *component_generation; + /* + * This column is only actually on the "sbat" version entry + */ + const CHAR8 *sbat_datestamp; list_t list; }; extern list_t sbat_var; +#define SBAT_VAR_COLUMNS ((sizeof (struct sbat_var_entry) - sizeof(list_t)) / sizeof(CHAR8 *)) +#define SBAT_VAR_REQUIRED_COLUMNS (SBAT_VAR_COLUMNS - 1) +#ifdef SHIM_UNIT_TEST +EFI_STATUS parse_sbat_var_data(list_t *entries, UINT8 *data, UINTN datasize); +#endif EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); diff --git a/sbat.c b/sbat.c index 9a61007d..0353e790 100644 --- a/sbat.c +++ b/sbat.c @@ -6,24 +6,6 @@ #include "shim.h" #include "string.h" -CHAR8 * -get_sbat_field(CHAR8 *current, CHAR8 *end, const CHAR8 **field, char delim) -{ - CHAR8 *offset; - - if (!field || !current || !end || current >= end) - return NULL; - - offset = strchrnula(current, delim); - *field = current; - - if (!offset || !*offset) - return NULL; - - *offset = '\0'; - return offset + 1; -} - EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, size_t *n_entries, @@ -116,22 +98,14 @@ err: void cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries) { - size_t i; - if (!n || !entries) return; - for (i = 0; i < n; i++) { - if (entries[i]) { - FreePool(entries[i]); - entries[i] = NULL; - } - } FreePool(entries); } EFI_STATUS -verify_single_entry(struct sbat_section_entry *entry, struct sbat_var *sbat_var_entry) +verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry) { UINT16 sbat_gen, sbat_var_gen; @@ -160,18 +134,19 @@ void cleanup_sbat_var(list_t *entries) { list_t *pos = NULL, *tmp = NULL; - struct sbat_var *entry; + struct sbat_var_entry *entry; + void *first = NULL; list_for_each_safe(pos, tmp, entries) { - entry = list_entry(pos, struct sbat_var, list); - list_del(&entry->list); + entry = list_entry(pos, struct sbat_var_entry, list); - if (entry->component_generation) - FreePool((CHAR8 *)entry->component_name); - if (entry->component_name) - FreePool((CHAR8 *)entry->component_generation); - FreePool(entry); + if ((uintptr_t)entry < (uintptr_t)first && entry != NULL) + first = entry; + + list_del(&entry->list); } + if (first) + FreePool(first); } EFI_STATUS @@ -180,7 +155,7 @@ verify_sbat(size_t n, struct sbat_section_entry **entries) unsigned int i; list_t *pos = NULL; EFI_STATUS efi_status = EFI_SUCCESS; - struct sbat_var *sbat_var_entry; + struct sbat_var_entry *sbat_var_entry; if (list_empty(&sbat_var)) { dprint(L"SBAT variable not present\n"); @@ -189,7 +164,7 @@ verify_sbat(size_t n, struct sbat_section_entry **entries) for (i = 0; i < n; i++) { list_for_each(pos, &sbat_var) { - sbat_var_entry = list_entry(pos, struct sbat_var, list); + sbat_var_entry = list_entry(pos, struct sbat_var_entry, list); efi_status = verify_single_entry(entries[i], sbat_var_entry); if (EFI_ERROR(efi_status)) return efi_status; @@ -200,112 +175,113 @@ verify_sbat(size_t n, struct sbat_section_entry **entries) return efi_status; } -static struct sbat_var * -new_entry(const CHAR8 *comp_name, const CHAR8 *comp_gen) +EFI_STATUS +parse_sbat_var_data(list_t *entry_list, UINT8 *data, UINTN datasize) { - struct sbat_var *new_entry = AllocatePool(sizeof(*new_entry)); + struct sbat_var_entry *entry = NULL, **entries; + EFI_STATUS efi_status = EFI_SUCCESS; + list_t csv, *pos = NULL; + char * start = (char *)data; + char * end = (char *)data + datasize - 1; + size_t allocsz = 0; + size_t n; + char *strtab; - if (!new_entry) - return NULL; + if (!entry_list|| !data || datasize == 0) + return EFI_INVALID_PARAMETER; - INIT_LIST_HEAD(&new_entry->list); - new_entry->component_name = comp_name; - new_entry->component_generation = comp_gen; + INIT_LIST_HEAD(&csv); - return new_entry; -} + efi_status = parse_csv_data(start, end, SBAT_VAR_COLUMNS, &csv); + if (EFI_ERROR(efi_status)) { + return efi_status; + } -EFI_STATUS -add_entry(list_t *list, const CHAR8 *comp_name, const CHAR8 *comp_gen) -{ - struct sbat_var *new; + n = 0; + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; - new = new_entry(comp_name, comp_gen); - if (!new) - return EFI_OUT_OF_RESOURCES; + row = list_entry(pos, struct csv_row, list); - list_add_tail(&new->list, list); - return EFI_SUCCESS; + if (row->n_columns < SBAT_VAR_REQUIRED_COLUMNS) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } + + + allocsz += sizeof(struct sbat_var_entry *); + allocsz += sizeof(struct sbat_var_entry); + for (i = 0; i < row->n_columns; i++) { + if (row->columns[i][0]) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } + allocsz += strlena(row->columns[i]) + 1; + } + n++; + } + + strtab = AllocateZeroPool(allocsz); + if (!strtab) { + efi_status = EFI_OUT_OF_RESOURCES; + goto err; + } + + INIT_LIST_HEAD(entry_list); + + entries = (struct sbat_var_entry **)strtab; + strtab += sizeof(struct sbat_var_entry *) * n; + entry = (struct sbat_var_entry *)strtab; + strtab += sizeof(struct sbat_var_entry) * n; + n = 0; + + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; + const char **ptrs[] = { + &entry->component_name, + &entry->component_generation, + &entry->sbat_datestamp, + }; + + row = list_entry(pos, struct csv_row, list); + for (i = 0; i < row->n_columns; i++) { + *(ptrs[i]) = strtab; + strtab = stpcpy(strtab, row->columns[i]) + 1; + } + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, entry_list); + entries[n] = entry; + entry++; + n++; + } +err: + free_csv_list(&csv); + return efi_status; } EFI_STATUS parse_sbat_var(list_t *entries) { UINT8 *data = 0; - UINTN datasize, i; + UINTN datasize; EFI_STATUS efi_status; - char delim; if (!entries) return EFI_INVALID_PARAMETER; - INIT_LIST_HEAD(entries); - efi_status = get_variable(L"SBAT", &data, &datasize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { - LogError(L"Failed to read SBAT variable\n", - efi_status); + LogError(L"Failed to read SBAT variable\n", efi_status); return efi_status; } - CHAR8 *start = (CHAR8 *)data; - CHAR8 *end = (CHAR8 *)data + datasize; - if (is_utf8_bom(start, datasize)) - start += UTF8_BOM_SIZE; - - dprint(L"SBAT variable data:\n"); - - while (start[0] != '\0') { - const CHAR8 *fields[2] = { - NULL, - }; - for (i = 0; i < 3; i++) { - const CHAR8 *tmp; - /* - * on third iteration we check if we had extra stuff on line while parsing - * component_name. If delimeter on 2nd iteration was ',', this means that - * we have comments after component_name. get_sbat_field in this if condition - * parses comments, if they are present and drops them. - */ - if (i == 2 && start) { - if (delim == ',') { - start = get_sbat_field(start, end, &tmp, - '\n'); - } - break; - } - delim = ','; - /* we do not want to jump to next line and grab stuff from that - */ - if ((strchrnula(start, '\n') - start + 1) <= - (strchrnula(start, ',') - start + 1)) { - delim = '\n'; - if (i == 0) - goto error; - } - if (!start) { - goto error; - } - start = get_sbat_field(start, end, &tmp, delim); - /* to be replaced when we have strdupa() - */ - fields[i] = strndupa(tmp, strlen(tmp)); - if (!fields[i]) { - goto error; - } - } - dprint(L"component %a with generation %a\n", fields[0], fields[1]); - efi_status = - add_entry(entries, fields[0], fields[1]); - if (EFI_ERROR(efi_status)) - goto error; - } - FreePool(data); - return EFI_SUCCESS; -error: - perror(L"failed to parse SBAT variable\n"); - cleanup_sbat_var(entries); - FreePool(data); - return EFI_INVALID_PARAMETER; + /* + * We've intentionally made sure there's a NUL byte on all variable + * allocations, so use that here. + */ + return parse_sbat_var_data(entries, data, datasize+1); } + // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 9ca8e9a633501c3ec3c95d2576b795eee8d9b1cc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 14 Feb 2021 14:50:44 -0500 Subject: make 'make test' able to run unit test harnesses This adds a couple of make targets to do unit tests that are linked to libc: test-FOO : builds and runs test-FOO for any test-FOO.c test : builds and runs all test-FOO tests Note that building and running this test does not quite work yet /on this branch/. In order to do that, we need some cleanups and reorganizing that I don't want to push just yet, which can be found on https://github.com/rhboot/shim/tree/test-reorg Signed-off-by: Peter Jones --- .gitignore | 2 + Makefile | 13 +++++- include/test.h | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/test.mk | 45 +++++++++++++++++++++ shim.h | 11 +++++ test.c | 63 +++++++++++++++++++++++++++++ 6 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 include/test.h create mode 100644 include/test.mk create mode 100644 test.c (limited to 'include') diff --git a/.gitignore b/.gitignore index 811ca69d..9f492510 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,6 @@ /scan-results/ [Ss]creenlog* shim_cert.h +test-* +!test-*.c version.c diff --git a/Makefile b/Makefile index 5dc745b3..87115b6f 100644 --- a/Makefile +++ b/Makefile @@ -240,6 +240,17 @@ else $(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f endif +test : + @make -f include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" all + +$(patsubst %.c,%,$(wildcard test-*.c)) : + @make -f include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@ + +.PHONY : $(patsubst %.c,%,$(wildcard test-*.c)) test + +clean-test-objs: + @make -f include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" clean + clean-shim-objs: $(MAKE) -C lib -f $(TOPDIR)/lib/Makefile clean @rm -rvf $(TARGET) *.o $(SHIM_OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb $(BOOTCSVNAME) @@ -247,7 +258,7 @@ clean-shim-objs: @rm -vf Cryptlib/*.[oa] Cryptlib/*/*.[oa] @if [ -d .git ] ; then git clean -f -d -e 'Cryptlib/OpenSSL/*'; fi -clean: clean-shim-objs +clean: clean-shim-objs clean-test-objs $(MAKE) -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile clean $(MAKE) -C Cryptlib/OpenSSL -f $(TOPDIR)/Cryptlib/OpenSSL/Makefile clean diff --git a/include/test.h b/include/test.h new file mode 100644 index 00000000..6fc178ba --- /dev/null +++ b/include/test.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test.h - fake a bunch of EFI types so we can build test harnesses with libc + * Copyright Peter Jones + */ + +#ifdef SHIM_UNIT_TEST +#ifndef TEST_H_ +#define TEST_H_ + +#include + +#if defined(__aarch64__) +#include +#elif defined(__arm__) +#include +#elif defined(__i386__) || defined(__i486__) || defined(__i686__) +#include +#elif defined(__x86_64__) +#include +#else +#error what arch is this +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define ZeroMem(buf, sz) memset(buf, 0, sz) +#define SetMem(buf, sz, value) memset(buf, value, sz) +#define CopyMem(dest, src, len) memcpy(dest, src, len) +#define CompareMem(dest, src, len) memcmp(dest, src, len) + +#include + +#define AllocateZeroPool(x) calloc(1, (x)) +#define AllocatePool(x) malloc(x) +#define FreePool(x) free(x) +#define ReallocatePool(old, oldsz, newsz) realloc(old, newsz) + +extern int debug; +#ifdef dprint +#undef dprint +#define dprint(fmt, ...) {( if (debug) printf("%s:%d:" fmt, __func__, __LINE__, ##__VA_ARGS__); }) +#endif + +#define eassert(cond, fmt, ...) \ + ({ \ + if (!(cond)) { \ + printf("%s:%d:" fmt, __func__, __LINE__, \ + ##__VA_ARGS__); \ + } \ + assert(cond); \ + }) + +#define assert_equal_return(a, b, status, fmt, ...) \ + ({ \ + if (!((a) == (b))) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a == b)); \ + return status; \ + } \ + }) + +#define assert_return(cond, status, fmt, ...) \ + ({ \ + if (!(cond)) { \ + printf("%s:%d:" fmt, __func__, __LINE__, \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(cond)); \ + return status; \ + } \ + }) + +#define assert_goto(cond, label, fmt, ...) \ + ({ \ + if (!(cond)) { \ + printf("%s:%d:" fmt, __func__, __LINE__, \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(cond)); \ + goto label; \ + } \ + }) + +#define assert_equal_goto(a, b, label, fmt, ...) \ + ({ \ + if (!((a) == (b))) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a == b)); \ + goto label; \ + } \ + }) + +#define test(x, ...) \ + ({ \ + int rc; \ + printf("running %s\n", __stringify(x)); \ + rc = x(__VA_ARGS__); \ + if (rc < 0) \ + status = 1; \ + printf("%s: %s\n", __stringify(x), \ + rc < 0 ? "failed" : "passed"); \ + }) + +#endif /* !TEST_H_ */ +#endif /* SHIM_UNIT_TEST */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/test.mk b/include/test.mk new file mode 100644 index 00000000..f70fdaa9 --- /dev/null +++ b/include/test.mk @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# test.mk - makefile to make local test programs +# + +.SUFFIXES: + +CC = gcc +VALGRIND ?= +DEBUG_PRINTS ?= 0 +CFLAGS = -O2 -ggdb -std=gnu11 \ + -isystem $(TOPDIR)/include/system \ + $(EFI_INCLUDES) \ + -Iinclude -iquote . \ + -fshort-wchar -flto -fno-builtin \ + -Wall \ + -Wextra \ + -Wsign-compare \ + -Wno-deprecated-declarations \ + -Wno-pointer-sign \ + -Wno-unused \ + -Werror \ + -Werror=nonnull \ + -Werror=nonnull-compare \ + $(ARCH_DEFINES) \ + -DEFI_FUNCTION_WRAPPER \ + -DGNU_EFI_USE_MS_ABI -DPAGE_SIZE=4096 \ + -DSHIM_UNIT_TEST \ + "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" + +tests := $(patsubst %.c,%,$(wildcard test-*.c)) + +$(tests) :: test-% : test.c test-%.c $(test-%_FILES) + $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) + $(VALGRIND) ./$@ + +test : $(tests) + +all : test + +clean : + +.PHONY: $(tests) all test clean + +# vim:ft=make diff --git a/shim.h b/shim.h index 55b0aa28..0a6c8cfa 100644 --- a/shim.h +++ b/shim.h @@ -26,15 +26,21 @@ #endif #endif +#ifndef SHIM_UNIT_TEST #include #include #undef uefi_call_wrapper #include #include +#endif #include #include +#ifdef SHIM_UNIT_TEST +#include "include/test.h" +#endif + #ifdef __x86_64__ #ifndef DEFAULT_LOADER #define DEFAULT_LOADER L"\\grubx64.efi" @@ -222,6 +228,7 @@ verify_buffer (char *data, int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash, UINT8 *sha1hash); +#ifndef SHIM_UNIT_TEST #define perror_(file, line, func, fmt, ...) ({ \ UINTN __perror_ret = 0; \ if (!in_protocol) \ @@ -233,5 +240,9 @@ verify_buffer (char *data, int datasize, perror_(__FILE__, __LINE__ - 1, __func__, fmt, ##__VA_ARGS__) #define LogError(fmt, ...) \ LogError_(__FILE__, __LINE__ - 1, __func__, fmt, ##__VA_ARGS__) +#else +#define perror(fmt, ...) +#define LogError(fmt, ...) +#endif #endif /* SHIM_H_ */ diff --git a/test.c b/test.c new file mode 100644 index 00000000..b21e2191 --- /dev/null +++ b/test.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test.c - stuff we need for test harnesses + * Copyright Peter Jones + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +UINT8 in_protocol = 0; +int debug = DEFAULT_DEBUG_PRINT_STATE; + +EFI_STATUS LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) +{ + assert(0); + return EFI_SUCCESS; +} + +INTN +StrCmp(CONST CHAR16 *s1, CONST CHAR16 *s2) { + assert(s1 != NULL); + assert(s2 != NULL); + + int i; + for (i = 0; s1[i] && s2[i]; i++) { + if (s1[i] != s2[i]) + return s2[i] - s1[i]; + } + return 0; +} + +INTN +StrnCmp(CONST CHAR16 *s1, CONST CHAR16 *s2, UINTN len) { + assert(s1 != NULL); + assert(s2 != NULL); + + UINTN i; + for (i = 0; i < len && s1[i] && s2[i]; i++) { + if (s1[i] != s2[i]) + return s2[i] - s1[i]; + + } + return 0; +} + +EFI_STATUS +get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, + EFI_GUID owner, UINT32 *attributes) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +get_variable(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner) +{ + return get_variable_attr(var, data, len, owner, NULL); +} + +EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; + +// vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 615e701d53383a67131e9082d1c7329b69aa9f5d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 15:01:38 -0500 Subject: Add test cases for our CSV parser. This does a couple of straightforward tests on our CSV parser, and then for good measure it does two with random data - one that's just random data, one that's had all the zeros changed to nonzero values. Note that building and running this test does not quite work yet /on this branch/. In order to do that, we need some cleanups and reorganizing that I don't want to push just yet, which can be found on https://github.com/rhboot/shim/tree/test-reorg Signed-off-by: Peter Jones --- .gitignore | 6 +- include/str.h | 5 + include/test.mk | 12 +- test-csv.c | 471 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 490 insertions(+), 4 deletions(-) create mode 100644 test-csv.c (limited to 'include') diff --git a/.gitignore b/.gitignore index 9f492510..e8a34a07 100644 --- a/.gitignore +++ b/.gitignore @@ -28,10 +28,12 @@ /build*/ /certdb/ /cov-int/ +/random.bin /sbat.*.csv /scan-results/ [Ss]creenlog* shim_cert.h -test-* -!test-*.c +/test-* +!/test-*.c +/test-random.h version.c diff --git a/include/str.h b/include/str.h index 91f05dc6..c4d12113 100644 --- a/include/str.h +++ b/include/str.h @@ -256,4 +256,9 @@ EFI_STATUS parse_csv_data(char *data, char *end, size_t n_columns, list_t *list); void free_csv_list(list_t *list); +#ifdef SHIM_UNIT_TEST +void NONNULL(1, 3, 4) +parse_csv_line(char * line, size_t max, size_t *n_columns, const char *columns[]); +#endif + #endif /* SHIM_STR_H */ diff --git a/include/test.mk b/include/test.mk index f70fdaa9..8cdcc332 100644 --- a/include/test.mk +++ b/include/test.mk @@ -28,6 +28,12 @@ CFLAGS = -O2 -ggdb -std=gnu11 \ -DSHIM_UNIT_TEST \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" +$(wildcard test-*.c) :: %.c : test-random.h + +test-random.h: + dd if=/dev/urandom bs=512 count=17 of=random.bin + xxd -i random.bin test-random.h + tests := $(patsubst %.c,%,$(wildcard test-*.c)) $(tests) :: test-% : test.c test-%.c $(test-%_FILES) @@ -36,10 +42,12 @@ $(tests) :: test-% : test.c test-%.c $(test-%_FILES) test : $(tests) -all : test - clean : + @rm -vf test-random.h random.bin + +all : clean test .PHONY: $(tests) all test clean +.SECONDARY: random.bin # vim:ft=make diff --git a/test-csv.c b/test-csv.c new file mode 100644 index 00000000..4acf966b --- /dev/null +++ b/test-csv.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-csv.c - test our csv parser + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +#include + +struct test_entry { + size_t n_columns; + char *columns[7]; +}; + +int +test_parse_csv_line_size_0(void) +{ + char *s0 = ""; + char *columns[] = { "a", "b", "c", "d" }; + char *test_columns[] = { NULL, NULL, NULL, NULL }; + size_t n_columns = 3; + size_t i; + + test_columns[3] = columns[3]; + + parse_csv_line(s0, 0, &n_columns, (const char **)columns); + + assert_equal_return(s0[0], '\0', -1, "got %#hhx expected %#hhx\n"); + assert_equal_return(n_columns, 0, -1, "got %#hhx expected %#hhx\n"); + for (i = 0; i < 4; i++) { + assert_equal_return(test_columns[i], columns[i], -1, + "expected %p got %p for column %d\n", + i); + } + return 0; +} + +int +test_parse_csv_line_size_1(void) +{ + char *s0 = ""; + char *columns[] = { "a", "b", "c", "d" }; + char *test_columns[] = { "", NULL, NULL, NULL }; + size_t n_columns = 3; + size_t max = 1; + size_t i; + + test_columns[3] = columns[3]; + + parse_csv_line(s0, max, &n_columns, (const char **)columns); + + assert_equal_return(s0[0], '\0', -1, "got %#hhx expected %#hhx\n"); + assert_equal_return(n_columns, 1, -1, "got %#hhx expected %#hhx\n"); + for (i = 0; i < 4; i++) { + assert_equal_return(test_columns[i], columns[i], -1, + "expected %p got %p for column %d\n", + i); + } + return 0; +} + +int +test_parse_csv_line_comma_size_1(void) +{ + char *s0; + char *columns[] = { "a", "b", "c", "d" }; + char *test_columns[] = { "", NULL, NULL, "d" }; + size_t n_columns = 3; + size_t max = 1; + size_t i; + + /* + * For reasons unknown, when I do this the normal way with: + * char *s0 = ","; + * gcc is putting it in .rodata, + * *** AND combining it with the "," from delims from parse_csv_line***. + */ + s0 = alloca(2); + s0[0] = ','; + s0[1] = '\0'; + + parse_csv_line(s0, max, &n_columns, (const char **)columns); + + assert_equal_return(s0[0], '\0', -1, "got %#hhx expected %#hhx\n"); + assert_equal_return(n_columns, 1, -1, "got %#hhx expected %#hhx\n"); +// for (i = 0; i < 4; i++) { +// printf("columns[%d]:%p:\"%s\"\n", i, columns[i], columns[i]); +// } + for (i = 0; i < 1; i++) { + assert_equal_return(strcmp(test_columns[i], columns[i]), 0, -1, + "expected %d got %d for column %d\n", i); + } + for (i = 1; i < 3; i++) { + assert_equal_return(test_columns[i], columns[i], -1, + "expected %p got %p for column %d\n", + i); + } + for (i = 3; i < 4; i++) { + assert_equal_return(strcmp(test_columns[i], columns[i]), 0, -1, + "expected %d got %d for column %d\n", i); + } + + return 0; +} + +int +test_parse_csv_line_comma_size_2(void) +{ + char *s0; + char *columns[] = { "a", "b", "c", "d" }; + char *test_columns[] = { "", "", NULL, "d" }; + size_t n_columns = 3; + size_t max = 2; + size_t i; + + /* + * For reasons unknown, when I do this the normal way with: + * char *s0 = ","; + * gcc is putting it in .rodata, + * *** AND combining it with the "," from delims from parse_csv_line***. + */ + s0 = alloca(2); + s0[0] = ','; + s0[1] = '\0'; + + parse_csv_line(s0, max, &n_columns, (const char **)columns); + + assert_equal_return(s0[0], '\0', -1, "got %#hhx expected %#hhx\n"); + assert_equal_return(n_columns, 2, -1, "got %#hhx expected %#hhx\n"); + for (i = 0; i < 2; i++) { + assert_equal_return(strcmp(test_columns[i], columns[i]), 0, -1, + "expected %d got %d for column %d\n", i); + } + for (i = 2; i < 3; i++) { + assert_equal_return(test_columns[i], columns[i], -1, + "expected %p got %p for column %d\n", + i); + } + for (i = 3; i < 4; i++) { + assert_equal_return(strcmp(test_columns[i], columns[i]), 0, -1, + "expected %d got %d for column %d\n", i); + } + + return 0; +} + +int +test_csv_0(void) +{ + char csv[] = + "\000\000\000" + "a,b,c,d,e,f,g,h\n" + "a,b,c\n" + "\n" + "\n" + "a,b,c,d,e,f,g,h\n" + "a,b,c"; + struct test_entry test_entries[]= { + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 3, { "a", "b", "c", NULL, NULL, NULL, NULL } }, + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 3, { "a", "b", "c", NULL, NULL, NULL, NULL } }, + }; + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + memcpy(csv, (char [])UTF8_BOM, UTF8_BOM_SIZE); + + current = csv; + end = csv + sizeof(csv) - 1; + + efi_status = parse_csv_data(current, end, 7, &entry_list); + assert_equal_return(efi_status, EFI_SUCCESS, -1, "got %x expected %x\n"); + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + struct test_entry *test_entry = &test_entries[i++]; + size_t j; + + assert_goto(i > 0 && i <= 4, fail, "got %d expected 0 to 4\n", i); + + csv_row = list_entry(pos, struct csv_row, list); + + assert_equal_goto(csv_row->n_columns, test_entry->n_columns, + fail, "got %d expected %d\n"); + for (j = 0; j < csv_row->n_columns; j++) { + assert_equal_goto(strcmp(csv_row->columns[j], + test_entry->columns[j]), 0, + fail, "got %d expected %d\n"); + } + } + + assert_equal_return(list_size(&entry_list), 4, -1, + "got %d expected %d\n"); + free_csv_list(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + return 0; +fail: + free_csv_list(&entry_list); + return -1; +} + +int +test_csv_1(void) +{ + char csv[] = + "a,b,c,d,e,f,g,h\n" + "a,b,c\n" + "\n" + "\n" + "a,b,c,d,e,f,g,h\n" + "a,b,c"; + struct test_entry test_entries[]= { + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 3, { "a", "b", "c", NULL, NULL, NULL, NULL } }, + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 3, { "a", "b", "c", NULL, NULL, NULL, NULL } }, + }; + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + current = csv; + end = csv + sizeof(csv) - 1; + + efi_status = parse_csv_data(current, end, 7, &entry_list); + assert_equal_return(efi_status, EFI_SUCCESS, -1, "got %x expected %x\n"); + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + struct test_entry *test_entry = &test_entries[i++]; + size_t j; + + assert_goto(i > 0 && i <= 4, fail, "got %d expected 0 to 4\n", i); + + csv_row = list_entry(pos, struct csv_row, list); + + assert_equal_goto(csv_row->n_columns, test_entry->n_columns, + fail, "got %d expected %d\n"); + for (j = 0; j < csv_row->n_columns; j++) { + assert_equal_goto(strcmp(csv_row->columns[j], + test_entry->columns[j]), 0, + fail, "got %d expected %d\n"); + } + } + + assert_equal_return(list_size(&entry_list), 4, -1, + "got %d expected %d\n"); + free_csv_list(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + return 0; +fail: + free_csv_list(&entry_list); + return -1; +} + +int +test_csv_2(void) +{ + char csv[] = + "\000\000\000" + "a,b,c,d,e,f,g,h\n" + ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,c\n" + "\n" + "\n" + "a,b,c,d,e,f,g,h\n" + "a,b,c"; + struct test_entry test_entries[]= { + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 7, { "", "", "", "", "", "", "" } }, + { 7, { "a", "b", "c", "d", "e", "f", "g" } }, + { 3, { "a", "b", "c", NULL, NULL, NULL, NULL } }, + }; + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + memcpy(csv, (char [])UTF8_BOM, UTF8_BOM_SIZE); + + current = csv; + end = csv + sizeof(csv) - 1; + + efi_status = parse_csv_data(current, end, 7, &entry_list); + assert_equal_return(efi_status, EFI_SUCCESS, -1, "got %x expected %x\n"); + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + struct test_entry *test_entry = &test_entries[i++]; + size_t j; + + assert_goto(i > 0 && i <= 7, fail, "got %d expected 0 to 7\n", i); + csv_row = list_entry(pos, struct csv_row, list); + + assert_equal_goto(csv_row->n_columns, test_entry->n_columns, + fail, "got %d expected %d\n"); + for (j = 0; j < csv_row->n_columns; j++) { + assert_equal_goto(strcmp(csv_row->columns[j], + test_entry->columns[j]), 0, + fail, "got %d expected %d\n"); + } + } + + free_csv_list(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + return 0; +fail: + free_csv_list(&entry_list); + return -1; +} + +int +test_simple_sbat_csv(void) +{ + char csv[] = + "test1,1,SBAT test1,acme1,1,testURL1\n" + "test2,2,SBAT test2,acme2,2,testURL2\n"; + struct test_entry test_entries[]= { + { 6, { "test1", "1", "SBAT test1", "acme1", "1", "testURL1" } }, + { 6, { "test2", "2", "SBAT test2", "acme2", "2", "testURL2" } }, + }; + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + current = csv; + end = csv + sizeof(csv) - 1; + + efi_status = parse_csv_data(current, end, 6, &entry_list); + assert_equal_return(efi_status, EFI_SUCCESS, -1, + "got %d expected %d\n"); + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + struct test_entry *test_entry = &test_entries[i++]; + size_t j; + + csv_row = list_entry(pos, struct csv_row, list); + + assert_equal_goto(csv_row->n_columns, test_entry->n_columns, + fail, "got %d expected %d"); + + for (j = 0; j < csv_row->n_columns; j++) { + assert_equal_goto(strcmp(csv_row->columns[j], + test_entry->columns[j]), 0, + fail, "got %d expected %d\n"); + } + } + + assert_equal_return(list_size(&entry_list), 2, -1, + "got %d expected %d\n"); + free_csv_list(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + return 0; +fail: + free_csv_list(&entry_list); + return -1; + +} + +int +test_csv_simple_fuzz(char *random_bin, size_t random_bin_len, + bool assert_entries) +{ + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + current = &random_bin[0]; + current = current + 1 - 1; + end = current + random_bin_len - 1; + *end = '\0'; + + efi_status = parse_csv_data(current, end, 7, &entry_list); + assert_equal_return(efi_status, EFI_SUCCESS, -1, "expected %#x got %#x\n"); + printf("parsed %zd entries\n", list_size(&entry_list)); + if (assert_entries) + assert_goto(list_size(&entry_list) > 0, fail, + "expected >0 entries\n"); + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + + csv_row = list_entry(pos, struct csv_row, list); + dprint("row[%zd]: %zd columns\n", i, csv_row->n_columns); + i++; + } + + free_csv_list(&entry_list); + assert_equal_return(list_size(&entry_list), 0, -1, + "got %d expected %d\n"); + + return 0; +fail: + free_csv_list(&entry_list); + return -1; +} + +#include "test-random.h" + +int +main(void) +{ + int status = 0; + size_t i, j; + + setbuf(stdout, NULL); + test(test_parse_csv_line_size_0); + test(test_parse_csv_line_size_1); + test(test_parse_csv_line_comma_size_1); + test(test_parse_csv_line_comma_size_2); + test(test_csv_0); + test(test_csv_1); + test(test_csv_2); + test(test_simple_sbat_csv); + test(test_csv_simple_fuzz, random_bin, random_bin_len, false); + for (i = 0; i < random_bin_len; i++) { + j = i; + while (random_bin[i] == '\0') + random_bin[i] = j++; + } + test(test_csv_simple_fuzz, random_bin, random_bin_len, true); + + return status; +} + +// vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 16f94b261f5b95b4cf5787b27312775cbcd60cc8 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Feb 2021 15:30:20 -0500 Subject: Fix-up and enable a bunch of .sbat section parsing tests. This brings all the tests Chris Co wrote about parsing the .sbat section back. Some of the actual test functions became redundant, and some new ones were needed, but all of the actual test cases should be represented here. Note that building and running this test does not quite work yet /on this branch/. In order to do that, we need some cleanups and reorganizing that I don't want to push just yet, which can be found on https://github.com/rhboot/shim/tree/test-reorg Signed-off-by: Peter Jones --- include/test.mk | 2 + test-sbat.c | 395 ++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 255 insertions(+), 142 deletions(-) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 8cdcc332..735d0dc5 100644 --- a/include/test.mk +++ b/include/test.mk @@ -34,6 +34,8 @@ test-random.h: dd if=/dev/urandom bs=512 count=17 of=random.bin xxd -i random.bin test-random.h +test-sbat_FILES = csv.c + tests := $(patsubst %.c,%,$(wildcard test-*.c)) $(tests) :: test-% : test.c test-%.c $(test-%_FILES) diff --git a/test-sbat.c b/test-sbat.c index 7a4b767a..c671f03e 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -185,208 +185,249 @@ free_mock_sbat_entries(list_t *entries) FreePool(entries); } } +#endif /* - * parse_sbat() tests + * parse_sbat_section() tests */ -void -test_parse_sbat_null_sbat_base(void) +int +test_parse_sbat_section_null_sbat_base(void) { - size_t sbat_size = 20; - struct sbat sbat = { 0, NULL }; + char *section_base = NULL; + size_t section_size = 20; + struct sbat_section_entry **entries; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(NULL, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, &n, &entries); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_zero_sbat_size(void) +int +test_parse_sbat_section_zero_sbat_size(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; - size_t sbat_size = 0; - struct sbat sbat = { 0, NULL }; + char section_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; + size_t section_size = 0; + struct sbat_section_entry **entries; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, &n, &entries); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_null_sbat(void) +int +test_parse_sbat_section_null_entries(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; - size_t sbat_size = sizeof(sbat_base); + char section_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, NULL); + status = parse_sbat_section(section_base, section_size, &n, NULL); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_no_newline(void) +int +test_parse_sbat_section_null_count(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + char section_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + struct sbat_section_entry **entries; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, NULL, &entries); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_too_few_elem(void) +int +test_parse_sbat_section_no_newline(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + char section_base[] = "test1,1,SBAT test1,acme,1,testURL"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + struct sbat_section_entry **entries; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, &n, &entries); + cleanup_sbat_section_entries(n, entries); + assert_equal_return(status, EFI_SUCCESS, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_too_many_elem(void) +int +test_parse_sbat_section_no_commas(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL,testURL2\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + char section_base[] = "test1"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + struct sbat_section_entry **entries; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, &n, &entries); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_SUCCESS); - assert(sbat.size == 1); - assert(strncmp(sbat.entries[0]->component_name, "test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->component_generation, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_name, "SBAT test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_package_name, "acme", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_version, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_url, "testURL,testURL2", MAX_SIZE) == 0); + return 0; } -void -test_parse_sbat_no_newline_multiple_entries(void) +int +test_parse_sbat_section_too_few_elem(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n" \ - "test2,2,SBAT test2,acme2,2,testURL2"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + char section_base[] = "test1,1,acme"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + struct sbat_section_entry **entries; + size_t n = 0; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + status = parse_sbat_section(section_base, section_size, &n, &entries); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_too_few_elem_multiple_entries(void) +int +test_parse_sbat_section_too_many_elem(void) +{ + char section_base[] = "test1,1,SBAT test1,acme1,1,testURL1,other1,stuff,is,here\n" + "test2,2,SBAT test2,acme2,2,testURL2,other2"; + /* intentionally not NUL terminated */ + size_t section_size = sizeof(section_base) - 1; + struct sbat_section_entry **entries; + size_t n = 0, i; + list_t *pos = NULL; + EFI_STATUS status; + struct sbat_section_entry test_section_entry1 = { + "test1", "1", "SBAT test1", "acme1", "1", "testURL1" + }; + struct sbat_section_entry test_section_entry2 = { + "test2", "2", "SBAT test2", "acme2", "2", "testURL2" + }; + struct sbat_section_entry *test_entries[] = { + &test_section_entry1, &test_section_entry2, + }; + + status = parse_sbat_section(section_base, section_size, &n, &entries); + assert_equal_return(status, EFI_SUCCESS, -1, "got %#hhx expected %#hhx\n"); + + for (i = 0; i < n; i++) { + struct sbat_section_entry *entry = entries[i]; + struct sbat_section_entry *test_entry = test_entries[i]; + +#define mkassert(a) \ + assert_equal_goto(strcmp(entry-> a, test_entry-> a), 0, fail, \ + "got %zu expected %d\n") + + mkassert(component_name); + mkassert(component_generation); + mkassert(vendor_name); + mkassert(vendor_package_name); + mkassert(vendor_version); + mkassert(vendor_url); + +#undef mkassert + } + assert_equal_goto(n, 2, fail, "got %zu expected %d\n"); + return 0; +fail: + cleanup_sbat_section_entries(n, entries); + return -1; +} + +/* + * parse_sbat_var() tests + */ +int +test_parse_sbat_var_null_list(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n" \ - "test2,2,SBAT test2,acme2,2\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var(NULL); + cleanup_sbat_var(&sbat_var); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); - assert(status == EFI_INVALID_PARAMETER); + return 0; } -void -test_parse_sbat_too_many_elem_multiple_entries(void) -{ - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n" \ - "test2,2,SBAT test2,acme2,2,testURL2,test3\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; +int +test_parse_sbat_var_data_null_list(void) +{ + char sbat_var_data[] = "test1,1,2021022400"; + /* + * intentionally including the NUL termination, because + * get_variable() will always include it. + */ + size_t sbat_var_data_size = sizeof(sbat_var_data); EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var_data(NULL, sbat_var_data, sbat_var_data_size); + cleanup_sbat_var(&sbat_var); - assert(status == EFI_SUCCESS); - assert(sbat.size == 2); - assert(strncmp(sbat.entries[0]->component_name, "test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->component_generation, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_name, "SBAT test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_package_name, "acme", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_version, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_url, "testURL", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->component_name, "test2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->component_generation, "2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_name, "SBAT test2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_package_name, "acme2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_version, "2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_url, "testURL2,test3", MAX_SIZE) == 0); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); + + return 0; } -void -test_parse_sbat_single_entry(void) +int +test_parse_sbat_var_data_null_data(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + size_t sbat_var_data_size = 4; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var_data(&sbat_var, NULL, sbat_var_data_size); + cleanup_sbat_var(&sbat_var); - assert(status == EFI_SUCCESS); - assert(sbat.size == 1); - assert(strncmp(sbat.entries[0]->component_name, "test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->component_generation, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_name, "SBAT test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_package_name, "acme", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_version, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_url, "testURL", MAX_SIZE) == 0); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); + + return 0; } -void -test_parse_sbat_multiple_entries(void) +int +test_parse_sbat_var_data_zero_size(void) { - char sbat_base[] = "test1,1,SBAT test1,acme,1,testURL\n" \ - "test2,2,SBAT test2,acme2,2,testURL2\n"; - size_t sbat_size = sizeof(sbat_base); - struct sbat sbat = { 0 }; + char sbat_var_data[] = "test1,1,2021022400"; EFI_STATUS status; - status = parse_sbat(sbat_base, sbat_size, &sbat); + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var_data(&sbat_var, sbat_var_data, 0); + cleanup_sbat_var(&sbat_var); - assert(status == EFI_SUCCESS); - assert(sbat.size == 2); - assert(strncmp(sbat.entries[0]->component_name, "test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->component_generation, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_name, "SBAT test1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_package_name, "acme", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_version, "1", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[0]->vendor_url, "testURL", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->component_name, "test2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->component_generation, "2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_name, "SBAT test2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_package_name, "acme2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_version, "2", MAX_SIZE) == 0); - assert(strncmp(sbat.entries[1]->vendor_url, "testURL2", MAX_SIZE) == 0); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); + + return 0; } -/* - * parse_sbat_var() tests - */ -void -test_parse_sbat_var_invalid_list(void) +int +test_parse_sbat_var_data(void) { + char sbat_var_data[] = "test1,1,2021022400"; EFI_STATUS status; - status = parse_sbat_var(NULL); + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var_data(&sbat_var, sbat_var_data, 0); - assert(status == EFI_INVALID_PARAMETER); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); + + return 0; } +#if 0 /* * verify_sbat() tests * Note: verify_sbat also frees the underlying "sbat_entries" memory. @@ -840,26 +881,95 @@ test_verify_sbat_reject_diff_name_mixed(void) } #endif +int +test_parse_and_verify(void) +{ + EFI_STATUS status; + char sbat_section[] = + "test1,1,SBAT test1,acme1,1,testURL1\n" + "test2,2,SBAT test2,acme2,2,testURL2\n"; + struct sbat_section_entry **section_entries = NULL; + size_t n_section_entries = 0, i; + struct sbat_section_entry test_section_entry1 = { + "test1", "1", "SBAT test1", "acme1", "1", "testURL1" + }; + struct sbat_section_entry test_section_entry2 = { + "test2", "2", "SBAT test2", "acme2", "2", "testURL2" + }; + struct sbat_section_entry *test_entries[] = { + &test_section_entry1, &test_section_entry2, + }; + + status = parse_sbat_section(sbat_section, sizeof(sbat_section)-1, + &n_section_entries, §ion_entries); + eassert(status == EFI_SUCCESS, "expected %d got %d\n", + EFI_SUCCESS, status); + eassert(section_entries != NULL, "expected non-NULL got NULL\n"); + + for (i = 0; i < n_section_entries; i++) { + struct sbat_section_entry *entry = section_entries[i]; + struct sbat_section_entry *test_entry = test_entries[i]; + +#define mkassert(a) \ + eassert(strcmp(entry-> a, test_entry-> a) == 0, \ + "expected \"%s\" got \"%s\"\n", \ + test_entry-> a, entry-> a ) + + mkassert(component_name); + mkassert(component_generation); + mkassert(vendor_name); + mkassert(vendor_package_name); + mkassert(vendor_version); + mkassert(vendor_url); + +#undef mkassert + } + + eassert(n_section_entries == 2, "expected %d got %d\n", + 2, n_section_entries); + + char sbat_var_data[] = "test1,5\nbar,2\n"; + size_t sbat_var_data_size = sizeof(sbat_var_data); + char *sbat_var_alloced = calloc(1, sbat_var_data_size); + if (!sbat_var_alloced) + return -1; + memcpy(sbat_var_alloced, sbat_var_data, sbat_var_data_size); + + INIT_LIST_HEAD(&sbat_var); + status = parse_sbat_var_data(&sbat_var, sbat_var_alloced, sbat_var_data_size); + if (status != EFI_SUCCESS || list_empty(&sbat_var)) + return -1; + + status = verify_sbat(n_section_entries, section_entries); + + assert_equal_return(status, EFI_SECURITY_VIOLATION, -1, "expected %#x got %#x\n"); + cleanup_sbat_var(&sbat_var); + cleanup_sbat_section_entries(n_section_entries, section_entries); + + return 0; +} + int main(void) { -#if 0 - // parse_sbat tests - test_parse_sbat_null_sbat_base(); - test_parse_sbat_zero_sbat_size(); - test_parse_sbat_null_sbat(); - //test_parse_sbat_no_newline(); - test_parse_sbat_too_few_elem(); - test_parse_sbat_too_many_elem(); - //test_parse_sbat_no_newline_multiple_entries(); - test_parse_sbat_too_few_elem_multiple_entries(); - test_parse_sbat_too_many_elem_multiple_entries(); - test_parse_sbat_single_entry(); - test_parse_sbat_multiple_entries(); + int status = 0; + // parse_sbat section tests + test(test_parse_sbat_section_null_sbat_base); + test(test_parse_sbat_section_zero_sbat_size); + test(test_parse_sbat_section_null_entries); + test(test_parse_sbat_section_null_count); + test(test_parse_sbat_section_no_newline); + test(test_parse_sbat_section_no_commas); + test(test_parse_sbat_section_too_few_elem); + test(test_parse_sbat_section_too_many_elem); // parse_sbat_var tests - test_parse_sbat_var_invalid_list(); + test(test_parse_sbat_var_null_list); + test(test_parse_sbat_var_data_null_list); + test(test_parse_sbat_var_data_null_data); + test(test_parse_sbat_var_data_zero_size); +#if 0 // verify_sbat tests //test_verify_sbat_null_sbat(); test_verify_sbat_null_sbat_entries(); @@ -874,6 +984,7 @@ main(void) test_verify_sbat_match_diff_name_mixed(); test_verify_sbat_reject_diff_name_mixed(); #endif + test_parse_and_verify(); return 0; } -- cgit v1.2.3 From 223c8e3cdf2f8af03d1d6ea4b0ac2b11b4674125 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 25 Feb 2021 09:55:34 -0500 Subject: Make verify_sbat() more testable Signed-off-by: Peter Jones --- include/sbat.h | 8 +++++--- sbat.c | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index c3e96179..69f4e78a 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -21,9 +21,6 @@ extern list_t sbat_var; #define SBAT_VAR_COLUMNS ((sizeof (struct sbat_var_entry) - sizeof(list_t)) / sizeof(CHAR8 *)) #define SBAT_VAR_REQUIRED_COLUMNS (SBAT_VAR_COLUMNS - 1) -#ifdef SHIM_UNIT_TEST -EFI_STATUS parse_sbat_var_data(list_t *entries, UINT8 *data, UINTN datasize); -#endif EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); @@ -44,5 +41,10 @@ void cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries) EFI_STATUS verify_sbat(size_t n, struct sbat_section_entry **entries); +#ifdef SHIM_UNIT_TEST +EFI_STATUS parse_sbat_var_data(list_t *entries, UINT8 *data, UINTN datasize); +EFI_STATUS verify_sbat_helper(list_t *sbat_var, size_t n, + struct sbat_section_entry **entries); +#endif /* !SHIM_UNIT_TEST */ #endif /* !SBAT_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/sbat.c b/sbat.c index 0353e790..21b21bff 100644 --- a/sbat.c +++ b/sbat.c @@ -150,28 +150,38 @@ cleanup_sbat_var(list_t *entries) } EFI_STATUS -verify_sbat(size_t n, struct sbat_section_entry **entries) +verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries) { unsigned int i; list_t *pos = NULL; EFI_STATUS efi_status = EFI_SUCCESS; struct sbat_var_entry *sbat_var_entry; - if (list_empty(&sbat_var)) { + if (list_empty(local_sbat_var)) { dprint(L"SBAT variable not present\n"); return EFI_SUCCESS; } for (i = 0; i < n; i++) { - list_for_each(pos, &sbat_var) { + list_for_each(pos, local_sbat_var) { sbat_var_entry = list_entry(pos, struct sbat_var_entry, list); efi_status = verify_single_entry(entries[i], sbat_var_entry); if (EFI_ERROR(efi_status)) - return efi_status; + goto out; } } - dprint(L"all entries from SBAT section verified\n"); +out: + dprint(L"finished verifying SBAT data: %r\n", efi_status); + return efi_status; +} + +EFI_STATUS +verify_sbat(size_t n, struct sbat_section_entry **entries) +{ + EFI_STATUS efi_status; + + efi_status = verify_sbat_helper(&sbat_var, n, entries); return efi_status; } -- cgit v1.2.3 From e90fe4421f888a4734c2bf710bc61fc5af354ee3 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Tue, 23 Feb 2021 15:00:58 +0800 Subject: sbat: fix the residual "resource section" for SBAT Signed-off-by: Gary Lin --- include/sbat.h | 2 +- pe.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 69f4e78a..5b64f20a 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent /* - * sbat.c - parse SBAT data from the .rsrc section data + * sbat.c - parse SBAT data from the .sbat section data */ #ifndef SBAT_H_ diff --git a/pe.c b/pe.c index 9da87530..9fa62d68 100644 --- a/pe.c +++ b/pe.c @@ -1033,7 +1033,7 @@ handle_image (void *data, unsigned int datasize, } } else if (CompareMem(Section->Name, ".sbat\0\0\0", 8) == 0) { if (SBATBase || SBATSize) { - perror(L"Image has multiple resource sections\n"); + perror(L"Image has multiple SBAT sections\n"); return EFI_UNSUPPORTED; } -- cgit v1.2.3 From 58cf755d5fd5b3d1f87a5936107a70705ccbb57b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 25 Feb 2021 23:18:24 -0500 Subject: Add get_variable_size()/set_variable()del_variable() wrappers. This get_variable_size() implementation success in either of two cases: - EFI_SUCCESS with *lenp == 0 if the variable isn't found - EFI_SUCCESS with *lenp > 0 on success In the event of other errors, it returns them to you. There's nothing particularly interesting about the set_variable() or del_variable() implementation here. Signed-off-by: Peter Jones --- include/variables.h | 6 ++++++ lib/variables.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/include/variables.h b/include/variables.h index 09d97c31..31cfcb65 100644 --- a/include/variables.h +++ b/include/variables.h @@ -40,6 +40,12 @@ get_variable(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner) EFI_STATUS get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner, UINT32 *attributes); EFI_STATUS +get_variable_size(const CHAR16 * const var, EFI_GUID owner, UINTN *lenp); +EFI_STATUS +set_variable(CHAR16 *var, EFI_GUID owner, UINT32 attributes, UINTN datasize, void *data); +EFI_STATUS +del_variable(CHAR16 *var, EFI_GUID owner); +EFI_STATUS find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen); EFI_STATUS find_in_variable_esl(const CHAR16 * const var, EFI_GUID owner, UINT8 *key, UINTN keylen); diff --git a/lib/variables.c b/lib/variables.c index 0431d4a2..6db069ef 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -259,6 +259,43 @@ get_variable(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner) return get_variable_attr(var, data, len, owner, NULL); } +EFI_STATUS +get_variable_size(const CHAR16 * const var, EFI_GUID owner, UINTN *lenp) +{ + UINTN len = 0; + EFI_STATUS efi_status; + + efi_status = get_variable_attr(var, NULL, &len, owner, NULL); + if (EFI_ERROR(efi_status)) { + if (efi_status == EFI_BUFFER_TOO_SMALL) { + *lenp = len; + return EFI_SUCCESS; + } else if (efi_status == EFI_NOT_FOUND) { + *lenp = 0; + return EFI_SUCCESS; + } + return efi_status; + } + /* + * who knows what this means, but... + */ + *lenp = len; + return efi_status; +} + +EFI_STATUS +set_variable(CHAR16 *var, EFI_GUID owner, UINT32 attributes, + UINTN datasize, void *data) +{ + return gRT->SetVariable(var, &owner, attributes, datasize, data); +} + +EFI_STATUS +del_variable(CHAR16 *var, EFI_GUID owner) +{ + return set_variable(var, owner, 0, 0, ""); +} + EFI_STATUS find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen) { -- cgit v1.2.3 From 7d5df1cebdbdc3ed11cde165a752cf358878aa8a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 8 Mar 2021 12:42:21 -0500 Subject: static analysis: make our build targets work better This improves our static analysis targets by making them work better with our make variables, and inhibits the use of ccache while building those. Signed-off-by: Peter Jones --- Cryptlib/Makefile | 3 +++ Cryptlib/OpenSSL/Makefile | 3 +++ Make.defaults | 9 +++++++++ include/coverity.mk | 37 +++++++++++++++++++++++++++++-------- include/fanalyzer.mk | 36 ++++++++++++++++++++++++------------ include/scan-build.mk | 35 +++++++++++++++++++++++++++++------ 6 files changed, 97 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 5bae10c9..bc5681c5 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -1,3 +1,6 @@ +ifneq ($(CCACHE_DISABLE),) +export CCACHE_DISABLE +endif INCLUDES = -I$(TOPDIR) -iquote $(TOPDIR) -I$(TOPDIR)/Include \ $(EFI_INCLUDES) -I$(shell $(CC) -print-file-name=include) diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index 6a58dbaa..6ff58e47 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -1,3 +1,6 @@ +ifneq ($(CCACHE_DISABLE),) +export CCACHE_DISABLE +endif DEFINES = -DL_ENDIAN \ -D_CRT_SECURE_NO_DEPRECATE \ diff --git a/Make.defaults b/Make.defaults index 5f30e292..7f1b8015 100644 --- a/Make.defaults +++ b/Make.defaults @@ -28,6 +28,15 @@ DASHJ ?= -j$(shell echo $$(($$(grep -c "^model name" /proc/cpuinfo) + 1))) ARCH ?= $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,) OBJCOPY_GTE224 = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.*\((.*)\|version\) //g' | cut -f1-2 -d.` \>= 2.24) OPTIMIZATIONS ?= -Os +FA_OPTIMIZATIONS ?= -O2 +ifneq ($(FANALYZER),) +override OPTIMIZATIONS := $(FA_OPTIMIZATIONS) +override CCACHE_DISABLE := true +endif +export OPTIMIZATIONS +ifneq ($(CCACHE_DISABLE),) +export CCACHE_DISABLE +endif SUBDIRS = $(TOPDIR)/Cryptlib $(TOPDIR)/lib diff --git a/include/coverity.mk b/include/coverity.mk index a897aa0a..e1e5c874 100644 --- a/include/coverity.mk +++ b/include/coverity.mk @@ -3,12 +3,18 @@ COV_TOKEN=$(call get-config,coverity.token) COV_URL=$(call get-config,coverity.url) COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 -cov-int : clean-shim-objs - make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a - cov-build --dir cov-int make $(DASHJ) all +include $(TOPDIR)/Make.rules -cov-int-all : clean - cov-build --dir cov-int make $(DASHJ) all +define prop +$(if $(findstring undefined,$(origin $(1))),,$(1)="$($1)") +endef + +override CCACHE_DISABLE := 1 +export CCACHE_DISABLE + +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE + +MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) cov-clean : @rm -vf $(NAME)-coverity-*.tar.* @@ -19,7 +25,7 @@ cov-file : | $(COV_FILE) $(COV_FILE) : | cov-int tar caf $@ cov-int -cov-upload : +cov-upload : | cov-file @if [ -n "$(COV_URL)" ] && \ [ -n "$(COV_TOKEN)" ] && \ [ -n "$(COV_EMAIL)" ] ; \ @@ -30,11 +36,26 @@ cov-upload : echo Coverity output is in $(COV_FILE) ; \ fi +cov-build-unchecked-cryptlib : | clean-cryptlib-objs +cov-build-unchecked-cryptlib : Cryptlib/libcryptlib.a + +cov-build-unchecked-openssl : | clean-openssl-objs +cov-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a + +cov-build-all : | clean clean-shim-objs clean-cryptlib-objs clean-openssl-objs + +cov-build --dir cov-int $(MAKE) $(MAKEARGS) CCACHE_DISABLE=1 all + +coverity-no-openssl : | cov-test +coverity-no-openssl : clean-shim-objs clean-cryptlib-objs cov-build-unchecked-openssl cov-build-all cov-file cov-upload + +coverity-no-cryptlib : | cov-test +coverity-no-cryptlib : clean-shim-objs cov-build-unchecked-openssl cov-build-unchecked-cryptlib cov-build-all cov-file cov-upload + coverity : | cov-test -coverity : cov-int cov-file cov-upload +coverity : coverity-no-openssl cov-file cov-upload coverity-all : | cov-test -coverity-all : cov-int-all cov-file cov-upload +coverity-all : clean cov-build-all cov-file cov-upload clean : | cov-clean diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk index 1018d1da..7e31a082 100644 --- a/include/fanalyzer.mk +++ b/include/fanalyzer.mk @@ -3,25 +3,37 @@ GCC_BINARY ?= $(shell x=$$(which --skip-alias --skip-functions gcc 2>/dev/null) fanalyzer-test : ; $(if $(findstring /,$(GCC_BINARY)),,$(error gcc not found)) define prop -$(if $(filter-out undefined,$(origin $(1))),$(1)=$($1),) +$(if $(findstring undefined,$(origin $(1))),,$(eval export $(1))) endef -MAKEARGS := \ - $(call prop,ARCH) \ - $(call prop,COLOR) \ - $(call prop,CROSS_COMPILE) +override CCACHE_DISABLE := 1 +export CCACHE_DISABLE +override COMPILER := gcc +export COMPILER + +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE DASHJ + +MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) fanalyzer : | fanalyzer-test -fanalyzer : clean-shim-objs fanalyzer-build +fanalyzer : fanalyzer-no-openssl + +fanalyzer-build-unchecked-cryptlib : Cryptlib/libcryptlib.a + +fanalyzer-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a -fanalyzer-build : - make CC=gcc $(MAKEARGS) $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a - make CC=gcc $(MAKEARGS) FANALYZER=true all +fanalyzer-build-all : CCACHE_DISABLE=1 +fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer +fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference +fanalyzer-build-all : all + +fanalyzer-no-openssl : | fanalyzer-test +fanalyzer-no-openssl : clean-shim-objs clean-cryptlib-objs fanalyzer-build-unchecked-openssl fanalyzer-build-all + +fanalyzer-no-cryptlib : | fanalyzer-test +fanalyzer-no-cryptlib : clean-shim-objs fanalyzer-build-unchecked-openssl fanalyzer-build-unchecked-cryptlib fanalyzer-build-all fanalyzer-all : | fanalyzer-test fanalyzer-all : clean fanalyzer-build-all -fanalyzer-build-all : - make CC=gcc $(MAKEARGS) FANALYZER=true all - .PHONY : fanalyzer fanalyzer-build fanalyzer-all fanalyzer-build-all fanalyzer-clean diff --git a/include/scan-build.mk b/include/scan-build.mk index 7697cb89..2cb33e79 100644 --- a/include/scan-build.mk +++ b/include/scan-build.mk @@ -2,16 +2,39 @@ SCAN_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions scan-build 2>/dev scan-test : ; $(if $(findstring /,$(SCAN_BUILD)),,$(error scan-build not found)) +define prop +$(if $(findstring undefined,$(origin $(1))),,$(1)="$($1)") +endef + +override CCACHE_DISABLE := 1 +export CCACHE_DISABLE +override COMPILER = clang + +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE DASHJ + +MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) + scan-clean : @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi -scan-build : | scan-test -scan-build : clean-shim-objs - make $(DASHJ) Cryptlib/OpenSSL/libopenssl.a Cryptlib/libcryptlib.a - scan-build -o scan-results make $(DASHJ) CC=clang all +scan : | scan-test +scan : clean-shim-objs clean-cryptlib-objs scan-build-no-openssl + +scan-build-unchecked-cryptlib : Cryptlib/libcryptlib.a + +scan-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a scan-build-all : | scan-test -scan-build-all : clean - scan-build -o scan-results make $(DASHJ) CC=clang all +scan-build-all : + +scan-build -o scan-results make $(MAKEARGS) $(DASHJ) CCACHE_DISABLE=1 all + +scan-build-no-openssl : | scan-test +scan-build-no-openssl : clean-shim-objs clean-cryptlib-objs scan-build-unchecked-openssl scan-build-all + +scan-build-no-cryptlib : | scan-test +scan-build-no-cryptlib : clean-shim-objs scan-build-unchecked-cryptlib scan-build-unchecked-openssl scan-build-all + +scan-all : | scan-test +scan-all : clean scan-build-all .PHONY : scan-build scan-clean -- cgit v1.2.3 From f033a1da9f4c3acf7e3dfef906d01e348b6fcf42 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Mar 2021 11:42:34 -0500 Subject: Restructure our includes. This re-structures our includes so we can be sure everything is always including all the system headers in a uniform, predictable way. Temporarily it also adds a bunch of junk at all the places we use variadic functions to specifically pick either the MS (cdecl) or ELF ABIs. I'm not 100% sure that's all correct (see later patch) but it's enough to allow this to build. Signed-off-by: Peter Jones --- Cryptlib/Include/OpenSslSupport.h | 13 +++++++----- Cryptlib/Include/ctype.h | 16 -------------- Cryptlib/Include/openssl/crypto.h | 1 + Cryptlib/Include/stdarg.h | 16 -------------- Cryptlib/Include/stddef.h | 15 -------------- Cryptlib/Include/stdlib.h | 16 -------------- Cryptlib/Include/string.h | 16 -------------- Cryptlib/Include/strings.h | 15 -------------- Cryptlib/InternalCryptLib.h | 2 ++ Cryptlib/Makefile | 14 +++++++++---- Cryptlib/OpenSSL/Makefile | 16 +++++++++----- Cryptlib/OpenSSL/crypto/bio/b_print.c | 8 +++---- Make.defaults | 3 ++- Makefile | 7 ++++--- MokManager.c | 8 +------ PasswordCrypt.c | 6 ++---- crypt_blowfish.c | 5 ----- errlog.c | 29 +++++++++++++------------- fallback.c | 4 ---- httpboot.c | 4 ---- include/console.h | 8 +++---- include/hexdump.h | 17 ++++++++------- include/system/alloca.h | 10 +++++++++ include/system/ctype.h | 14 +++++++++++++ include/system/inttypes.h | 13 ++++++++++++ include/system/stdarg.h | 31 ++++++++++++++++++++++++++++ include/system/stdio.h | 13 ++++++++++++ include/system/stdlib.h | 16 ++++++++++++++ include/system/string.h | 14 +++++++++++++ include/system/strings.h | 10 +++++++++ include/test.h | 4 ++-- lib/Makefile | 39 +++++++++++++++++++++++++++++++++-- lib/configtable.c | 3 --- lib/console.c | 21 +++++++------------ lib/execute.c | 4 ---- lib/print_crypto.c | 5 ----- lib/security_policy.c | 4 ---- lib/shell.c | 3 --- lib/simple_file.c | 4 ---- lib/variables.c | 3 --- mok.c | 4 ---- netboot.c | 2 -- pe.c | 1 - replacements.c | 5 ----- sbat.c | 1 - shim.c | 1 - shim.h | 25 ++++++++++++++++------ test.c | 3 ++- tpm.c | 6 ------ 49 files changed, 262 insertions(+), 236 deletions(-) delete mode 100644 Cryptlib/Include/ctype.h delete mode 100644 Cryptlib/Include/stdarg.h delete mode 100644 Cryptlib/Include/stddef.h delete mode 100644 Cryptlib/Include/stdlib.h delete mode 100644 Cryptlib/Include/string.h delete mode 100644 Cryptlib/Include/strings.h create mode 100644 include/system/alloca.h create mode 100644 include/system/ctype.h create mode 100644 include/system/inttypes.h create mode 100644 include/system/stdarg.h create mode 100644 include/system/stdio.h create mode 100644 include/system/stdlib.h create mode 100644 include/system/string.h create mode 100644 include/system/strings.h (limited to 'include') diff --git a/Cryptlib/Include/OpenSslSupport.h b/Cryptlib/Include/OpenSslSupport.h index 0b555271..7af9650f 100644 --- a/Cryptlib/Include/OpenSslSupport.h +++ b/Cryptlib/Include/OpenSslSupport.h @@ -15,6 +15,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef __OPEN_SSL_SUPPORT_H__ #define __OPEN_SSL_SUPPORT_H__ +/* + * Include stddef.h to avoid redefining "offsetof" + */ +#include +#include +#include +#include + #include #include #include "Base.h" @@ -23,11 +31,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "Library/MemoryAllocationLib.h" #include "Library/DebugLib.h" -/* - * Include stddef.h to avoid redefining "offsetof" - */ -#include - #define CONST const // diff --git a/Cryptlib/Include/ctype.h b/Cryptlib/Include/ctype.h deleted file mode 100644 index ee07f6bc..00000000 --- a/Cryptlib/Include/ctype.h +++ /dev/null @@ -1,16 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include - diff --git a/Cryptlib/Include/openssl/crypto.h b/Cryptlib/Include/openssl/crypto.h index bea4ca19..e201a123 100644 --- a/Cryptlib/Include/openssl/crypto.h +++ b/Cryptlib/Include/openssl/crypto.h @@ -117,6 +117,7 @@ #ifndef HEADER_CRYPTO_H # define HEADER_CRYPTO_H +# include # include # include diff --git a/Cryptlib/Include/stdarg.h b/Cryptlib/Include/stdarg.h deleted file mode 100644 index ee07f6bc..00000000 --- a/Cryptlib/Include/stdarg.h +++ /dev/null @@ -1,16 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include - diff --git a/Cryptlib/Include/stddef.h b/Cryptlib/Include/stddef.h deleted file mode 100644 index 8dfc36ff..00000000 --- a/Cryptlib/Include/stddef.h +++ /dev/null @@ -1,15 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include diff --git a/Cryptlib/Include/stdlib.h b/Cryptlib/Include/stdlib.h deleted file mode 100644 index ee07f6bc..00000000 --- a/Cryptlib/Include/stdlib.h +++ /dev/null @@ -1,16 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include - diff --git a/Cryptlib/Include/string.h b/Cryptlib/Include/string.h deleted file mode 100644 index ee07f6bc..00000000 --- a/Cryptlib/Include/string.h +++ /dev/null @@ -1,16 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include - diff --git a/Cryptlib/Include/strings.h b/Cryptlib/Include/strings.h deleted file mode 100644 index 8dfc36ff..00000000 --- a/Cryptlib/Include/strings.h +++ /dev/null @@ -1,15 +0,0 @@ -/** @file - Include file to support building OpenSSL Crypto Library. - -Copyright (c) 2010, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include diff --git a/Cryptlib/InternalCryptLib.h b/Cryptlib/InternalCryptLib.h index e9a4c20a..dc1a95e6 100644 --- a/Cryptlib/InternalCryptLib.h +++ b/Cryptlib/InternalCryptLib.h @@ -15,6 +15,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef __INTERNAL_CRYPT_LIB_H__ #define __INTERNAL_CRYPT_LIB_H__ +#include + #include "Library/BaseLib.h" #include "Library/BaseMemoryLib.h" #include "Library/MemoryAllocationLib.h" diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index bc5681c5..65a3918c 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -2,8 +2,14 @@ ifneq ($(CCACHE_DISABLE),) export CCACHE_DISABLE endif -INCLUDES = -I$(TOPDIR) -iquote $(TOPDIR) -I$(TOPDIR)/Include \ - $(EFI_INCLUDES) -I$(shell $(CC) -print-file-name=include) +CRYPTDIR = $(TOPDIR)/Cryptlib + +FEATUREFLAGS += -nostdinc + +INCLUDES = -I$(CRYPTDIR) -I$(CRYPTDIR)/Include \ + $(EFI_INCLUDES) \ + -isystem $(TOPDIR)/include/system \ + -isystem $(shell $(CC) -print-file-name=include) CFLAGS = $(FEATUREFLAGS) \ $(OPTIMIZATIONS) \ @@ -15,12 +21,12 @@ CFLAGS = $(FEATUREFLAGS) \ CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) ifeq ($(ARCH),x86_64) -FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) +FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone $(CLANG_BUGS) DEFINES += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ -DNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 endif ifeq ($(ARCH),ia32) -FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) +FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone $(CLANG_BUGS) DEFINES += -DMDE_CPU_IA32 endif ifeq ($(ARCH),aarch64) diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index 5bd72481..294e889a 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -2,17 +2,23 @@ ifneq ($(CCACHE_DISABLE),) export CCACHE_DISABLE endif +CRYPTDIR = $(TOPDIR)/Cryptlib +OSSLDIR = $(TOPDIR)/Cryptlib/OpenSSL + DEFINES = -DL_ENDIAN \ -D_CRT_SECURE_NO_DEPRECATE \ -D_CRT_NONSTDC_NO_DEPRECATE \ -DOPENSSL_SMALL_FOOTPRINT \ -DPEDANTIC -INCLUDES = -I$(TOPDIR) -I$(TOPDIR)/.. -I$(TOPDIR)/../Include/ -I$(TOPDIR)/crypto \ - -I$(shell $(CC) -print-file-name=include) \ - -I$(TOPDIR)/../Include $(EFI_INCLUDES) \ - -I$(TOPDIR)/crypto/asn1 -I$(TOPDIR)/crypto/evp \ - -I$(TOPDIR)/crypto/modes -I$(TOPDIR)/crypto/include +INCLUDES = -I$(OSSLDIR) -I$(CRYPTDIR) -I$(OSSLDIR)/Include/ \ + -I$(OSSLDIR)/crypto -I$(CRYPTDIR)/Include $(EFI_INCLUDES) \ + -I$(OSSLDIR)/crypto/asn1 -I$(OSSLDIR)/crypto/evp \ + -I$(OSSLDIR)/crypto/modes -I$(OSSLDIR)/crypto/include \ + -isystem $(TOPDIR)/include/system \ + -isystem $(shell $(CC) -print-file-name=include) + +FEATUREFLAGS += -nostdinc WERRFLAGS += -Wno-error=discarded-qualifiers \ -Wno-error=maybe-uninitialized \ diff --git a/Cryptlib/OpenSSL/crypto/bio/b_print.c b/Cryptlib/OpenSSL/crypto/bio/b_print.c index fea73864..2d303ee8 100644 --- a/Cryptlib/OpenSSL/crypto/bio/b_print.c +++ b/Cryptlib/OpenSSL/crypto/bio/b_print.c @@ -134,9 +134,9 @@ static int fmtfp(char **, char **, size_t *, size_t *, LDOUBLE, int, int, int); #endif static int doapr_outch(char **, char **, size_t *, size_t *, int); -static int _dopr(char **sbuffer, char **buffer, - size_t *maxlen, size_t *retlen, int *truncated, - const char *format, va_list args); +static int EFIAPI _dopr(char **sbuffer, char **buffer, + size_t *maxlen, size_t *retlen, int *truncated, + const char *format, va_list args); /* format read states */ #define DP_S_DEFAULT 0 @@ -167,7 +167,7 @@ static int _dopr(char **sbuffer, char **buffer, #define char_to_int(p) (p - '0') #define OSSL_MAX(p,q) ((p >= q) ? p : q) -static int +static int EFIAPI _dopr(char **sbuffer, char **buffer, size_t *maxlen, diff --git a/Make.defaults b/Make.defaults index bef3cb51..ebb9e3c3 100644 --- a/Make.defaults +++ b/Make.defaults @@ -102,7 +102,8 @@ INCLUDES = -nostdinc \ -I$(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \ -I$(TOPDIR)/Cryptlib -I$(TOPDIR)/Cryptlib/Include \ -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH_GNUEFI) -I$(EFI_INCLUDE)/protocol \ - -I$(TOPDIR)/include -iquote $(TOPDIR) -iquote $(shell pwd) + -I$(TOPDIR)/include -iquote $(TOPDIR) -iquote $(shell pwd) \ + -isystem $(TOPDIR)/include/system override DEFAULT_FEATUREFLAGS = \ -std=gnu11 \ diff --git a/Makefile b/Makefile index c1d13947..6a62e00a 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ override TOPDIR := $(shell pwd) endif override TOPDIR := $(abspath $(TOPDIR)) VPATH = $(TOPDIR) +export TOPDIR include $(TOPDIR)/Make.rules include $(TOPDIR)/Make.defaults @@ -134,15 +135,15 @@ gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: Cryptlib/libcryptlib.a: for i in Hash Hmac Cipher Rand Pk Pem SysCall; do mkdir -p Cryptlib/$$i; done - $(MAKE) VPATH=$(TOPDIR)/Cryptlib TOPDIR=$(TOPDIR)/Cryptlib -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile + $(MAKE) VPATH=$(TOPDIR)/Cryptlib -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile Cryptlib/OpenSSL/libopenssl.a: for i in x509v3 x509 txt_db stack sha rsa rc4 rand pkcs7 pkcs12 pem ocsp objects modes md5 lhash kdf hmac evp err dso dh conf comp cmac buffer bn bio async/arch asn1 aes; do mkdir -p Cryptlib/OpenSSL/crypto/$$i; done - $(MAKE) VPATH=$(TOPDIR)/Cryptlib/OpenSSL TOPDIR=$(TOPDIR)/Cryptlib/OpenSSL -C Cryptlib/OpenSSL -f $(TOPDIR)/Cryptlib/OpenSSL/Makefile + $(MAKE) VPATH=$(TOPDIR)/Cryptlib/OpenSSL -C Cryptlib/OpenSSL -f $(TOPDIR)/Cryptlib/OpenSSL/Makefile lib/lib.a: | $(TOPDIR)/lib/Makefile $(wildcard $(TOPDIR)/include/*.[ch]) if [ ! -d lib ]; then mkdir lib ; fi - $(MAKE) VPATH=$(TOPDIR)/lib TOPDIR=$(TOPDIR) CFLAGS="$(CFLAGS)" -C lib -f $(TOPDIR)/lib/Makefile lib.a + $(MAKE) VPATH=$(TOPDIR)/lib -C lib -f $(TOPDIR)/lib/Makefile lib.a buildid : $(TOPDIR)/buildid.c $(CC) -Og -g3 -Wall -Werror -Wextra -o $@ $< -lelf diff --git a/MokManager.c b/MokManager.c index 5a851d86..cd1492f8 100644 --- a/MokManager.c +++ b/MokManager.c @@ -1,18 +1,12 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent +#include "shim.h" -#include -#include -#include #include #include #include #include #include -#include "shim.h" - -#include "hexdump.h" - #define PASSWORD_MAX 256 #define PASSWORD_MIN 1 #define SB_PASSWORD_LEN 16 diff --git a/PasswordCrypt.c b/PasswordCrypt.c index 311c914b..1030a6dd 100644 --- a/PasswordCrypt.c +++ b/PasswordCrypt.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent -#include -#include +#include "shim.h" + #include #include #include -#include "shim.h" - #define TRAD_DES_HASH_SIZE 13 /* (64/6+1) + (12/6) */ #define BSDI_DES_HASH_SIZE 20 /* (64/6+1) + (24/6) + 4 + 1 */ #define BLOWFISH_HASH_SIZE 31 /* 184/6+1 */ diff --git a/crypt_blowfish.c b/crypt_blowfish.c index 7a474f26..b1eb0e60 100644 --- a/crypt_blowfish.c +++ b/crypt_blowfish.c @@ -43,11 +43,6 @@ * Blowfish library (I can't be sure if I would think of something if I * hadn't seen his code). */ - -#include -#include - -/* Just to make sure the prototypes match the actual definitions */ #include "shim.h" typedef unsigned int BF_word; diff --git a/errlog.c b/errlog.c index 714d09d3..16af23b0 100644 --- a/errlog.c +++ b/errlog.c @@ -5,30 +5,29 @@ */ #include "shim.h" -#include "hexdump.h" static CHAR16 **errs = NULL; static UINTN nerrs = 0; -EFI_STATUS -vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, va_list args) +EFI_STATUS EFIAPI +vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, elf_va_list args) { - va_list args2; + elf_va_list args2; EFI_STATUS efi_status = EFI_SUCCESS; if (verbose) { - va_copy(args2, args); + elf_va_copy(args2, args); console_print(L"%a:%d:%a() ", file, line, func); efi_status = VPrint(fmt, args2); - va_end(args2); + elf_va_end(args2); } return efi_status; } -EFI_STATUS -VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, va_list args) +EFI_STATUS EFIAPI +VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, elf_va_list args) { - va_list args2; + elf_va_list args2; CHAR16 **newerrs; newerrs = ReallocatePool(errs, (nerrs + 1) * sizeof(*errs), @@ -39,11 +38,11 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, va_li newerrs[nerrs] = PoolPrint(L"%a:%d %a() ", file, line, func); if (!newerrs[nerrs]) return EFI_OUT_OF_RESOURCES; - va_copy(args2, args); + elf_va_copy(args2, args); newerrs[nerrs+1] = VPoolPrint(fmt, args2); if (!newerrs[nerrs+1]) return EFI_OUT_OF_RESOURCES; - va_end(args2); + elf_va_end(args2); nerrs += 2; newerrs[nerrs] = NULL; @@ -52,15 +51,15 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, va_li return EFI_SUCCESS; } -EFI_STATUS +EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { - va_list args; + elf_va_list args; EFI_STATUS efi_status; - va_start(args, fmt); + elf_va_start(args, fmt); efi_status = VLogError(file, line, func, fmt, args); - va_end(args); + elf_va_end(args); return efi_status; } diff --git a/fallback.c b/fallback.c index fc81c5e4..ba90bb3b 100644 --- a/fallback.c +++ b/fallback.c @@ -3,10 +3,6 @@ * Copyright Red Hat, Inc. * Copyright Peter Jones */ - -#include -#include - #include "shim.h" #define NO_REBOOT L"FB_NO_REBOOT" diff --git a/httpboot.c b/httpboot.c index bedb99d2..fe08f3f7 100644 --- a/httpboot.c +++ b/httpboot.c @@ -7,10 +7,6 @@ * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel * Corporation. */ - -#include -#include - #include "shim.h" static UINTN diff --git a/include/console.h b/include/console.h index b2ab5fe4..d8af3cd3 100644 --- a/include/console.h +++ b/include/console.h @@ -17,9 +17,9 @@ EFI_STATUS console_get_keystroke(EFI_INPUT_KEY *key); -UINTN +UINTN EFIAPI console_print(const CHAR16 *fmt, ...); -UINTN +UINTN EFIAPI console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...); void console_print_box_at(CHAR16 *str_arr[], int highlight, @@ -101,8 +101,8 @@ extern UINT32 verbose; #define dprint(fmt, ...) \ dprint_(L"%a:%d:%a() " fmt, __FILE__, __LINE__ - 1, __func__, \ ##__VA_ARGS__) -extern EFI_STATUS -vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, va_list args); +extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, + const char *func, elf_va_list args); #define vdprint(fmt, ...) \ vdprint_(fmt, __FILE__, __LINE__ - 1, __func__, ##__VA_ARGS__) diff --git a/include/hexdump.h b/include/hexdump.h index 8b8b4557..36d77ec4 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -3,7 +3,8 @@ #ifndef STATIC_HEXDUMP_H #define STATIC_HEXDUMP_H -#include +#include "shim.h" +#include "include/console.h" static inline unsigned long UNUSED prepare_hex(const void *data, size_t size, char *buf, unsigned int position) @@ -80,8 +81,9 @@ prepare_text(const void *data, size_t size, char *buf, unsigned int position) * variadic hexdump formatted * think of it as: printf("%s%s\n", vformat(fmt, ap), hexdump(data,size)); */ -static inline void UNUSED -vhexdumpf(const char *file, int line, const char *func, const CHAR16 * const fmt, const void *data, unsigned long size, size_t at, va_list ap) +static inline void UNUSED EFIAPI +vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, + const void *data, unsigned long size, size_t at, elf_va_list ap) { unsigned long display_offset = at; unsigned long offset = 0; @@ -115,13 +117,14 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 * const fmt * think of it as: printf("%s%s", format(fmt, ...), hexdump(data,size)[lineN]); */ static inline void UNUSED -hexdumpf(const char *file, int line, const char *func, const CHAR16 * const fmt, const void *data, unsigned long size, size_t at, ...) +hexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, + const void *data, unsigned long size, size_t at, ...) { - va_list ap; + elf_va_list ap; - va_start(ap, at); + elf_va_start(ap, at); vhexdumpf(file, line, func, fmt, data, size, at, ap); - va_end(ap); + elf_va_end(ap); } static inline void UNUSED diff --git a/include/system/alloca.h b/include/system/alloca.h new file mode 100644 index 00000000..dc11b60d --- /dev/null +++ b/include/system/alloca.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#endif /* !_ALLOCA_H */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/ctype.h b/include/system/ctype.h new file mode 100644 index 00000000..c771bb69 --- /dev/null +++ b/include/system/ctype.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * ctype.h - standard ctype functions + */ +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _CTYPE_H +#define _CTYPE_H + + +#endif /* !_CTYPE_H */ +#endif /* !SHIM_UNIT_TEST */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/inttypes.h b/include/system/inttypes.h new file mode 100644 index 00000000..a35b0090 --- /dev/null +++ b/include/system/inttypes.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _INTTYPES_H +#define _INTTYPES_H + +#include +#include + +#endif /* !INTTYPES_H_ */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/stdarg.h b/include/system/stdarg.h new file mode 100644 index 00000000..346b760d --- /dev/null +++ b/include/system/stdarg.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * stdarg.h - try to make consistent va_* handling for EFI + */ +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _STDARG_H +#define _STDARG_H + +#include + +#endif /* !_STDARG_H */ +#endif +#ifndef SHIM_STDARG_H_ +#define SHIM_STDARG_H_ + +typedef __builtin_ms_va_list ms_va_list; +#define ms_va_copy(dest, start) __builtin_ms_va_copy(dest, start) +#define ms_va_start(marker, arg) __builtin_ms_va_start(marker, arg) +#define ms_va_arg(marker, type) __builtin_va_arg(marker, type) +#define ms_va_end(marker) __builtin_ms_va_end(marker) + +typedef __builtin_va_list elf_va_list; +#define elf_va_copy(dest, start) __builtin_va_copy(dest, start) +#define elf_va_start(marker, arg) __builtin_va_start(marker, arg) +#define elf_va_arg(marker, type) __builtin_va_arg(marker, type) +#define elf_va_end(marker) __builtin_va_end(marker) + +#endif /* !SHIM_STDARG_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/stdio.h b/include/system/stdio.h new file mode 100644 index 00000000..6ea60d71 --- /dev/null +++ b/include/system/stdio.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * stdio.h - sigh + */ +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _STDIO_H +#define _STDIO_H + +#endif /* !_STDIO_H */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/stdlib.h b/include/system/stdlib.h new file mode 100644 index 00000000..f2660f63 --- /dev/null +++ b/include/system/stdlib.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _STDLIB_H +#define _STDLIB_H + +/* + * I don't know why, but openssl expects to get size_t from stdlib.h + * instead of stddef.h, so... whatever. + */ +#include + +#endif /* !_STDLIB_H */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/string.h b/include/system/string.h new file mode 100644 index 00000000..21e46c1d --- /dev/null +++ b/include/system/string.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _STRING_H +#define _STRING_H + +#include + +__typeof__(__builtin_memset) memset; +__typeof__(__builtin_memcpy) memcpy; + +#endif /* _STRING_H */ +#endif diff --git a/include/system/strings.h b/include/system/strings.h new file mode 100644 index 00000000..c82bd917 --- /dev/null +++ b/include/system/strings.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifdef SHIM_UNIT_TEST +#include_next +#else +#ifndef _STRINGS_H +#define _STRINGS_H + +#endif /* !_STRINGS_H */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/test.h b/include/test.h index 6fc178ba..8a970fd2 100644 --- a/include/test.h +++ b/include/test.h @@ -11,13 +11,13 @@ #include #if defined(__aarch64__) -#include +#include #elif defined(__arm__) #include #elif defined(__i386__) || defined(__i486__) || defined(__i686__) #include #elif defined(__x86_64__) -#include +#include #else #error what arch is this #endif diff --git a/lib/Makefile b/lib/Makefile index d9188c74..63893c3e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -2,9 +2,44 @@ TARGET = lib.a LIBFILES = $(foreach x,$(wildcard *.c),$(patsubst %.c,%.o,$(x))) +CRYPTDIR = $(TOPDIR)/Cryptlib + INCLUDES = $(EFI_INCLUDES) \ - -I$(TOPDIR)/../include \ - -I$(TOPDIR)/CryptLib/Include/openssl/ + -I$(TOPDIR)/include \ + -I$(CRYPTDIR)/Include/openssl/ \ + -I$(CRYPTDIR)/Include/ \ + -I$(CRYPTDIR) \ + -I$(TOPDIR) \ + -isystem $(TOPDIR)/include/system \ + -isystem $(shell $(CC) -print-file-name=include) + +CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) + +ifeq ($(ARCH),x86_64) +FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) +DEFINES += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ + -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 +endif +ifeq ($(ARCH),ia32) +FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) +DEFINES += -DMDE_CPU_IA32 +endif +ifeq ($(ARCH),aarch64) +DEFINES += -DMDE_CPU_AARCH64 +endif +ifeq ($(ARCH),arm) +DEFINES += -DMDE_CPU_ARM +endif + +LDFLAGS = -nostdlib -znocombreloc + + +CFLAGS = $(FEATUREFLAGS) \ + $(OPTIMIZATIONS) \ + $(WARNFLAGS) \ + $(WERRFLAGS) \ + $(INCLUDES) \ + $(DEFINES) lib.a: $(LIBFILES) $(AR) rcs lib.a $(LIBFILES) diff --git a/lib/configtable.c b/lib/configtable.c index 8675fad1..66e97f63 100644 --- a/lib/configtable.c +++ b/lib/configtable.c @@ -4,9 +4,6 @@ * * read some platform configuration tables */ -#include -#include - #include "shim.h" void * diff --git a/lib/console.c b/lib/console.c index ffa8ea5c..32c6d55d 100644 --- a/lib/console.c +++ b/lib/console.c @@ -3,11 +3,6 @@ * Copyright 2012 * Copyright 2013 Red Hat Inc. */ -#include -#include -#include -#include - #include "shim.h" static UINT8 console_text_mode = 0; @@ -88,27 +83,27 @@ VOID console_fini(VOID) setup_console(0); } -UINTN +UINTN EFIAPI console_print(const CHAR16 *fmt, ...) { - va_list args; + elf_va_list args; UINTN ret; if (!console_text_mode) setup_console(1); - va_start(args, fmt); + elf_va_start(args, fmt); ret = VPrint(fmt, args); - va_end(args); + elf_va_end(args); return ret; } -UINTN +UINTN EFIAPI console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) { SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; - va_list args; + elf_va_list args; UINTN ret; if (!console_text_mode) @@ -116,9 +111,9 @@ console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) co->SetCursorPosition(co, col, row); - va_start(args, fmt); + elf_va_start(args, fmt); ret = VPrint(fmt, args); - va_end(args); + elf_va_end(args); return ret; } diff --git a/lib/execute.c b/lib/execute.c index f57a6321..642f94a3 100644 --- a/lib/execute.c +++ b/lib/execute.c @@ -3,10 +3,6 @@ * Copyright 2012 * Code Copyright 2012 Red Hat, Inc */ - -#include -#include - #include "shim.h" EFI_STATUS diff --git a/lib/print_crypto.c b/lib/print_crypto.c index 39dfd2c0..ccdb65b1 100644 --- a/lib/print_crypto.c +++ b/lib/print_crypto.c @@ -2,11 +2,6 @@ /* * Copyright 2019 SUSE LLC */ - -#include -#include -#include - #include "shim.h" #include diff --git a/lib/security_policy.c b/lib/security_policy.c index 6a9b13ed..6c42cc14 100644 --- a/lib/security_policy.c +++ b/lib/security_policy.c @@ -4,10 +4,6 @@ * * Install and remove a platform security2 override policy */ - -#include -#include - #include "shim.h" #if defined(OVERRIDE_SECURITY_POLICY) diff --git a/lib/shell.c b/lib/shell.c index 87f279d6..146d9a21 100644 --- a/lib/shell.c +++ b/lib/shell.c @@ -4,9 +4,6 @@ * * misc shell helper functions */ -#include -#include - #include "shim.h" EFI_STATUS diff --git a/lib/simple_file.c b/lib/simple_file.c index e6544709..5fd3e1a6 100644 --- a/lib/simple_file.c +++ b/lib/simple_file.c @@ -2,10 +2,6 @@ /* * Copyright 2012 */ - -#include -#include - #include "shim.h" EFI_STATUS diff --git a/lib/variables.c b/lib/variables.c index 6db069ef..57875e26 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -10,9 +10,6 @@ * Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.
* */ -#include -#include - #include "shim.h" EFI_STATUS diff --git a/mok.c b/mok.c index ac0276ec..6bd506be 100644 --- a/mok.c +++ b/mok.c @@ -6,10 +6,6 @@ #include "shim.h" -#include - -#include "hexdump.h" - /* * Check if a variable exists */ diff --git a/netboot.c b/netboot.c index 25a6df7f..450e9def 100644 --- a/netboot.c +++ b/netboot.c @@ -13,8 +13,6 @@ #include "shim.h" -#include - #define ntohs(x) __builtin_bswap16(x) /* supported both by GCC and clang */ #define htons(x) ntohs(x) diff --git a/pe.c b/pe.c index 45dd4714..73b05a51 100644 --- a/pe.c +++ b/pe.c @@ -5,7 +5,6 @@ */ #include "shim.h" -#include "hexdump.h" #include #include diff --git a/replacements.c b/replacements.c index 69dbd5a2..278a8e78 100644 --- a/replacements.c +++ b/replacements.c @@ -18,11 +18,6 @@ * National Security Policy and Scientific Developments, November 20, * 1969. */ - -#include -#include -#include - #include "shim.h" static EFI_SYSTEM_TABLE *systab; diff --git a/sbat.c b/sbat.c index f46bb8ab..d8750962 100644 --- a/sbat.c +++ b/sbat.c @@ -4,7 +4,6 @@ */ #include "shim.h" -#include "string.h" EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, diff --git a/shim.c b/shim.c index 32bc3e81..6f627b1f 100644 --- a/shim.c +++ b/shim.c @@ -12,7 +12,6 @@ */ #include "shim.h" -#include "hexdump.h" #if defined(ENABLE_SHIM_CERT) #include "shim_cert.h" #endif /* defined(ENABLE_SHIM_CERT) */ diff --git a/shim.h b/shim.h index d28e16b7..61dafa82 100644 --- a/shim.h +++ b/shim.h @@ -26,6 +26,14 @@ #endif #endif +#include +#include +#include +#include +#include +#include +#include + #ifndef SHIM_UNIT_TEST #include #include @@ -34,9 +42,6 @@ #include #endif -#include -#include - #ifdef SHIM_UNIT_TEST #include "include/test.h" #endif @@ -158,9 +163,14 @@ #include "include/tpm.h" #include "include/ucs2.h" #include "include/variables.h" +#include "include/hexdump.h" #include "version.h" +#ifndef SHIM_UNIT_TEST +#include "Cryptlib/Include/OpenSslSupport.h" +#endif + INTERFACE_DECL(_SHIM_LOCK); typedef @@ -196,9 +206,12 @@ typedef struct _SHIM_LOCK { extern EFI_STATUS shim_init(void); extern void shim_fini(void); -extern EFI_STATUS LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...); -extern EFI_STATUS VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, va_list args); -extern VOID LogHexdump_(const char *file, int line, const char *func, const void *data, size_t sz); +extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, + const CHAR16 *fmt, ...); +extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, + const CHAR16 *fmt, elf_va_list args); +extern VOID LogHexdump_(const char *file, int line, const char *func, + const void *data, size_t sz); extern VOID PrintErrors(VOID); extern VOID ClearErrors(VOID); extern VOID restore_loaded_image(VOID); diff --git a/test.c b/test.c index b21e2191..aa0da1fd 100644 --- a/test.c +++ b/test.c @@ -12,7 +12,8 @@ UINT8 in_protocol = 0; int debug = DEFAULT_DEBUG_PRINT_STATE; -EFI_STATUS LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) +EFI_STATUS EFIAPI +LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { assert(0); return EFI_SUCCESS; diff --git a/tpm.c b/tpm.c index e1fcb8be..808e0444 100644 --- a/tpm.c +++ b/tpm.c @@ -1,10 +1,4 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent - -#include -#include -#include -#include - #include "shim.h" typedef struct { -- cgit v1.2.3 From 766aac4d5cfbe76026be5ce718b0883ee211f323 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Mar 2021 11:54:58 -0500 Subject: Consolidate most of our standard lib functions to lib Signed-off-by: Peter Jones --- Cryptlib/Include/OpenSslSupport.h | 23 +--- Cryptlib/Makefile | 3 +- Cryptlib/SysCall/BaseStrings.c | 34 +----- Cryptlib/SysCall/CrtWrapper.c | 82 ------------- Cryptlib/SysCall/memset.c | 40 ------- Makefile | 4 +- csv.c | 4 +- gnu-efi | 2 +- httpboot.c | 14 +-- include/compiler.h | 5 + include/endian.h | 19 +++ include/hexdump.h | 2 - include/str.h | 118 +----------------- include/system/alloca.h | 6 + include/system/builtins_begin_.h | 94 +++++++++++++++ include/system/builtins_end_.h | 28 +++++ include/system/ctype.h | 73 +++++++++++- include/system/stdlib.h | 12 ++ include/system/string.h | 51 +++++++- include/system/strings.h | 9 ++ lib/string.c | 245 ++++++++++++++++++++++++++++++++++++++ mok.c | 2 +- netboot.c | 8 +- sbat.c | 4 +- shim.h | 2 + 25 files changed, 566 insertions(+), 318 deletions(-) delete mode 100644 Cryptlib/SysCall/memset.c create mode 100644 include/endian.h create mode 100644 include/system/builtins_begin_.h create mode 100644 include/system/builtins_end_.h create mode 100644 lib/string.c (limited to 'include') diff --git a/Cryptlib/Include/OpenSslSupport.h b/Cryptlib/Include/OpenSslSupport.h index 7af9650f..6bb7ba64 100644 --- a/Cryptlib/Include/OpenSslSupport.h +++ b/Cryptlib/Include/OpenSslSupport.h @@ -288,34 +288,21 @@ extern int errno; void *malloc (size_t); void *realloc (void *, size_t); void free (void *); -int isdigit (int); -int isspace (int); -int tolower (int); -int isupper (int); -int isxdigit (int); -int isalnum (int); void *memcpy (void *, const void *, size_t); void *memset (void *, int, size_t); void *memchr (const void *, int, size_t); int memcmp (const void *, const void *, size_t); void *memmove (void *, const void *, size_t); -int strcmp (const char *, const char *); -int strncmp (const char *, const char *, size_t); char *strcpy (char *, const char *); char *strncpy (char *, const char *, size_t); -size_t strlen (const char *); char *strcat (char *, const char *); char *strchr (const char *, int); int strcasecmp (const char *, const char *); int strncasecmp (const char *, const char *, size_t); char *strncpy (char *, const char *, size_t); -int strncmp (const char *, const char *, size_t); -char *strrchr (const char *, int); unsigned long strtoul (const char *, char **, int); long strtol (const char *, char **, int); char *strerror (int); -size_t strspn (const char *, const char *); -size_t strcspn (const char *, const char *); int printf (const char *, ...); int sscanf (const char *, const char *, ...); int open (const char *, int, ...); @@ -351,7 +338,6 @@ gid_t getegid (void); void qsort (void *, size_t, size_t, int (*)(const void *, const void *)); char *getenv (const char *); void exit (int); -void abort (void); __sighandler_t *signal (int, __sighandler_t *); // @@ -361,7 +347,7 @@ extern FILE *stderr; extern FILE *stdin; extern FILE *stdout; -#define AsciiStrLen(x) strlena(x) +#define AsciiStrLen(x) strlen(x) #define AsciiStrnCmp(s1, s2, len) strncmpa((CHAR8 *)s1, (CHAR8 *)s2, len) // @@ -372,17 +358,10 @@ extern FILE *stdout; #define memchr(buf,ch,count) ScanMem8((CHAR8 *)buf,(UINTN)(count),ch) #define memcmp(buf1,buf2,count) (int)(CompareMem(buf1,buf2,(UINTN)(count))) #define memmove(dest,source,count) CopyMem(dest,source,(UINTN)(count)) -#define strlen(str) (size_t)(AsciiStrLen((CHAR8 *)str)) -#define strcpy(strDest,strSource) AsciiStrCpy((CHAR8 *)strDest,(const CHAR8 *)strSource) -#define strncpy(strDest,strSource,count) AsciiStrnCpy((CHAR8 *)strDest,(const CHAR8 *)strSource,(UINTN)count) -#define strcat(strDest,strSource) AsciiStrCat((CHAR8 *)strDest,(const CHAR8 *)strSource) -#define strchr(str,ch) (char *)(ScanMem8((CHAR8 *)str,AsciiStrSize((CHAR8 *)str),ch)) -#define strncmp(string1,string2,count) (int)(AsciiStrnCmp((const CHAR8 *)string1, (const CHAR8 *)string2,(UINTN)(count))) #define localtime(timer) NULL #define assert(expression) #define atoi(nptr) AsciiStrDecimalToUintn((const CHAR8 *)nptr) #define gettimeofday(tvp,tz) do { (tvp)->tv_sec = time(NULL); (tvp)->tv_usec = 0; } while (0) #define gmtime_r(timer,result) (result = NULL) -#define abort() #endif diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 65a3918c..27614618 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -63,8 +63,7 @@ OBJS = Hash/CryptMd4Null.o \ SysCall/CrtWrapper.o \ SysCall/TimerWrapper.o \ SysCall/BaseMemAllocation.o \ - SysCall/BaseStrings.o \ - SysCall/memset.o + SysCall/BaseStrings.o all: $(TARGET) diff --git a/Cryptlib/SysCall/BaseStrings.c b/Cryptlib/SysCall/BaseStrings.c index c4b3e18e..29a16100 100644 --- a/Cryptlib/SysCall/BaseStrings.c +++ b/Cryptlib/SysCall/BaseStrings.c @@ -3,7 +3,7 @@ CHAR8 * AsciiStrCat(CHAR8 *Destination, const CHAR8 *Source) { - UINTN dest_len = strlena((CHAR8 *)Destination); + UINTN dest_len = strlen((CHAR8 *)Destination); UINTN i; for (i = 0; Source[i] != '\0'; i++) @@ -61,37 +61,7 @@ WriteUnaligned32(UINT32 *Buffer, UINT32 Value) UINTN AsciiStrSize(const CHAR8 *string) { - return strlena(string) + 1; -} - -int -strcmp (const char *str1, const char *str2) -{ - return strcmpa((CHAR8 *)str1,(CHAR8 *)str2); -} - -inline static char -toupper (char c) -{ - return ((c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c); -} - -/* Based on AsciiStriCmp() in edk2 MdePkg/Library/BaseLib/String.c */ -int -strcasecmp (const char *str1, const char *str2) -{ - char c1, c2; - - c1 = toupper (*str1); - c2 = toupper (*str2); - while ((*str1 != '\0') && (c1 == c2)) { - str1++; - str2++; - c1 = toupper (*str1); - c2 = toupper (*str2); - } - - return c1 - c2; + return strlen(string) + 1; } /* Based on AsciiStrDecimalToUintnS() in edk2 diff --git a/Cryptlib/SysCall/CrtWrapper.c b/Cryptlib/SysCall/CrtWrapper.c index 698e1eef..4bdaede9 100644 --- a/Cryptlib/SysCall/CrtWrapper.c +++ b/Cryptlib/SysCall/CrtWrapper.c @@ -121,21 +121,6 @@ QuickSortWorker ( // -- String Manipulation Routines -- // -/* Scan a string for the last occurrence of a character */ -char *strrchr (const char *str, int c) -{ - char * save; - - for (save = NULL; ; ++str) { - if (*str == c) { - save = (char *)str; - } - if (*str == 0) { - return (save); - } - } -} - /* Read formatted data from a string */ int sscanf (const char *buffer, const char *format, ...) { @@ -146,59 +131,6 @@ int sscanf (const char *buffer, const char *format, ...) return 0; } -// -// -- Character Classification Routines -- -// - -/* Determines if a particular character is a decimal-digit character */ -int isdigit (int c) -{ - // - // ::= [0-9] - // - return (('0' <= (c)) && ((c) <= '9')); -} - -/* Determine if an integer represents character that is a hex digit */ -int isxdigit (int c) -{ - // - // ::= [0-9] | [a-f] | [A-F] - // - return ((('0' <= (c)) && ((c) <= '9')) || - (('a' <= (c)) && ((c) <= 'f')) || - (('A' <= (c)) && ((c) <= 'F'))); -} - -/* Determines if a particular character represents a space character */ -int isspace (int c) -{ - // - // ::= [ ] - // - return ((c) == ' '); -} - -/* Determine if a particular character is an alphanumeric character */ -int isalnum (int c) -{ - // - // ::= [0-9] | [a-z] | [A-Z] - // - return ((('0' <= (c)) && ((c) <= '9')) || - (('a' <= (c)) && ((c) <= 'z')) || - (('A' <= (c)) && ((c) <= 'Z'))); -} - -/* Determines if a particular character is in upper case */ -int isupper (int c) -{ - // - // := [A-Z] - // - return (('A' <= (c)) && ((c) <= 'Z')); -} - // // -- Data Conversion Routines -- // @@ -223,15 +155,6 @@ unsigned long strtoul (const char *nptr, char **endptr, int base) return 0; } -/* Convert character to lowercase */ -int tolower (int c) -{ - if (('A' <= (c)) && ((c) <= 'Z')) { - return (c - ('A' - 'a')); - } - return (c); -} - // // -- Searching and Sorting Routines -- // @@ -424,11 +347,6 @@ int stat (const char *c, struct stat *s) return -1; } -int strncasecmp (const char *c, const char *s, size_t l) -{ - return 0; -} - void syslog (int a, const char *c, ...) { diff --git a/Cryptlib/SysCall/memset.c b/Cryptlib/SysCall/memset.c deleted file mode 100644 index 76deed68..00000000 --- a/Cryptlib/SysCall/memset.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 SUSE LINUX GmbH - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -typedef UINTN size_t; - -void * -memset (void *dest, int ch, size_t count) -{ - SetMem(dest, count, (UINT8)(ch)); - return dest; -} diff --git a/Makefile b/Makefile index 6a62e00a..7c0fbb11 100644 --- a/Makefile +++ b/Makefile @@ -116,12 +116,12 @@ LIBS = Cryptlib/libcryptlib.a \ gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a $(SHIMSONAME): $(OBJS) $(LIBS) - $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) + $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a fallback.o: $(FALLBACK_SRCS) $(FBSONAME): $(FALLBACK_OBJS) $(LIBS) - $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) + $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a MokManager.o: $(MOK_SOURCES) diff --git a/csv.c b/csv.c index 3c821414..d141f035 100644 --- a/csv.c +++ b/csv.c @@ -21,8 +21,8 @@ parse_csv_line(char * line, size_t max, size_t *n_columns, const char *columns[] valid = strntoken(next, max, delims, &token, &state); } if (valid) { - next += strlena(token) + 1; - max -= strlena(token) + 1; + next += strlen(token) + 1; + max -= strlen(token) + 1; columns[n] = token; new_n = n + 1; } else { diff --git a/gnu-efi b/gnu-efi index 9aa86c75..d7e183a3 160000 --- a/gnu-efi +++ b/gnu-efi @@ -1 +1 @@ -Subproject commit 9aa86c7526a4adc363afe6847bc3a4c8efe6df84 +Subproject commit d7e183a351d1a81efb2402e5148a4a69fc170365 diff --git a/httpboot.c b/httpboot.c index fe08f3f7..93d88931 100644 --- a/httpboot.c +++ b/httpboot.c @@ -130,7 +130,7 @@ find_httpboot (EFI_HANDLE device) /* Save the current URI */ UriNode = (URI_DEVICE_PATH *)Node; - uri_size = strlena(UriNode->Uri); + uri_size = strlen(UriNode->Uri); uri = AllocatePool(uri_size + 1); if (!uri) { perror(L"Failed to allocate uri\n"); @@ -156,10 +156,10 @@ generate_next_uri (CONST CHAR8 *current_uri, CONST CHAR8 *next_loader, UINTN path_len = 0; UINTN count = 0; - if (strncmpa(current_uri, (CHAR8 *)"http://", 7) == 0) { + if (strncmp(current_uri, (CHAR8 *)"http://", 7) == 0) { ptr = current_uri + 7; count += 7; - } else if (strncmpa(current_uri, (CHAR8 *)"https://", 8) == 0) { + } else if (strncmp(current_uri, (CHAR8 *)"https://", 8) == 0) { ptr = current_uri + 8; count += 8; } else { @@ -167,7 +167,7 @@ generate_next_uri (CONST CHAR8 *current_uri, CONST CHAR8 *next_loader, } /* Extract the path */ - next_len = strlena(next_loader); + next_len = strlen(next_loader); while (*ptr != '\0') { count++; if (*ptr == '/') @@ -192,9 +192,9 @@ extract_hostname (CONST CHAR8 *url, CHAR8 **hostname) CONST CHAR8 *ptr, *start; UINTN host_len = 0; - if (strncmpa(url, (CHAR8 *)"http://", 7) == 0) + if (strncmp(url, (CHAR8 *)"http://", 7) == 0) start = url + 7; - else if (strncmpa(url, (CHAR8 *)"https://", 8) == 0) + else if (strncmp(url, (CHAR8 *)"https://", 8) == 0) start = url + 8; else return EFI_INVALID_PARAMETER; @@ -571,7 +571,7 @@ receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) /* Check the length of the file */ for (i = 0; i < rx_message.HeaderCount; i++) { - if (!strcmpa(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) { + if (!strcmp(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) { *buf_size = ascii_to_int(rx_message.Headers[i].FieldValue); } } diff --git a/include/compiler.h b/include/compiler.h index 3cabd09c..40358a11 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -179,5 +179,10 @@ #define MIN(a, b) ({(a) < (b) ? (a) : (b);}) #define MAX(a, b) ({(a) <= (b) ? (b) : (a);}) +/** + * Builtins that don't go in string.h + */ +#define unreachable() __builtin_unreachable() + #endif /* !COMPILER_H_ */ // vim:fenc=utf-8:tw=75:et diff --git a/include/endian.h b/include/endian.h new file mode 100644 index 00000000..267fbf00 --- /dev/null +++ b/include/endian.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * endian.h - bswap decls that can't go in compiler.h + * Copyright Peter Jones + */ + +#ifndef ENDIAN_H_ +#define ENDIAN_H_ + +#include + +#include "system/builtins_begin_.h" +mkbi1_(uint16_t, bswap16, uint16_t, x) +mkbi1_(uint32_t, bswap32, uint32_t, x) +mkbi1_(uint64_t, bswap64, uint64_t, x) +#include "system/builtins_end_.h" + +#endif /* !ENDIAN_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/hexdump.h b/include/hexdump.h index 36d77ec4..a6aa2bfa 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -48,8 +48,6 @@ prepare_hex(const void *data, size_t size, char *buf, unsigned int position) return ret; } -#define isprint(c) ((c) >= 0x20 && (c) <= 0x7e) - static inline void UNUSED prepare_text(const void *data, size_t size, char *buf, unsigned int position) { diff --git a/include/str.h b/include/str.h index c4d12113..189aceff 100644 --- a/include/str.h +++ b/include/str.h @@ -9,122 +9,8 @@ #pragma GCC diagnostic ignored "-Wnonnull-compare" #endif -static inline UNUSED NONNULL(1) unsigned long -strnlena(const CHAR8 *s, unsigned long n) -{ - unsigned long i; - for (i = 0; i < n; i++) - if (s[i] == '\0') - break; - return i; -} - -static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) CHAR8 * -strncpya(CHAR8 *dest, const CHAR8 *src, unsigned long n) -{ - unsigned long i; - - for (i = 0; i < n && src[i] != '\0'; i++) - dest[i] = src[i]; - for (; i < n; i++) - dest[i] = '\0'; - - return dest; -} - -static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) CHAR8 * -strcata(CHAR8 *dest, const CHAR8 *src) -{ - unsigned long dest_len = strlena(dest); - unsigned long i; - - for (i = 0; src[i] != '\0'; i++) - dest[dest_len + i] = src[i]; - dest[dest_len + i] = '\0'; - - return dest; -} - -static inline UNUSED NONNULL(1) CHAR8 * -strdup(const CHAR8 * const src) -{ - UINTN len; - CHAR8 *news = NULL; - - len = strlena(src); - news = AllocateZeroPool(len + 1); - if (news) - strncpya(news, src, len); - return news; -} - -static inline UNUSED NONNULL(1) CHAR8 * -strndupa(const CHAR8 * const src, const UINTN srcmax) -{ - UINTN len; - CHAR8 *news = NULL; - - len = strnlena(src, srcmax); - news = AllocateZeroPool(len + 1); - if (news) - strncpya(news, src, len); - return news; -} - -static inline UNUSED RETURNS_NONNULL NONNULL(1, 2) char * -stpcpy(char *dest, const char * const src) -{ - size_t i = 0; - for (i = 0; src[i]; i++) - dest[i] = src[i]; - dest[i] = '\000'; - return &dest[i]; -} - -static inline UNUSED CHAR8 * -translate_slashes(CHAR8 *out, const char *str) -{ - int i; - int j; - if (str == NULL || out == NULL) - return NULL; - - for (i = 0, j = 0; str[i] != '\0'; i++, j++) { - if (str[i] == '\\') { - out[j] = '/'; - if (str[i+1] == '\\') - i++; - } else - out[j] = str[i]; - } - out[j] = '\0'; - return out; -} - -static inline UNUSED RETURNS_NONNULL NONNULL(1) CHAR8 * -strchrnula(const CHAR8 *s, int c) -{ - unsigned int i; - - for (i = 0; s[i] != '\000' && s[i] != c; i++) - ; - - return (CHAR8 *)&s[i]; -} - -static inline UNUSED NONNULL(1) CHAR8 * -strchra(const CHAR8 *s, int c) -{ - const CHAR8 *s1; - - s1 = strchrnula(s, c); - if (!s1 || s1[0] == '\000') - return NULL; - - return (CHAR8 *)s1; -} - -static inline UNUSED RETURNS_NONNULL NONNULL(1) char * +static inline UNUSED RETURNS_NONNULL NONNULL(1) +char * strnchrnul(const char *s, size_t max, int c) { unsigned int i; diff --git a/include/system/alloca.h b/include/system/alloca.h index dc11b60d..a9d1aab1 100644 --- a/include/system/alloca.h +++ b/include/system/alloca.h @@ -5,6 +5,12 @@ #ifndef _ALLOCA_H #define _ALLOCA_H +#include +mkbi1_(void *, alloca, size_t, size) +#define alloca_with_align(size, alignment) __builtin_alloca_with_align(size, alignment) +#define alloca_with_align_and_max(size, alignment, max) __builtin_alloca_with_align_and_max(size, alignment, max) +#include + #endif /* !_ALLOCA_H */ #endif // vim:fenc=utf-8:tw=75:noet diff --git a/include/system/builtins_begin_.h b/include/system/builtins_begin_.h new file mode 100644 index 00000000..92ea5e3a --- /dev/null +++ b/include/system/builtins_begin_.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/** + * macros to build builtin wrappers + */ +#ifndef mkbi_cat_ +#define mkbi_cat_(a, b) a##b +#endif +#ifdef SHIM_STRING_C_ + +#ifndef mkbi1_ +#define mkbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; +#endif +#ifndef mkbi2_ +#define mkbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; +#endif + +#ifndef mkbi3_ +#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) __typeof__(mkbi_cat_(__builtin_, x)) x; +#endif + +#ifndef mkdepbi1_ +#define mkdepbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; +#endif + +#ifndef mkdepbi2_ +#define mkdepbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; +#endif + +#else /* ! SHIM_STRING_C_ */ + +#ifndef mkbi1_ +#define mkbi1_(rtype, x, typea, a) \ + static inline __attribute__((__unused__)) \ + rtype \ + x(typea a) \ + { \ + return mkbi_cat_(__builtin_, x)(a); \ + } +#endif + +#ifndef mkbi2_ +#define mkbi2_(rtype, x, typea, a, typeb, b) \ + static inline __attribute__((__unused__)) \ + rtype \ + x(typea a, typeb b) \ + { \ + return mkbi_cat_(__builtin_, x)(a, b); \ + } +#endif + +#ifndef mkbi3_ +#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) \ + static inline __attribute__((__unused__)) \ + rtype \ + x(typea a, typeb b, typec c) \ + { \ + return mkbi_cat_(__builtin_, x)(a, b,c); \ + } +#endif + +#ifdef SHIM_DEPRECATE_STRLEN +#ifndef mkdepbi_dep_ +#define mkdepbi_dep_ __attribute__((__deprecated__)) +#endif +#else /* !SHIM_DEPRECATE_STRLEN */ +#ifndef mkdepbi_dep_ +#define mkdepbi_dep_ +#endif +#endif /* SHIM_DEPRECATE_STRLEN */ + +#ifndef mkdepbi1_ +#define mkdepbi1_(rtype, x, typea, a) \ + static inline __attribute__((__unused__)) \ + mkdepbi_dep_ \ + rtype \ + x(typea a) \ + { \ + return mkbi_cat_(__builtin_, x)(a); \ + } +#endif + +#ifndef mkdepbi2_ +#define mkdepbi2_(rtype, x, typea, a, typeb, b) \ + static inline __attribute__((__unused__)) \ + mkdepbi_dep_ \ + rtype \ + x(typea a, typeb b) \ + { \ + return mkbi_cat_(__builtin_, x)(a, b); \ + } +#endif +#endif /* SHIM_STRING_C_ */ + +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/builtins_end_.h b/include/system/builtins_end_.h new file mode 100644 index 00000000..0a6ad60a --- /dev/null +++ b/include/system/builtins_end_.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#ifdef mkbi1_ +#undef mkbi1_ +#endif +#ifdef mkbi2_ +#undef mkbi2_ +#endif +#ifdef mkbi3_ +#undef mkbi3_ +#endif +#ifdef mkdepbi1_ +#undef mkdepbi1_ +#endif +#ifdef mkdepbi2_ +#undef mkdepbi2_ +#endif +#ifdef mkdepbi_dep_ +#undef mkdepbi_dep_ +#endif +#ifdef mkbi_cat_ +#undef mkbi_cat_ +#endif + +#ifdef _BUILTINS_BEGIN__H +#undef _BUILTINS_BEGIN__H +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/ctype.h b/include/system/ctype.h index c771bb69..65e7348f 100644 --- a/include/system/ctype.h +++ b/include/system/ctype.h @@ -3,11 +3,82 @@ * ctype.h - standard ctype functions */ #ifdef SHIM_UNIT_TEST -#include_next +#include_next #else #ifndef _CTYPE_H #define _CTYPE_H +#define isprint(c) ((c) >= 0x20 && (c) <= 0x7e) + +/* Determines if a particular character is a decimal-digit character */ +static inline __attribute__((__unused__)) int +isdigit(int c) +{ + // + // ::= [0-9] + // + return (('0' <= (c)) && ((c) <= '9')); +} + +/* Determine if an integer represents character that is a hex digit */ +static inline __attribute__((__unused__)) int +isxdigit(int c) +{ + // + // ::= [0-9] | [a-f] | [A-F] + // + return ((('0' <= (c)) && ((c) <= '9')) || + (('a' <= (c)) && ((c) <= 'f')) || + (('A' <= (c)) && ((c) <= 'F'))); +} + +/* Determines if a particular character represents a space character */ +static inline __attribute__((__unused__)) int +isspace(int c) +{ + // + // ::= [ ] + // + return ((c) == ' '); +} + +/* Determine if a particular character is an alphanumeric character */ +static inline __attribute__((__unused__)) int +isalnum(int c) +{ + // + // ::= [0-9] | [a-z] | [A-Z] + // + return ((('0' <= (c)) && ((c) <= '9')) || + (('a' <= (c)) && ((c) <= 'z')) || + (('A' <= (c)) && ((c) <= 'Z'))); +} + +/* Determines if a particular character is in upper case */ +static inline __attribute__((__unused__)) int +isupper(int c) +{ + // + // := [A-Z] + // + return (('A' <= (c)) && ((c) <= 'Z')); +} + +/* Convert character to lowercase */ +static inline __attribute__((__unused__)) int +tolower(int c) +{ + if (('A' <= (c)) && ((c) <= 'Z')) { + return (c - ('A' - 'a')); + } + return (c); +} + +static inline __attribute__((__unused__)) int +toupper(int c) +{ + return ((c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c); +} #endif /* !_CTYPE_H */ #endif /* !SHIM_UNIT_TEST */ diff --git a/include/system/stdlib.h b/include/system/stdlib.h index f2660f63..da7d3af9 100644 --- a/include/system/stdlib.h +++ b/include/system/stdlib.h @@ -11,6 +11,18 @@ */ #include +static inline void abort(void) { } + +#include +mkbi1_(int, abs, int, j) +mkbi1_(long int, labs, long int, j) +mkbi1_(long long int, llabs, long long int, j) + +#ifdef _INTTYPES_H +mkbi1_(intmax_t, imaxabs, intmax_t, j) +#endif /* _INTTYPES_H */ +#include + #endif /* !_STDLIB_H */ #endif // vim:fenc=utf-8:tw=75:noet diff --git a/include/system/string.h b/include/system/string.h index 21e46c1d..822e28fa 100644 --- a/include/system/string.h +++ b/include/system/string.h @@ -7,8 +7,55 @@ #include -__typeof__(__builtin_memset) memset; -__typeof__(__builtin_memcpy) memcpy; +#include + +mkbi1_(long int, ffsl, long int, x) +mkbi1_(long int, clzl, long int, x) +mkbi1_(long int, ctzl, long int, x) +mkbi1_(long int, clrsbl, long int, x) +mkbi1_(long int, popcountl, long int, x) +mkbi1_(long int, parityl, long int, x) +mkbi1_(long long int, ffsll, long long int, x) +mkbi1_(long long int, clzll, long long int, x) +mkbi1_(long long int, ctzll, long long int, x) +mkbi1_(long long int, clrsbll, long long int, x) +mkbi1_(long long int, popcountll, long long int, x) +mkbi1_(long long int, parityll, long long int, x) + +mkbi3_(int, bcmp, const void *, s1, const void *, s2, size_t, n) +mkbi3_(void, bcopy, const void *, src, void *, dest, size_t, n) +mkbi2_(void, bzero, void *, s, size_t, n) +mkdepbi2_(char *, index, const char *, s, int, c) +mkbi3_(void *, memchr, const void *, s, int, c, size_t, n) +mkbi3_(int, memcmp, const void *, s1, const void *, s2, size_t, n) +mkbi3_(void *, memcpy, void *, dest, const void *, src, size_t, n) +mkbi3_(void *, memmove, void *, dest, const void *, src, size_t, n) +mkbi3_(void *, mempcpy, void *, dest, const void *, src, size_t, n) +mkdepbi2_(char *, rindex, const char *, s, int, c) +mkdepbi2_(char *, stpcpy, char *, dest, const char *, src) +mkbi3_(char *, stpncpy, char *, dest, const char *, src, size_t, n) +mkdepbi2_(int, strcasecmp, const char *, s1, const char *, s2) +mkdepbi2_(char *, strcat, char *, dest, const char *, src) +mkdepbi2_(char *, strchr, const char *, s, int, c) +mkdepbi2_(int, strcmp, const char *, s1, const char *, s2) +mkdepbi2_(char *, strcpy, char *, dest, const char *, src) +mkdepbi2_(size_t, strcspn, const char *, s, const char *, reject) +mkdepbi1_(char *, strdup, const char *, s) +mkbi2_(char *, strndup, const char *, s, size_t, n) +mkdepbi1_(size_t, strlen, const char *, s) +mkbi3_(int, strncasecmp, const char *, s1, const char *, s2, size_t, n) +mkbi3_(char *, strncat, char *, dest, const char *, src, size_t, n) +mkbi3_(int, strncmp, const char *, s1, const char *, s2, size_t, n) +mkbi3_(char *, strncpy, char *, dest, const char *, src, size_t, n) +mkbi2_(int, strnlen, const char *, s1, size_t, n) +mkdepbi2_(char *, strpbrk, const char *, s, const char *, accept) +mkdepbi2_(char *, strrchr, const char *, s, int, c) +mkdepbi2_(size_t, strspn, const char *, s, const char *, accept) +mkdepbi2_(char *, strstr, const char *, haystack, const char *, needle) + +mkbi3_(void *, memset, void *, s, int, c, size_t, n); + +#include #endif /* _STRING_H */ #endif diff --git a/include/system/strings.h b/include/system/strings.h index c82bd917..99bc05f2 100644 --- a/include/system/strings.h +++ b/include/system/strings.h @@ -5,6 +5,15 @@ #ifndef _STRINGS_H #define _STRINGS_H +#include +mkbi1_(int, ffs, int, x) +mkbi1_(int, clz, int, x) +mkbi1_(int, ctz, int, x) +mkbi1_(int, clrsb, int, x) +mkbi1_(int, popcount, int, x) +mkbi1_(int, parity, int, x) +#include + #endif /* !_STRINGS_H */ #endif // vim:fenc=utf-8:tw=75:noet diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 00000000..1f58f3ec --- /dev/null +++ b/lib/string.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * string.c - implementations we need for string finctions + */ +#define SHIM_STRING_C_ +#include "shim.h" + +size_t +strlen(const char *s1) +{ + size_t len; + + for (len = 0; *s1; s1 += 1, len += 1) + ; + return len; +} + +int +strcmp(const char *s1p, const char *s2p) +{ + const uint8_t *s1 = (const uint8_t *)s1p; + const uint8_t *s2 = (const uint8_t *)s2p; + + while (*s1) { + if (*s1 != *s2) { + break; + } + + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +int +strncmp(const char *s1p, const char *s2p, size_t len) +{ + const uint8_t *s1 = (const uint8_t *)s1p; + const uint8_t *s2 = (const uint8_t *)s2p; + + while (*s1 && len) { + if (*s1 != *s2) { + break; + } + + s1 += 1; + s2 += 1; + len -= 1; + } + + return len ? *s1 - *s2 : 0; +} + +/* Based on AsciiStriCmp() in edk2 MdePkg/Library/BaseLib/String.c */ +int +strncasecmp(const char *s1p, const char *s2p, size_t n) +{ + const uint8_t *s1 = (const uint8_t *)s1p; + const uint8_t *s2 = (const uint8_t *)s2p; + + while (*s1 && n) { + if (toupper(*s1) != toupper(*s2)) { + break; + } + + s1 += 1; + s2 += 1; + n -= 1; + } + + return n ? *s1 - *s2 : 0; +} + +/* Based on AsciiStriCmp() in edk2 MdePkg/Library/BaseLib/String.c */ +int +strcasecmp(const char *str1, const char *str2) +{ + char c1, c2; + + c1 = toupper(*str1); + c2 = toupper(*str2); + while ((*str1 != '\0') && (c1 == c2)) { + str1++; + str2++; + c1 = toupper(*str1); + c2 = toupper(*str2); + } + + return c1 - c2; +} + +/* Scan a string for the last occurrence of a character */ +char * +strrchr(const char *str, int c) +{ + char *save; + + for (save = NULL;; ++str) { + if (*str == c) { + save = (char *)str; + } + if (*str == 0) { + return (save); + } + } +} + +NONNULL(1) +size_t +strnlen(const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) + if (s[i] == '\0') + break; + return i; +} + +RETURNS_NONNULL NONNULL(1, 2) +char * +strcpy(char *dest, const char *src) +{ + size_t i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + + dest[i] = '\0'; + return dest; +} + +RETURNS_NONNULL NONNULL(1, 2) +char * +strncpy(char *dest, const char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n && src[i] != '\0'; i++) + dest[i] = src[i]; + for (; i < n; i++) + dest[i] = '\0'; + + return dest; +} + +RETURNS_NONNULL NONNULL(1, 2) +char * +strcat(char *dest, const char *src) +{ + size_t dest_len = strlen(dest); + size_t i; + + for (i = 0; src[i] != '\0'; i++) + dest[dest_len + i] = src[i]; + dest[dest_len + i] = '\0'; + + return dest; +} + +NONNULL(1) +char * +strdup(const char *const src) +{ + size_t len; + char *news = NULL; + + len = strlen(src); + news = AllocateZeroPool(len + 1); + if (news) + strncpy(news, src, len); + return news; +} + +NONNULL(1) +char * +strndup(const char *const src, const size_t srcmax) +{ + size_t len; + char *news = NULL; + + len = strnlen(src, srcmax); + news = AllocateZeroPool(len + 1); + if (news) + strncpy(news, src, len); + return news; +} + +RETURNS_NONNULL NONNULL(1, 2) +char * +stpcpy(char *dest, const char *const src) +{ + size_t i = 0; + for (i = 0; src[i]; i++) + dest[i] = src[i]; + dest[i] = '\000'; + return &dest[i]; +} + +RETURNS_NONNULL NONNULL(1) +char * +strchrnul(const char *s, int c) +{ + unsigned int i; + + for (i = 0; s[i] != '\000' && s[i] != c; i++) + ; + + return (char *)&s[i]; +} + +NONNULL(1) +char * +strchr(const char *s, int c) +{ + const char *s1; + + s1 = strchrnul(s, c); + if (!s1 || s1[0] == '\000') + return NULL; + + return (char *)s1; +} + +char * +translate_slashes(char *out, const char *str) +{ + int i; + int j; + if (str == NULL || out == NULL) + return NULL; + + for (i = 0, j = 0; str[i] != '\0'; i++, j++) { + if (str[i] == '\\') { + out[j] = '/'; + if (str[i + 1] == '\\') + i++; + } else + out[j] = str[i]; + } + out[j] = '\0'; + return out; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/mok.c b/mok.c index 6bd506be..be477c48 100644 --- a/mok.c +++ b/mok.c @@ -1013,7 +1013,7 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle) struct mok_state_variable *v = &mok_state_variables[i]; ZeroMem(&config_template, sizeof(config_template)); - strncpya(config_template.name, (CHAR8 *)v->rtname8, 255); + strncpy(config_template.name, (CHAR8 *)v->rtname8, 255); config_template.name[255] = '\0'; config_template.data_size = v->data_size; diff --git a/netboot.c b/netboot.c index 450e9def..3f5c5198 100644 --- a/netboot.c +++ b/netboot.c @@ -172,7 +172,7 @@ static BOOLEAN extract_tftp_info(CHAR8 *url) // to check against str2ip6() errors memset(ip6inv, 0, sizeof(ip6inv)); - if (strncmp((UINT8 *)url, (UINT8 *)"tftp://", 7)) { + if (strncmp((const char *)url, (const char *)"tftp://", 7)) { console_print(L"URLS MUST START WITH tftp://\n"); return FALSE; } @@ -258,7 +258,7 @@ static EFI_STATUS parseDhcp4() pkt_v4 = &pxe->Mode->PxeReply.Dhcpv4; } - INTN dir_len = strnlena((CHAR8 *)pkt_v4->BootpBootFile, 127); + INTN dir_len = strnlen((CHAR8 *)pkt_v4->BootpBootFile, 127); INTN i; UINT8 *dir = pkt_v4->BootpBootFile; @@ -274,13 +274,13 @@ static EFI_STATUS parseDhcp4() return EFI_OUT_OF_RESOURCES; if (dir_len > 0) { - strncpya(full_path, (CHAR8 *)dir, dir_len); + strncpy(full_path, (CHAR8 *)dir, dir_len); if (full_path[dir_len-1] == '/' && template[0] == '/') full_path[dir_len-1] = '\0'; } if (dir_len == 0 && dir[0] != '/' && template[0] == '/') template_ofs++; - strcata(full_path, template + template_ofs); + strcat(full_path, template + template_ofs); memcpy(&tftp_addr.v4, pkt_v4->BootpSiAddr, 4); return EFI_SUCCESS; diff --git a/sbat.c b/sbat.c index d8750962..fec22a73 100644 --- a/sbat.c +++ b/sbat.c @@ -48,7 +48,7 @@ parse_sbat_section(char *section_base, size_t section_size, efi_status = EFI_INVALID_PARAMETER; goto err; } - allocsz += strlena(row->columns[i]) + 1; + allocsz += strlen(row->columns[i]) + 1; } n++; } @@ -226,7 +226,7 @@ parse_sbat_var_data(list_t *entry_list, UINT8 *data, UINTN datasize) efi_status = EFI_INVALID_PARAMETER; goto err; } - allocsz += strlena(row->columns[i]) + 1; + allocsz += strlen(row->columns[i]) + 1; } n++; } diff --git a/shim.h b/shim.h index 61dafa82..3d2ac2d4 100644 --- a/shim.h +++ b/shim.h @@ -259,4 +259,6 @@ verify_buffer (char *data, int datasize, #define LogError(fmt, ...) #endif +char *translate_slashes(char *out, const char *str); + #endif /* SHIM_H_ */ -- cgit v1.2.3 From 9beca885c29c77bb901547321a5ce6fd3c9c8ee3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Mar 2021 14:40:03 -0500 Subject: Fix stdarg to work the same everywhere. This gets us the same working definition for VA_* va_* etc everywhere, and it's the same definition edk2 is using. Signed-off-by: Peter Jones --- Cryptlib/Include/OpenSslSupport.h | 125 ++++++-------------------------------- Cryptlib/Library/BaseLib.h | 16 +++++ Cryptlib/Makefile | 3 +- Cryptlib/OpenSSL/Makefile | 3 +- Make.defaults | 4 +- errlog.c | 24 ++++---- include/console.h | 2 +- include/hexdump.h | 10 +-- include/system/efistdarg.h | 15 +++++ include/system/stdarg.h | 41 ++++++++----- lib/Makefile | 3 +- lib/console.c | 12 ++-- shim.h | 23 +++++-- 13 files changed, 120 insertions(+), 161 deletions(-) create mode 100644 include/system/efistdarg.h (limited to 'include') diff --git a/Cryptlib/Include/OpenSslSupport.h b/Cryptlib/Include/OpenSslSupport.h index 6bb7ba64..1f475a32 100644 --- a/Cryptlib/Include/OpenSslSupport.h +++ b/Cryptlib/Include/OpenSslSupport.h @@ -15,6 +15,22 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef __OPEN_SSL_SUPPORT_H__ #define __OPEN_SSL_SUPPORT_H__ +#if defined(__x86_64__) +/* shim.h will check if the compiler is new enough in some other CU */ + +#if !defined(GNU_EFI_USE_EXTERNAL_STDARG) +#define GNU_EFI_USE_EXTERNAL_STDARG +#endif + +#if !defined(GNU_EFI_USE_MS_ABI) +#define GNU_EFI_USE_MS_ABI +#endif + +#ifdef NO_BUILTIN_VA_FUNCS +#undef NO_BUILTIN_VA_FUNCS +#endif +#endif + /* * Include stddef.h to avoid redefining "offsetof" */ @@ -64,113 +80,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // typedef VOID *FILE; -// -// Map all va_xxxx elements to VA_xxx defined in MdePkg/Include/Base.h -// -#if !defined(__CC_ARM) || defined(_STDARG_H) // if va_list is not already defined -/* - * These are now unconditionally #defined by GNU_EFI's efistdarg.h, - * so we should #undef them here before providing a new definition. - */ -#undef va_arg -#undef va_start -#undef va_end - -#define va_list VA_LIST -#define va_arg VA_ARG -#define va_start VA_START -#define va_end VA_END - -# if !defined(NO_BUILTIN_VA_FUNCS) - -typedef __builtin_va_list VA_LIST; - -#define VA_START(Marker, Parameter) __builtin_va_start (Marker, Parameter) - -#define VA_ARG(Marker, TYPE) ((sizeof (TYPE) < sizeof (UINTN)) ? (TYPE)(__builtin_va_arg (Marker, UINTN)) : (TYPE)(__builtin_va_arg (Marker, TYPE))) - -#define VA_END(Marker) __builtin_va_end (Marker) - -#define VA_COPY(Dest, Start) __builtin_va_copy (Dest, Start) - -# else - -#define _INT_SIZE_OF(n) ((sizeof (n) + sizeof (UINTN) - 1) &~(sizeof (UINTN) - 1)) -/// -/// Variable used to traverse the list of arguments. This type can vary by -/// implementation and could be an array or structure. -/// -typedef CHAR8 *VA_LIST; - -/** - Retrieves a pointer to the beginning of a variable argument list, based on - the name of the parameter that immediately precedes the variable argument list. - - This function initializes Marker to point to the beginning of the variable - argument list that immediately follows Parameter. The method for computing the - pointer to the next argument in the argument list is CPU-specific following the - EFIAPI ABI. - - @param Marker The VA_LIST used to traverse the list of arguments. - @param Parameter The name of the parameter that immediately precedes - the variable argument list. - - @return A pointer to the beginning of a variable argument list. - -**/ -#define VA_START(Marker, Parameter) (Marker = (VA_LIST) ((UINTN) & (Parameter) + _INT_SIZE_OF (Parameter))) - -/** - Returns an argument of a specified type from a variable argument list and updates - the pointer to the variable argument list to point to the next argument. - - This function returns an argument of the type specified by TYPE from the beginning - of the variable argument list specified by Marker. Marker is then updated to point - to the next argument in the variable argument list. The method for computing the - pointer to the next argument in the argument list is CPU-specific following the EFIAPI ABI. - - @param Marker VA_LIST used to traverse the list of arguments. - @param TYPE The type of argument to retrieve from the beginning - of the variable argument list. - - @return An argument of the type specified by TYPE. - -**/ -#define VA_ARG(Marker, TYPE) (*(TYPE *) ((Marker += _INT_SIZE_OF (TYPE)) - _INT_SIZE_OF (TYPE))) - -/** - Terminates the use of a variable argument list. - - This function initializes Marker so it can no longer be used with VA_ARG(). - After this macro is used, the only way to access the variable argument list is - by using VA_START() again. - - @param Marker VA_LIST used to traverse the list of arguments. - -**/ -#define VA_END(Marker) (Marker = (VA_LIST) 0) - -/** - Initializes a VA_LIST as a copy of an existing VA_LIST. - - This macro initializes Dest as a copy of Start, as if the VA_START macro had been applied to Dest - followed by the same sequence of uses of the VA_ARG macro as had previously been used to reach - the present state of Start. - - @param Dest VA_LIST used to traverse the list of arguments. - @param Start VA_LIST used to traverse the list of arguments. - -**/ -#define VA_COPY(Dest, Start) ((void)((Dest) = (Start))) - -# endif - -#else // __CC_ARM -#define va_start(Marker, Parameter) __va_start(Marker, Parameter) -#define va_arg(Marker, TYPE) __va_arg(Marker, TYPE) -#define va_end(Marker) ((void)0) -#endif - // // #defines from EFI Application Toolkit required to buiild Open SSL // @@ -318,7 +227,7 @@ size_t fwrite (const void *, size_t, size_t, FILE *); char *fgets (char *, int, FILE *); int fputs (const char *, FILE *); int fprintf (FILE *, const char *, ...); -int vfprintf (FILE *, const char *, VA_LIST); +int vfprintf (FILE *, const char *, va_list); int fflush (FILE *); int fclose (FILE *); DIR *opendir (const char *); diff --git a/Cryptlib/Library/BaseLib.h b/Cryptlib/Library/BaseLib.h index 93d5c691..94b25c93 100644 --- a/Cryptlib/Library/BaseLib.h +++ b/Cryptlib/Library/BaseLib.h @@ -1,3 +1,19 @@ +#if defined(__x86_64__) +/* shim.h will check if the compiler is new enough in some other CU */ + +#if !defined(GNU_EFI_USE_EXTERNAL_STDARG) +#define GNU_EFI_USE_EXTERNAL_STDARG +#endif + +#if !defined(GNU_EFI_USE_MS_ABI) +#define GNU_EFI_USE_MS_ABI +#endif + +#ifdef NO_BUILTIN_VA_FUNCS +#undef NO_BUILTIN_VA_FUNCS +#endif +#endif + #include #include diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 27614618..547fd106 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -22,8 +22,7 @@ CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) ifeq ($(ARCH),x86_64) FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone $(CLANG_BUGS) -DEFINES += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ - -DNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 +DEFINES += -DMDE_CPU_X64 endif ifeq ($(ARCH),ia32) FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone $(CLANG_BUGS) diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index 294e889a..1b8ca318 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -36,8 +36,7 @@ CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) ifeq ($(ARCH),x86_64) FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone $(CLANG_BUGS) -DEFINES += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ - -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 +DEFINES += -DMDE_CPU_X64 endif ifeq ($(ARCH),ia32) FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) diff --git a/Make.defaults b/Make.defaults index 50164ae8..f956c005 100644 --- a/Make.defaults +++ b/Make.defaults @@ -53,9 +53,7 @@ COMMIT_ID ?= $(shell if [ -e .git ] ; then git log -1 --pretty=format:%H ; elif ifeq ($(ARCH),x86_64) ARCH_CFLAGS ?= -mno-mmx -mno-sse -mno-red-zone -nostdinc \ $(CLANG_BUGS) -m64 \ - -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ - -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 \ - -DPAGE_SIZE=4096 + -DMDE_CPU_X64 -DPAGE_SIZE=4096 ARCH_GNUEFI ?= x86_64 ARCH_SUFFIX ?= x64 ARCH_SUFFIX_UPPER ?= X64 diff --git a/errlog.c b/errlog.c index 16af23b0..ac657151 100644 --- a/errlog.c +++ b/errlog.c @@ -10,24 +10,26 @@ static CHAR16 **errs = NULL; static UINTN nerrs = 0; EFI_STATUS EFIAPI -vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, elf_va_list args) +vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, + va_list args) { - elf_va_list args2; + va_list args2; EFI_STATUS efi_status = EFI_SUCCESS; if (verbose) { - elf_va_copy(args2, args); + va_copy(args2, args); console_print(L"%a:%d:%a() ", file, line, func); efi_status = VPrint(fmt, args2); - elf_va_end(args2); + va_end(args2); } return efi_status; } EFI_STATUS EFIAPI -VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, elf_va_list args) +VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, + va_list args) { - elf_va_list args2; + va_list args2; CHAR16 **newerrs; newerrs = ReallocatePool(errs, (nerrs + 1) * sizeof(*errs), @@ -38,11 +40,11 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, elf_v newerrs[nerrs] = PoolPrint(L"%a:%d %a() ", file, line, func); if (!newerrs[nerrs]) return EFI_OUT_OF_RESOURCES; - elf_va_copy(args2, args); + va_copy(args2, args); newerrs[nerrs+1] = VPoolPrint(fmt, args2); if (!newerrs[nerrs+1]) return EFI_OUT_OF_RESOURCES; - elf_va_end(args2); + va_end(args2); nerrs += 2; newerrs[nerrs] = NULL; @@ -54,12 +56,12 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, elf_v EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { - elf_va_list args; + va_list args; EFI_STATUS efi_status; - elf_va_start(args, fmt); + va_start(args, fmt); efi_status = VLogError(file, line, func, fmt, args); - elf_va_end(args); + va_end(args); return efi_status; } diff --git a/include/console.h b/include/console.h index d8af3cd3..00982744 100644 --- a/include/console.h +++ b/include/console.h @@ -102,7 +102,7 @@ extern UINT32 verbose; dprint_(L"%a:%d:%a() " fmt, __FILE__, __LINE__ - 1, __func__, \ ##__VA_ARGS__) extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, - const char *func, elf_va_list args); + const char *func, va_list args); #define vdprint(fmt, ...) \ vdprint_(fmt, __FILE__, __LINE__ - 1, __func__, ##__VA_ARGS__) diff --git a/include/hexdump.h b/include/hexdump.h index a6aa2bfa..f778de9a 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -81,7 +81,7 @@ prepare_text(const void *data, size_t size, char *buf, unsigned int position) */ static inline void UNUSED EFIAPI vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, - const void *data, unsigned long size, size_t at, elf_va_list ap) + const void *data, unsigned long size, size_t at, va_list ap) { unsigned long display_offset = at; unsigned long offset = 0; @@ -114,15 +114,15 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, * hexdump formatted * think of it as: printf("%s%s", format(fmt, ...), hexdump(data,size)[lineN]); */ -static inline void UNUSED +static inline void UNUSED EFIAPI hexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, const void *data, unsigned long size, size_t at, ...) { - elf_va_list ap; + va_list ap; - elf_va_start(ap, at); + va_start(ap, at); vhexdumpf(file, line, func, fmt, data, size, at, ap); - elf_va_end(ap); + va_end(ap); } static inline void UNUSED diff --git a/include/system/efistdarg.h b/include/system/efistdarg.h new file mode 100644 index 00000000..837c4f23 --- /dev/null +++ b/include/system/efistdarg.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * efistdarg.h - AAAARGGGG + * Copyright Peter Jones + */ + +#ifndef SHIM_UNIT_TEST +#ifndef _EFISTDARG_H_ +#define _EFISTDARG_H_ + +#include + +#endif /* !_EFISTDARG_H_ */ +#endif +// vim:fenc=utf-8:tw=75:noet diff --git a/include/system/stdarg.h b/include/system/stdarg.h index 346b760d..af1ac59b 100644 --- a/include/system/stdarg.h +++ b/include/system/stdarg.h @@ -8,24 +8,33 @@ #ifndef _STDARG_H #define _STDARG_H -#include - -#endif /* !_STDARG_H */ +#ifndef GNU_EFI_USE_EXTERNAL_STDARG +#define GNU_EFI_USE_EXTERNAL_STDARG #endif -#ifndef SHIM_STDARG_H_ -#define SHIM_STDARG_H_ -typedef __builtin_ms_va_list ms_va_list; -#define ms_va_copy(dest, start) __builtin_ms_va_copy(dest, start) -#define ms_va_start(marker, arg) __builtin_ms_va_start(marker, arg) -#define ms_va_arg(marker, type) __builtin_va_arg(marker, type) -#define ms_va_end(marker) __builtin_ms_va_end(marker) +#if defined(__aarch64__) || defined(__arm__) || defined(__i386__) || \ + defined(__i486__) || defined(__i686__) || defined(SHIM_UNIT_TEST) +typedef __builtin_va_list va_list; +#define va_copy(dest, start) __builtin_va_copy(dest, start) +#define va_start(marker, arg) __builtin_va_start(marker, arg) +#define va_arg(marker, type) __builtin_va_arg(marker, type) +#define va_end(marker) __builtin_va_end(marker) +#elif defined(__x86_64__) +typedef __builtin_ms_va_list va_list; +#define va_copy(dest, start) __builtin_ms_va_copy(dest, start) +#define va_start(marker, arg) __builtin_ms_va_start(marker, arg) +#define va_arg(marker, type) __builtin_va_arg(marker, type) +#define va_end(marker) __builtin_ms_va_end(marker) +#else +#error what arch is this +#endif -typedef __builtin_va_list elf_va_list; -#define elf_va_copy(dest, start) __builtin_va_copy(dest, start) -#define elf_va_start(marker, arg) __builtin_va_start(marker, arg) -#define elf_va_arg(marker, type) __builtin_va_arg(marker, type) -#define elf_va_end(marker) __builtin_va_end(marker) +typedef va_list VA_LIST; +#define VA_COPY(dest, start) va_copy(dest, start) +#define VA_START(marker, arg) va_start(marker, arg) +#define VA_END(marker) va_end(marker) +#define VA_ARG(marker, type) va_arg(marker, type) -#endif /* !SHIM_STDARG_H_ */ +#endif /* !_STDARG_H */ +#endif /* !SHIM_UNIT_TEST */ // vim:fenc=utf-8:tw=75:noet diff --git a/lib/Makefile b/lib/Makefile index 63893c3e..0d2d0a9d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,8 +17,7 @@ CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) ifeq ($(ARCH),x86_64) FEATUREFLAGS += -m64 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) -DEFINES += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \ - -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 +DEFINES += -DMDE_CPU_X64 endif ifeq ($(ARCH),ia32) FEATUREFLAGS += -m32 -mno-mmx -mno-sse -mno-red-zone -nostdinc $(CLANG_BUGS) diff --git a/lib/console.c b/lib/console.c index 32c6d55d..2da20b31 100644 --- a/lib/console.c +++ b/lib/console.c @@ -86,15 +86,15 @@ VOID console_fini(VOID) UINTN EFIAPI console_print(const CHAR16 *fmt, ...) { - elf_va_list args; + va_list args; UINTN ret; if (!console_text_mode) setup_console(1); - elf_va_start(args, fmt); + va_start(args, fmt); ret = VPrint(fmt, args); - elf_va_end(args); + va_end(args); return ret; } @@ -103,7 +103,7 @@ UINTN EFIAPI console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) { SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; - elf_va_list args; + va_list args; UINTN ret; if (!console_text_mode) @@ -111,9 +111,9 @@ console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) co->SetCursorPosition(co, col, row); - elf_va_start(args, fmt); + va_start(args, fmt); ret = VPrint(fmt, args); - elf_va_end(args); + va_end(args); return ret; } diff --git a/shim.h b/shim.h index 3d2ac2d4..0ea182eb 100644 --- a/shim.h +++ b/shim.h @@ -17,20 +17,29 @@ #endif #if defined(__x86_64__) -#if !defined(GNU_EFI_USE_MS_ABI) -#error On x86_64 you must use ms_abi (GNU_EFI_USE_MS_ABI) in gnu-efi and shim. -#endif /* gcc 4.5.4 is the first documented release with -mabi=ms */ #if !GNUC_PREREQ(4, 7) && !CLANG_PREREQ(3, 4) #error On x86_64 you must have a compiler new enough to support __attribute__((__ms_abi__)) #endif + +#if !defined(GNU_EFI_USE_EXTERNAL_STDARG) +#define GNU_EFI_USE_EXTERNAL_STDARG +#endif + +#if !defined(GNU_EFI_USE_MS_ABI) +#define GNU_EFI_USE_MS_ABI +#endif + +#ifdef NO_BUILTIN_VA_FUNCS +#undef NO_BUILTIN_VA_FUNCS +#endif #endif #include #include #include #include -#include +#include #include #include @@ -40,6 +49,10 @@ #undef uefi_call_wrapper #include #include + +#if defined(__x86_64__) && !defined(HAVE_USE_MS_ABI) +#error something has gone wrong with the gnu-efi includes and defines +#endif #endif #ifdef SHIM_UNIT_TEST @@ -209,7 +222,7 @@ extern void shim_fini(void); extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...); extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, - const CHAR16 *fmt, elf_va_list args); + const CHAR16 *fmt, va_list args); extern VOID LogHexdump_(const char *file, int line, const char *func, const void *data, size_t sz); extern VOID PrintErrors(VOID); -- cgit v1.2.3 From bbdfa72a0a5f8d5a8dd4a47e67195504a22ece5b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 9 Mar 2021 11:56:31 -0500 Subject: Add some test cases, and make "make test" actually work. Note the one test case I'm not 100% sure about. Someone let me know. Signed-off-by: Peter Jones --- include/console.h | 6 ++ include/system/string.h | 17 +++++ include/test.h | 47 +++++++++++++ include/test.mk | 1 + lib/string.c | 17 +++++ shim.h | 4 ++ test-sbat.c | 47 ++++++------- test-str.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 283 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/console.h b/include/console.h index 00982744..036262ef 100644 --- a/include/console.h +++ b/include/console.h @@ -92,6 +92,7 @@ struct _EFI_CONSOLE_CONTROL_PROTOCOL { extern VOID console_fini(VOID); extern VOID setup_verbosity(VOID); extern UINT32 verbose; +#ifndef SHIM_UNIT_TEST #define dprint_(fmt, ...) ({ \ UINTN __dprint_ret = 0; \ if (verbose) \ @@ -101,6 +102,11 @@ extern UINT32 verbose; #define dprint(fmt, ...) \ dprint_(L"%a:%d:%a() " fmt, __FILE__, __LINE__ - 1, __func__, \ ##__VA_ARGS__) +#else +#define dprint_(...) +#define dprint(fmt, ...) +#endif + extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, va_list args); #define vdprint(fmt, ...) \ diff --git a/include/system/string.h b/include/system/string.h index 822e28fa..66e7d93e 100644 --- a/include/system/string.h +++ b/include/system/string.h @@ -1,6 +1,23 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent #ifdef SHIM_UNIT_TEST #include_next + +__typeof__(strlen) shim_strlen; +__typeof__(strcmp) shim_strcmp; +__typeof__(strncmp) shim_strncmp; +__typeof__(strncasecmp) shim_strncasecmp; +__typeof__(strcasecmp) shim_strcasecmp; +__typeof__(strrchr) shim_strrchr; +__typeof__(strrchr) shim_strrchr; +__typeof__(strnlen) shim_strnlen; +__typeof__(strcpy) shim_strcpy; +__typeof__(strncpy) shim_strncpy; +__typeof__(strdup) shim_strdup; +__typeof__(strndup) shim_strndup; +__typeof__(stpcpy) shim_stpcpy; +__typeof__(strchrnul) shim_strchrnul; +__typeof__(strchr) shim_strchr; + #else #ifndef _STRING_H #define _STRING_H diff --git a/include/test.h b/include/test.h index 8a970fd2..4441f969 100644 --- a/include/test.h +++ b/include/test.h @@ -63,6 +63,53 @@ extern int debug; assert(cond); \ }) +#define assert_true_return(a, status, fmt, ...) \ + ({ \ + if (!(a)) { \ + printf("%s:%d:got %lld, expected nonzero " fmt, \ + __func__, __LINE__, (long long)(a), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(!(a))); \ + return status; \ + } \ + }) +#define assert_nonzero_return(a, ...) assert_true_return(a, ##__VA_ARGS__) + +#define assert_false_return(a, status, fmt, ...) \ + ({ \ + if (a) { \ + printf("%s:%d:got %lld, expected zero " fmt, __func__, \ + __LINE__, (long long)(a), ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a)); \ + return status; \ + } \ + }) +#define assert_zero_return(a, ...) assert_false_return(a, ##__VA_ARGS__) + +#define assert_positive_return(a, status, fmt, ...) \ + ({ \ + if ((a) <= 0) { \ + printf("%s:%d:got %lld, expected > 0 " fmt, __func__, \ + __LINE__, (long long)(a), ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify((a) <= 0)); \ + return status; \ + } \ + }) + +#define assert_negative_return(a, status, fmt, ...) \ + ({ \ + if ((a) >= 0) { \ + printf("%s:%d:got %lld, expected < 0 " fmt, __func__, \ + __LINE__, (long long)(a), ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify((a) >= 0)); \ + return status; \ + } \ + }) + #define assert_equal_return(a, b, status, fmt, ...) \ ({ \ if (!((a) == (b))) { \ diff --git a/include/test.mk b/include/test.mk index 735d0dc5..542be176 100644 --- a/include/test.mk +++ b/include/test.mk @@ -35,6 +35,7 @@ test-random.h: xxd -i random.bin test-random.h test-sbat_FILES = csv.c +test-str_FILES = lib/string.c tests := $(patsubst %.c,%,$(wildcard test-*.c)) diff --git a/lib/string.c b/lib/string.c index 1f58f3ec..3dc6f1cd 100644 --- a/lib/string.c +++ b/lib/string.c @@ -5,6 +5,23 @@ #define SHIM_STRING_C_ #include "shim.h" +#ifdef SHIM_UNIT_TEST +#define strlen shim_strlen +#define strcmp shim_strcmp +#define strncmp shim_strncmp +#define strncasecmp shim_strncasecmp +#define strcasecmp shim_strcasecmp +#define strrchr shim_strrchr +#define strlen shim_strlen +#define strcpy shim_strcpy +#define strncpy shim_strncpy +#define strdup shim_strdup +#define strndup shim_strndup +#define stpcpy shim_stpcpy +#define strchrnul shim_strchrnul +#define strchr shim_strchr +#endif + size_t strlen(const char *s1) { diff --git a/shim.h b/shim.h index 0ea182eb..44dddc7a 100644 --- a/shim.h +++ b/shim.h @@ -3,6 +3,10 @@ #ifndef SHIM_H_ #define SHIM_H_ +#ifdef SHIM_UNIT_TEST +#define _GNU_SOURCE +#endif + #if defined __GNUC__ && defined __GNUC_MINOR__ # define GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) diff --git a/test-sbat.c b/test-sbat.c index c671f03e..780e5cbe 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -427,26 +427,29 @@ test_parse_sbat_var_data(void) return 0; } -#if 0 /* * verify_sbat() tests * Note: verify_sbat also frees the underlying "sbat_entries" memory. */ int -test_verify_sbat_null_sbat(void) +test_verify_sbat_null_sbat_section(void) { - list_t *test_sbat_entries; - test_sbat_entries = create_mock_sbat_entries_one_entry("test1", "1"); - if (!test_sbat_entries) - return -1; + char sbat_var_data[] = "test1,1"; EFI_STATUS status; + list_t test_sbat_var; + size_t n = 0; + struct sbat_section_entry **entries = NULL; - status = verify_sbat(NULL, test_sbat_entries); + INIT_LIST_HEAD(&test_sbat_var); + status = parse_sbat_var_data(&test_sbat_var, sbat_var_data, sizeof(sbat_var_data)); + assert_equal_return(status, EFI_SUCCESS, -1, "got %#x expected %#x\n"); - assert(status == EFI_INVALID_PARAMETER); + status = verify_sbat_helper(&sbat_var, n, entries); + assert_equal_return(status, EFI_SUCCESS, -1, "got %#x expected %#x\n"); return 0; } +#if 0 int test_verify_sbat_null_sbat_entries(void) { @@ -969,22 +972,22 @@ main(void) test(test_parse_sbat_var_data_null_data); test(test_parse_sbat_var_data_zero_size); -#if 0 // verify_sbat tests - //test_verify_sbat_null_sbat(); - test_verify_sbat_null_sbat_entries(); - test_verify_sbat_match_one_exact(); - test_verify_sbat_match_one_higher(); - test_verify_sbat_reject_one(); - test_verify_sbat_reject_many(); - test_verify_sbat_match_many_higher(); - test_verify_sbat_match_many_exact(); - test_verify_sbat_reject_many_all(); - test_verify_sbat_match_diff_name(); - test_verify_sbat_match_diff_name_mixed(); - test_verify_sbat_reject_diff_name_mixed(); + test(test_verify_sbat_null_sbat_section); +#if 0 + test(test_verify_sbat_null_sbat_entries); + test(test_verify_sbat_match_one_exact); + test(test_verify_sbat_match_one_higher); + test(test_verify_sbat_reject_one); + test(test_verify_sbat_reject_many); + test(test_verify_sbat_match_many_higher); + test(test_verify_sbat_match_many_exact); + test(test_verify_sbat_reject_many_all); + test(test_verify_sbat_match_diff_name); + test(test_verify_sbat_match_diff_name_mixed); + test(test_verify_sbat_reject_diff_name_mixed); #endif - test_parse_and_verify(); + test(test_parse_and_verify); return 0; } diff --git a/test-str.c b/test-str.c index 8befa223..39b6d07a 100644 --- a/test-str.c +++ b/test-str.c @@ -13,15 +13,39 @@ #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic error "-Wnonnull" +/* + * copy-pasta from gnu-efi + */ +static inline UINTN +strncmpa ( + IN CONST CHAR8 *s1, + IN CONST CHAR8 *s2, + IN UINTN len + ) +// compare strings +{ + while (*s1 && len) { + if (*s1 != *s2) { + break; + } + + s1 += 1; + s2 += 1; + len -= 1; + } + + return len ? *s1 - *s2 : 0; +} + int test_strchrnul(void) { const char s0[] = "abcd\0fghi"; - assert_equal_return(strchrnul(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); - assert_equal_return(strchrnul(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); - assert_equal_return(strchrnul(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); - assert_equal_return(strchrnul(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchrnul(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchrnul(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchrnul(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchrnul(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); assert_equal_return(strnchrnul(s0, 0, 'b'), &s0[0], -1, "got %p expected %p\n"); assert_equal_return(strnchrnul(s0, -1, 'b'), &s0[1], 1, "got %p expected %p\n"); @@ -35,6 +59,143 @@ test_strchrnul(void) return 0; } +int +test_strncmp(void) +{ + /* + * these are constants so that the failures are readable if you get + * it wrong. + */ +#define s0 "sbat," +#define s0sz 6 +#define s0len 5 +#define s1 "sbat,1,2021030218" +#define s1sz 18 +#define s1len 17 +#define s2 "sbat,1,20210302" +#define s2sz 16 +#define s2len 15 +#define s3 "sbat,1,20210303" +#define s3sz 16 +#define s3len 15 + + int diff; + + assert_zero_return(strncmp(s0, s0, s0len), -1, "\n"); + assert_zero_return(strncmp(s0, s0, s0sz), -1, "\n"); + + assert_zero_return(strncmp(s0, s1, s0len), -1, "\n"); + assert_negative_return(strncmp(s0, s1, s0sz), -1, "\n"); + assert_equal_return(strncmp(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmp(s1, s0, s0sz), -1, "\n"); + assert_equal_return(strncmp(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); + + assert_positive_return(strncmp(s1, s2, s1sz), -1, "\n"); + assert_equal_return(strncmp(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmp(s1, s2, s1len), -1, "\n"); + assert_equal_return(strncmp(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); + assert_negative_return(strncmp(s2, s1, s1sz), -1, "\n"); + assert_equal_return(strncmp(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); + + assert_zero_return(strncmp(s1, s2, s2len), -1, "\n"); + assert_positive_return(strncmp(s1, s2, s2sz), -1, "\n"); + assert_equal_return(strncmp(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + + assert_negative_return(strncmp(s2, s3, s2sz), -1, "\n"); + assert_equal_return(strncmp(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + assert_equal_return(strncmp(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + assert_negative_return(strncmp(s2, s3, s2len), -1, "\n"); + assert_zero_return(strncmp(s2, s3, s2len - 1), -1, "\n"); + assert_false_return(strncmp(s1, s2, s2len), -1, "\n"); + + /* + * Now test gnu-efi's version, but with a cast back to a sane type + */ +#define strncmpa(a, b, c) ((INTN)strncmpa(a, b, c)) + + assert_zero_return(strncmpa(s0, s0, s0len), -1, "\n"); + assert_zero_return(strncmpa(s0, s0, s0sz), -1, "\n"); + + assert_zero_return(strncmpa(s0, s1, s0len), -1, "\n"); + assert_negative_return(strncmpa(s0, s1, s0sz), -1, "\n"); + assert_equal_return(strncmpa(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmpa(s1, s0, s0sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); + + assert_positive_return(strncmpa(s1, s2, s1sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmpa(s1, s2, s1len), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); + assert_negative_return(strncmpa(s2, s1, s1sz), -1, "\n"); + assert_equal_return(strncmpa(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); + + assert_zero_return(strncmpa(s1, s2, s2len), -1, "\n"); + assert_positive_return(strncmpa(s1, s2, s2sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + + assert_negative_return(strncmpa(s2, s3, s2sz), -1, "\n"); + assert_equal_return(strncmpa(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + assert_equal_return(strncmpa(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + assert_negative_return(strncmpa(s2, s3, s2len), -1, "\n"); + assert_zero_return(strncmpa(s2, s3, s2len - 1), -1, "\n"); + assert_false_return(strncmpa(s1, s2, s2len), -1, "\n"); + + + /* + * Once more, but with the casting /and the warnings/ turned off + * + * The ones marked with XXX I've inverted the test to make it work + * "correctly", because UINTN is what makes positive. + */ +#undef strncmpa +#pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wsign-compare" + + assert_zero_return(strncmpa(s0, s0, s0len), -1, "\n"); + assert_zero_return(strncmpa(s0, s0, s0sz), -1, "\n"); + + assert_zero_return(strncmpa(s0, s1, s0len), -1, "\n"); + /*XXX*/assert_positive_return(strncmpa(s0, s1, s0sz), -1, "\n");/*XXX*/ + assert_equal_return(strncmpa(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmpa(s1, s0, s0sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); + + assert_positive_return(strncmpa(s1, s2, s1sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + assert_positive_return(strncmpa(s1, s2, s1len), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); + /*XXX*/ assert_positive_return(strncmpa(s2, s1, s1sz), -1, "\n");/*XXX*/ + assert_equal_return(strncmpa(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); + + assert_zero_return(strncmpa(s1, s2, s2len), -1, "\n"); + assert_positive_return(strncmpa(s1, s2, s2sz), -1, "\n"); + assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); + + assert_positive_return(strncmpa(s2, s3, s2sz), -1, "\n"); + assert_equal_return(strncmpa(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + assert_equal_return(strncmpa(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); + /*XXX*/assert_positive_return(strncmpa(s2, s3, s2len), -1,"\n");/*XXX*/ + assert_zero_return(strncmpa(s2, s3, s2len - 1), -1, "\n"); + assert_false_return(strncmpa(s1, s2, s2len), -1, "\n"); + +#pragma GCC diagnostic error "-Wsign-compare" +#pragma GCC diagnostic error "-Wtype-limits" + return 0; + +#undef s0 +#undef s0sz +#undef s0len +#undef s1 +#undef s1sz +#undef s1len +#undef s2 +#undef s2sz +#undef s2len +#undef s3 +#undef s3sz +#undef s3len +} + int test_strntoken_null(void) { bool ret; @@ -416,6 +577,7 @@ main(void) { int status = 0; test(test_strchrnul); + test(test_strncmp); test(test_strntoken_null); test(test_strntoken_size_0); test(test_strntoken_empty_size_1); -- cgit v1.2.3 From 8d006f5c70b19d6e7f35f83e61880deb45b05977 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 10 Mar 2021 10:46:32 -0500 Subject: Test our strncmp vs known failing ones as well Signed-off-by: Peter Jones --- include/test.h | 87 ++++++++++++--- test-str.c | 336 +++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 293 insertions(+), 130 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index 4441f969..92d1314e 100644 --- a/include/test.h +++ b/include/test.h @@ -63,73 +63,132 @@ extern int debug; assert(cond); \ }) -#define assert_true_return(a, status, fmt, ...) \ +#define assert_true_as_expr(a, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if (!(a)) { \ printf("%s:%d:got %lld, expected nonzero " fmt, \ __func__, __LINE__, (long long)(a), \ ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify(!(a))); \ - return status; \ + rc_ = status; \ } \ + rc_; \ }) -#define assert_nonzero_return(a, ...) assert_true_return(a, ##__VA_ARGS__) +#define assert_nonzero_as_expr(a, ...) assert_true_as_expr(a, ##__VA_ARGS__) -#define assert_false_return(a, status, fmt, ...) \ +#define assert_false_as_expr(a, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if (a) { \ printf("%s:%d:got %lld, expected zero " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify(a)); \ - return status; \ + rc_ = status; \ } \ + rc_; \ }) -#define assert_zero_return(a, ...) assert_false_return(a, ##__VA_ARGS__) +#define assert_zero_as_expr(a, ...) assert_false_as_expr(a, ##__VA_ARGS__) -#define assert_positive_return(a, status, fmt, ...) \ +#define assert_positive_as_expr(a, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if ((a) <= 0) { \ printf("%s:%d:got %lld, expected > 0 " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify((a) <= 0)); \ - return status; \ + rc_ = status; \ } \ + rc_; \ }) -#define assert_negative_return(a, status, fmt, ...) \ +#define assert_negative_as_expr(a, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if ((a) >= 0) { \ printf("%s:%d:got %lld, expected < 0 " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify((a) >= 0)); \ - return status; \ + rc_ = status; \ } \ + rc_; \ }) -#define assert_equal_return(a, b, status, fmt, ...) \ +#define assert_equal_as_expr(a, b, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if (!((a) == (b))) { \ printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify(a == b)); \ - return status; \ + rc_ = status; \ } \ + rc_; \ }) -#define assert_return(cond, status, fmt, ...) \ +#define assert_as_expr(cond, status, fmt, ...) \ ({ \ + int rc_ = 0; \ if (!(cond)) { \ printf("%s:%d:" fmt, __func__, __LINE__, \ ##__VA_ARGS__); \ printf("%s:%d:Assertion `%s' failed.\n", __func__, \ __LINE__, __stringify(cond)); \ - return status; \ + rc_ = status; \ } \ + rc_; \ + }) + +#define assert_true_return(a, status, fmt, ...) \ + ({ \ + int rc_ = assert_true_as_expr(a, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) +#define assert_nonzero_return(a, ...) assert_true_return(a, ##__VA_ARGS__) + +#define assert_false_return(a, status, fmt, ...) \ + ({ \ + int rc_ = assert_false_as_expr(a, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) +#define assert_zero_return(a, ...) assert_false_return(a, ##__VA_ARGS__) + +#define assert_positive_return(a, status, fmt, ...) \ + ({ \ + int rc_ = assert_positive_as_expr(a, status, fmt, \ + ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) + +#define assert_negative_return(a, status, fmt, ...) \ + ({ \ + int rc_ = assert_negative_as_expr(a, status, fmt, \ + ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) + +#define assert_equal_return(a, b, status, fmt, ...) \ + ({ \ + int rc_ = assert_equal_as_expr(a, b, status, fmt, \ + ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) + +#define assert_return(cond, status, fmt, ...) \ + ({ \ + int rc_ = assert_as_expr(cond, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ }) #define assert_goto(cond, label, fmt, ...) \ diff --git a/test-str.c b/test-str.c index 39b6d07a..deb753d9 100644 --- a/test-str.c +++ b/test-str.c @@ -13,16 +13,49 @@ #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic error "-Wnonnull" +int +test_strchrnul_helper(__typeof__(strchrnul) fn) +{ + const char s0[] = "abcd\0fghi"; + + assert_equal_return(fn(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); + + return 0; +} + +int +test_strchrnul(void) +{ + const char s0[] = "abcd\0fghi"; + + assert_equal_return(test_strchrnul_helper(shim_strchrnul), + test_strchrnul_helper(strchrnul), + -1, "got %d expected %d\n"); + + assert_equal_return(strnchrnul(s0, 0, 'b'), &s0[0], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, -1, 'b'), &s0[1], 1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 2, 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 4, 'f'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 5, 'f'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 8, 'f'), &s0[4], -1, "got %p expected %p\n"); + + assert_equal_return(strnchrnul(&s0[4], 1, 'f'), &s0[4], -1, "got %p expected %p\n"); + + return 0; +} + /* * copy-pasta from gnu-efi */ static inline UINTN -strncmpa ( +gnuefi_strncmp ( IN CONST CHAR8 *s1, IN CONST CHAR8 *s2, IN UINTN len ) -// compare strings { while (*s1 && len) { if (*s1 != *s2) { @@ -37,35 +70,54 @@ strncmpa ( return len ? *s1 - *s2 : 0; } -int -test_strchrnul(void) +static inline INTN +gnuefi_signed_strncmp ( + IN CONST CHAR8 *s1, + IN CONST CHAR8 *s2, + IN UINTN len + ) { - const char s0[] = "abcd\0fghi"; + while (*s1 && len) { + if (*s1 != *s2) { + break; + } - assert_equal_return(shim_strchrnul(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); - assert_equal_return(shim_strchrnul(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); - assert_equal_return(shim_strchrnul(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); - assert_equal_return(shim_strchrnul(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); + s1 += 1; + s2 += 1; + len -= 1; + } - assert_equal_return(strnchrnul(s0, 0, 'b'), &s0[0], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, -1, 'b'), &s0[1], 1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 2, 'b'), &s0[1], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 4, 'f'), &s0[3], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 5, 'f'), &s0[4], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 8, 'f'), &s0[4], -1, "got %p expected %p\n"); + return len ? *s1 - *s2 : 0; +} - assert_equal_return(strnchrnul(&s0[4], 1, 'f'), &s0[4], -1, "got %p expected %p\n"); +static inline INTN +gnuefi_good_strncmp ( + IN CONST CHAR8 *s1p, + IN CONST CHAR8 *s2p, + IN UINTN len + ) +{ + CONST UINT8 *s1 = (CONST UINT8 *)s1p; + CONST UINT8 *s2 = (CONST UINT8 *)s2p; - return 0; + while (*s1 && len) { + if (*s1 != *s2) { + break; + } + + s1 += 1; + s2 += 1; + len -= 1; + } + + return len ? *s1 - *s2 : 0; } -int -test_strncmp(void) -{ - /* - * these are constants so that the failures are readable if you get - * it wrong. - */ + +/* + * these are constants so that the failures are readable if you get + * it wrong. + */ #define s0 "sbat," #define s0sz 6 #define s0len 5 @@ -78,109 +130,162 @@ test_strncmp(void) #define s3 "sbat,1,20210303" #define s3sz 16 #define s3len 15 +#define s4 "sbat\314\234\014," +#define s4sz 9 +#define s4len 8 +/* + * same as s4 but with a UTF8 encoding error; one bit is cleared. + */ +#define s5 "sbat\314\034\014," +#define s5sz 9 +#define s5len 8 + +#define test_strncmp_helper(fn, invert_sign_errors, invert_encoding_errors) \ + ({ \ + printf("testing %s\n", #fn); \ + int status_ = 0, rc_; \ + \ + rc_ = assert_zero_as_expr(fn(s0, s0, s0len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_zero_as_expr(fn(s0, s0, s0sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_zero_as_expr(fn(s0, s1, s0len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s0, s1, s0sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s0, s1, s0sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s0, s1, s0sz), \ + s0[s0len] - s1[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s0, s0sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s1, s0, s0sz), \ + s1[s0len] - s0[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_positive_as_expr(fn(s1, s2, s1sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s2, s1len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2len), \ + s1[s2len - 1] - s2[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s1, s1sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s1, s1sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s2, s1, s1sz), \ + s2[s2len] - s1[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_zero_as_expr(fn(s1, s2, s2len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s2, s2sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3, s2sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3, s2sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s2, s3, s2sz), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s2, s3, s2len), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3, s2len), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3, s2len), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_zero_as_expr(fn(s2, s3, s2len - 1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_false_as_expr(fn(s1, s2, s2len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_encoding_errors) \ + rc_ = assert_negative_as_expr(fn(s4, s5, s4sz), -1, \ + "\n"); \ + else \ + rc_ = assert_positive_as_expr(fn(s4, s5, s4sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (status_ < 0) \ + printf("%s failed\n", #fn); \ + status_; \ + }) - int diff; - - assert_zero_return(strncmp(s0, s0, s0len), -1, "\n"); - assert_zero_return(strncmp(s0, s0, s0sz), -1, "\n"); - - assert_zero_return(strncmp(s0, s1, s0len), -1, "\n"); - assert_negative_return(strncmp(s0, s1, s0sz), -1, "\n"); - assert_equal_return(strncmp(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmp(s1, s0, s0sz), -1, "\n"); - assert_equal_return(strncmp(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); - - assert_positive_return(strncmp(s1, s2, s1sz), -1, "\n"); - assert_equal_return(strncmp(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmp(s1, s2, s1len), -1, "\n"); - assert_equal_return(strncmp(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); - assert_negative_return(strncmp(s2, s1, s1sz), -1, "\n"); - assert_equal_return(strncmp(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); - - assert_zero_return(strncmp(s1, s2, s2len), -1, "\n"); - assert_positive_return(strncmp(s1, s2, s2sz), -1, "\n"); - assert_equal_return(strncmp(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - - assert_negative_return(strncmp(s2, s3, s2sz), -1, "\n"); - assert_equal_return(strncmp(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - assert_equal_return(strncmp(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - assert_negative_return(strncmp(s2, s3, s2len), -1, "\n"); - assert_zero_return(strncmp(s2, s3, s2len - 1), -1, "\n"); - assert_false_return(strncmp(s1, s2, s2len), -1, "\n"); +int +test_strncmp(void) +{ + int status = 0; + int rc; /* - * Now test gnu-efi's version, but with a cast back to a sane type + * shim's strncmp */ -#define strncmpa(a, b, c) ((INTN)strncmpa(a, b, c)) - - assert_zero_return(strncmpa(s0, s0, s0len), -1, "\n"); - assert_zero_return(strncmpa(s0, s0, s0sz), -1, "\n"); - - assert_zero_return(strncmpa(s0, s1, s0len), -1, "\n"); - assert_negative_return(strncmpa(s0, s1, s0sz), -1, "\n"); - assert_equal_return(strncmpa(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmpa(s1, s0, s0sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); - - assert_positive_return(strncmpa(s1, s2, s1sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmpa(s1, s2, s1len), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); - assert_negative_return(strncmpa(s2, s1, s1sz), -1, "\n"); - assert_equal_return(strncmpa(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); - - assert_zero_return(strncmpa(s1, s2, s2len), -1, "\n"); - assert_positive_return(strncmpa(s1, s2, s2sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - - assert_negative_return(strncmpa(s2, s3, s2sz), -1, "\n"); - assert_equal_return(strncmpa(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - assert_equal_return(strncmpa(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - assert_negative_return(strncmpa(s2, s3, s2len), -1, "\n"); - assert_zero_return(strncmpa(s2, s3, s2len - 1), -1, "\n"); - assert_false_return(strncmpa(s1, s2, s2len), -1, "\n"); + rc = test_strncmp_helper(shim_strncmp, false, false); + status = MIN(rc, status); + /* + * libc's strncmp + */ + rc = test_strncmp_helper(strncmp, false, false); + status = MIN(rc, status); /* - * Once more, but with the casting /and the warnings/ turned off - * - * The ones marked with XXX I've inverted the test to make it work - * "correctly", because UINTN is what makes positive. + * gnu-efi's broken strncmpa */ -#undef strncmpa #pragma GCC diagnostic ignored "-Wtype-limits" #pragma GCC diagnostic ignored "-Wsign-compare" - - assert_zero_return(strncmpa(s0, s0, s0len), -1, "\n"); - assert_zero_return(strncmpa(s0, s0, s0sz), -1, "\n"); - - assert_zero_return(strncmpa(s0, s1, s0len), -1, "\n"); - /*XXX*/assert_positive_return(strncmpa(s0, s1, s0sz), -1, "\n");/*XXX*/ - assert_equal_return(strncmpa(s0, s1, s0sz), s0[s0len] - s1[s0len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmpa(s1, s0, s0sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s0, s0sz), s1[s0len] - s0[s0len] , -1, "expected %d got %d\n"); - - assert_positive_return(strncmpa(s1, s2, s1sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - assert_positive_return(strncmpa(s1, s2, s1len), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2len), s1[s2len-1] - s2[s2len-1] , -1, "expected %d got %d\n"); - /*XXX*/ assert_positive_return(strncmpa(s2, s1, s1sz), -1, "\n");/*XXX*/ - assert_equal_return(strncmpa(s2, s1, s1sz), s2[s2len] - s1[s2len] , -1, "expected %d got %d\n"); - - assert_zero_return(strncmpa(s1, s2, s2len), -1, "\n"); - assert_positive_return(strncmpa(s1, s2, s2sz), -1, "\n"); - assert_equal_return(strncmpa(s1, s2, s2sz), s1[s2len] - s2[s2len] , -1, "expected %d got %d\n"); - - assert_positive_return(strncmpa(s2, s3, s2sz), -1, "\n"); - assert_equal_return(strncmpa(s2, s3, s2sz), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - assert_equal_return(strncmpa(s2, s3, s2len), s2[s2len-1] - s3[s2len-1] , -1, "expected %d got %d\n"); - /*XXX*/assert_positive_return(strncmpa(s2, s3, s2len), -1,"\n");/*XXX*/ - assert_zero_return(strncmpa(s2, s3, s2len - 1), -1, "\n"); - assert_false_return(strncmpa(s1, s2, s2len), -1, "\n"); - + rc = test_strncmp_helper(gnuefi_strncmp, true, false); + status = MIN(rc, status); #pragma GCC diagnostic error "-Wsign-compare" #pragma GCC diagnostic error "-Wtype-limits" - return 0; + + /* + * gnu-efi's broken strncmpa with the return type fixed + */ + rc = test_strncmp_helper(gnuefi_signed_strncmp, false, true); + status = MIN(rc, status); + + /* + * gnu-efi's strncmpa with the return type fixed and unsigned + * comparisons internally + */ + rc = test_strncmp_helper(gnuefi_good_strncmp, false, false); + status = MIN(rc, status); + + return status; +} #undef s0 #undef s0sz @@ -194,7 +299,6 @@ test_strncmp(void) #undef s3 #undef s3sz #undef s3len -} int test_strntoken_null(void) { -- cgit v1.2.3 From c722a590d08506f29ddb70c1c57c511a836efb7a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 10 Mar 2021 14:26:46 -0500 Subject: Add more string test cases. This adds test cases for the rest of our ASCII string functions. While doing so, it fixes two minor bugs: - strcasecmp() now handles utf8 correctly - strncpy() no longer does the stpncpy() behavior of clearing leftover buffer Signed-off-by: Peter Jones --- include/test.h | 14 + lib/string.c | 5 +- test-str.c | 911 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 854 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index 92d1314e..012ffc51 100644 --- a/include/test.h +++ b/include/test.h @@ -213,6 +213,20 @@ extern int debug; } \ }) +#define assert_negative_goto(a, label, fmt, ...) \ + ({ \ + int rc_ = assert_negative_as_expr(a, -1, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + goto label; \ + }) + +#define assert_positive_goto(a, label, fmt, ...) \ + ({ \ + int rc_ = assert_positive_as_expr(a, -1, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + goto label; \ + }) + #define test(x, ...) \ ({ \ int rc; \ diff --git a/lib/string.c b/lib/string.c index 3dc6f1cd..37eabe8c 100644 --- a/lib/string.c +++ b/lib/string.c @@ -13,6 +13,7 @@ #define strcasecmp shim_strcasecmp #define strrchr shim_strrchr #define strlen shim_strlen +#define strnlen shim_strnlen #define strcpy shim_strcpy #define strncpy shim_strncpy #define strdup shim_strdup @@ -93,7 +94,7 @@ strncasecmp(const char *s1p, const char *s2p, size_t n) int strcasecmp(const char *str1, const char *str2) { - char c1, c2; + uint8_t c1, c2; c1 = toupper(*str1); c2 = toupper(*str2); @@ -155,7 +156,7 @@ strncpy(char *dest, const char *src, size_t n) for (i = 0; i < n && src[i] != '\0'; i++) dest[i] = src[i]; - for (; i < n; i++) + if (i < n) dest[i] = '\0'; return dest; diff --git a/test-str.c b/test-str.c index deb753d9..d0d6832b 100644 --- a/test-str.c +++ b/test-str.c @@ -2,6 +2,9 @@ /* * test-str.c - test our string functions. */ +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic error "-Wnonnull" +#pragma GCC diagnostic error "-Wunused-function" #ifndef SHIM_UNIT_TEST #define SHIM_UNIT_TEST @@ -10,43 +13,448 @@ #include -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic error "-Wnonnull" - -int -test_strchrnul_helper(__typeof__(strchrnul) fn) +static int +test_strlen(void) { - const char s0[] = "abcd\0fghi"; + const char s1[] = "abcd"; + const char s2[] = ""; - assert_equal_return(fn(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); - assert_equal_return(fn(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); - assert_equal_return(fn(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); - assert_equal_return(fn(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(shim_strlen(s1), 4, -1, "got %d expected %d\n"); + assert_equal_return(strlen(s1), 4, -1, "got %d expected %d\n"); + assert_equal_return(shim_strlen(s2), 0, -1, "got %d expected %d\n"); + assert_equal_return(strlen(s2), 0, -1, "got %d expected %d\n"); return 0; } -int -test_strchrnul(void) +static int +test_strnlen(void) { - const char s0[] = "abcd\0fghi"; + const char s1[] = "abcd"; + const char s2[] = ""; + + assert_equal_return(shim_strnlen(s1, 0), 0, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s1, 0), 0, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s1, 1), 1, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s1, 1), 1, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s1, 3), 3, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s1, 3), 3, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s1, 4), 4, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s1, 4), 4, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s1, 5), 4, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s1, 5), 4, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s2, 0), 0, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s2, 0), 0, -1, "got %d expected %d\n"); + assert_equal_return(shim_strnlen(s2, 1), 0, -1, "got %d expected %d\n"); + assert_equal_return(strnlen(s2, 1), 0, -1, "got %d expected %d\n"); - assert_equal_return(test_strchrnul_helper(shim_strchrnul), - test_strchrnul_helper(strchrnul), - -1, "got %d expected %d\n"); + return 0; +} - assert_equal_return(strnchrnul(s0, 0, 'b'), &s0[0], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, -1, 'b'), &s0[1], 1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 2, 'b'), &s0[1], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 4, 'f'), &s0[3], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 5, 'f'), &s0[4], -1, "got %p expected %p\n"); - assert_equal_return(strnchrnul(s0, 8, 'f'), &s0[4], -1, "got %p expected %p\n"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" - assert_equal_return(strnchrnul(&s0[4], 1, 'f'), &s0[4], -1, "got %p expected %p\n"); +/* + * these are constants so that the failures are readable if you get + * it wrong. + */ +#define s0 "sbat," +#define s0sz 6 +#define s0len 5 +#define s1 "sbat,1,2021030218" +#define s1sz 18 +#define s1len 17 +#define s2 "sbat,1,20210302" +#define s2sz 16 +#define s2len 15 +#define s3 "sbat,1,20210303" +#define s3sz 16 +#define s3len 15 +#define s4 "sbat\314\234\014," +#define s4sz 9 +#define s4len 8 +/* + * same as s4 but with a UTF8 encoding error; one bit is cleared. + */ +#define s5 "sbat\314\034\014," +#define s5sz 9 +#define s5len 8 - return 0; +#define test_strcmp_helper(fn, invert_sign_errors, invert_encoding_errors) \ + ({ \ + printf("testing %s\n", #fn); \ + int status_ = 0, rc_; \ + \ + rc_ = assert_zero_as_expr(fn(s0, s0), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s0, s1), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s0, s1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s0), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_positive_as_expr(fn(s1, s2), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s1), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s3, s2), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_encoding_errors) \ + rc_ = assert_negative_as_expr(fn(s4, s5), -1, "\n"); \ + else \ + rc_ = assert_positive_as_expr(fn(s4, s5), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (status_ < 0) \ + printf("%s failed\n", #fn); \ + status_; \ + }) + +static int +test_strcmp(void) +{ + int status = 0; + int rc; + + /* + * shim's strcmp + */ + rc = test_strcmp_helper(shim_strcmp, false, false); + status = MIN(rc, status); + + /* + * libc's strcmp + */ + rc = test_strcmp_helper(strcmp, false, false); + status = MIN(rc, status); + + return status; +} + +#undef s0 +#undef s0sz +#undef s0len +#undef s1 +#undef s1sz +#undef s1len +#undef s2 +#undef s2sz +#undef s2len +#undef s3 +#undef s3sz +#undef s3len +#undef s4 +#undef s4sz +#undef s4len +#undef s5 +#undef s5sz +#undef s5len + +/* + * these are constants so that the failures are readable if you get + * it wrong. + */ +#define s0 "sbAt," +#define s0sz 6 +#define s0len 5 +#define s1 "sbaT,1,2021030218" +#define s1sz 18 +#define s1len 17 +#define s2 "sbAt,1,20210302" +#define s2sz 16 +#define s2len 15 +#define s3 "sbaT,1,20210303" +#define s3sz 16 +#define s3len 15 +#define s4 "sbat\314\234\014," +#define s4sz 9 +#define s4len 8 +/* + * same as s4 but with a UTF8 encoding error; one bit is cleared. + */ +#define s5 "sbat\314\034\014," +#define s5sz 9 +#define s5len 8 + +#define test_strcasecmp_helper(fn, invert_sign_errors, invert_encoding_errors) \ + ({ \ + printf("testing %s\n", #fn); \ + int status_ = 0, rc_; \ + \ + rc_ = assert_zero_as_expr(fn(s0, s0), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s0, s1), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s0, s1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s0), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_positive_as_expr(fn(s1, s2), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s1), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3), -1, "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s3, s2), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_encoding_errors) \ + rc_ = assert_negative_as_expr(fn(s4, s5), -1, "\n"); \ + else \ + rc_ = assert_positive_as_expr(fn(s4, s5), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (status_ < 0) \ + printf("%s failed\n", #fn); \ + status_; \ + }) + +static int +test_strcasecmp(void) +{ + int status = 0; + int rc; + + /* + * shim's strcasecmp + */ + rc = test_strcasecmp_helper(shim_strcasecmp, false, false); + status = MIN(rc, status); + + /* + * libc's strcasecmp + */ + rc = test_strcasecmp_helper(strcasecmp, false, false); + status = MIN(rc, status); + + return status; +} + +#undef s0 +#undef s0sz +#undef s0len +#undef s1 +#undef s1sz +#undef s1len +#undef s2 +#undef s2sz +#undef s2len +#undef s3 +#undef s3sz +#undef s3len +#undef s4 +#undef s4sz +#undef s4len +#undef s5 +#undef s5sz +#undef s5len + +/* + * these are constants so that the failures are readable if you get + * it wrong. + */ +#define s0 "sbAt," +#define s0sz 6 +#define s0len 5 +#define s1 "sbaT,1,2021030218" +#define s1sz 18 +#define s1len 17 +#define s2 "sbAt,1,20210302" +#define s2sz 16 +#define s2len 15 +#define s3 "sbaT,1,20210303" +#define s3sz 16 +#define s3len 15 +#define s4 "sbat\314\234\014," +#define s4sz 9 +#define s4len 8 +/* + * same as s4 but with a UTF8 encoding error; one bit is cleared. + */ +#define s5 "sbat\314\034\014," +#define s5sz 9 +#define s5len 8 + +#define test_strncasecmp_helper(fn, test_cmp_magnitude, invert_sign_errors, \ + invert_encoding_errors) \ + ({ \ + printf("testing %s\n", #fn); \ + int status_ = 0, rc_; \ + \ + rc_ = assert_zero_as_expr(fn(s0, s0, s0len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_zero_as_expr(fn(s0, s0, s0sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + rc_ = assert_zero_as_expr(fn(s0, s1, s0len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s0, s1, s0sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s0, s1, s0sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s0, s1, s0sz), \ + s0[s0len] - s1[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + rc_ = assert_positive_as_expr(fn(s1, s0, s0sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s0, s0sz), \ + s1[s0len] - s0[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + \ + rc_ = assert_positive_as_expr(fn(s1, s2, s1sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + rc_ = assert_positive_as_expr(fn(s1, s2, s1len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr( \ + fn(s1, s2, s2len), \ + s1[s2len - 1] - s2[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s1, s1sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s1, s1sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s2, s1, s1sz), \ + s2[s2len] - s1[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + \ + rc_ = assert_zero_as_expr(fn(s1, s2, s2len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_positive_as_expr(fn(s1, s2, s2sz), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ + \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3, s2sz), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3, s2sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s2, s3, s2sz), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr(fn(s2, s3, s2len), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + if (invert_sign_errors) \ + rc_ = assert_positive_as_expr(fn(s2, s3, s2len), -1, \ + "\n"); \ + else \ + rc_ = assert_negative_as_expr(fn(s2, s3, s2len), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_zero_as_expr(fn(s2, s3, s2len - 1), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_false_as_expr(fn(s1, s2, s2len), -1, "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (invert_encoding_errors) \ + rc_ = assert_negative_as_expr(fn(s4, s5, s4sz), -1, \ + "\n"); \ + else \ + rc_ = assert_positive_as_expr(fn(s4, s5, s4sz), -1, \ + "\n"); \ + status_ = MIN(status_, rc_); \ + \ + if (status_ < 0) \ + printf("%s failed\n", #fn); \ + status_; \ + }) + +static int +test_strncasecmp(void) +{ + int status = 0; + int rc; + + /* + * shim's strncasecmp + */ + rc = test_strncasecmp_helper(shim_strncasecmp, true, false, false); + status = MIN(rc, status); + + /* + * libc's strncasecmp + */ + rc = test_strncasecmp_helper(strncasecmp, false, false, false); + status = MIN(rc, status); + + return status; } +#undef s0 +#undef s0sz +#undef s0len +#undef s1 +#undef s1sz +#undef s1len +#undef s2 +#undef s2sz +#undef s2len +#undef s3 +#undef s3sz +#undef s3len +#undef s4 +#undef s4sz +#undef s4len +#undef s5 +#undef s5sz +#undef s5len + /* * copy-pasta from gnu-efi */ @@ -113,7 +521,6 @@ gnuefi_good_strncmp ( return len ? *s1 - *s2 : 0; } - /* * these are constants so that the failures are readable if you get * it wrong. @@ -140,7 +547,8 @@ gnuefi_good_strncmp ( #define s5sz 9 #define s5len 8 -#define test_strncmp_helper(fn, invert_sign_errors, invert_encoding_errors) \ +#define test_strncmp_helper(fn, test_cmp_magnitude, invert_sign_errors, \ + invert_encoding_errors) \ ({ \ printf("testing %s\n", #fn); \ int status_ = 0, rc_; \ @@ -159,29 +567,38 @@ gnuefi_good_strncmp ( rc_ = assert_negative_as_expr(fn(s0, s1, s0sz), -1, \ "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s0, s1, s0sz), \ - s0[s0len] - s1[s0len], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s0, s1, s0sz), \ + s0[s0len] - s1[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ rc_ = assert_positive_as_expr(fn(s1, s0, s0sz), -1, "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s1, s0, s0sz), \ - s1[s0len] - s0[s0len], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s0, s0sz), \ + s1[s0len] - s0[s0len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ \ rc_ = assert_positive_as_expr(fn(s1, s2, s1sz), -1, "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ - s1[s2len] - s2[s2len], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ rc_ = assert_positive_as_expr(fn(s1, s2, s1len), -1, "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s1, s2, s2len), \ - s1[s2len - 1] - s2[s2len - 1], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr( \ + fn(s1, s2, s2len), \ + s1[s2len - 1] - s2[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ if (invert_sign_errors) \ rc_ = assert_positive_as_expr(fn(s2, s1, s1sz), -1, \ "\n"); \ @@ -189,19 +606,23 @@ gnuefi_good_strncmp ( rc_ = assert_negative_as_expr(fn(s2, s1, s1sz), -1, \ "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s2, s1, s1sz), \ - s2[s2len] - s1[s2len], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s2, s1, s1sz), \ + s2[s2len] - s1[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ \ rc_ = assert_zero_as_expr(fn(s1, s2, s2len), -1, "\n"); \ status_ = MIN(status_, rc_); \ rc_ = assert_positive_as_expr(fn(s1, s2, s2sz), -1, "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ - s1[s2len] - s2[s2len], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr(fn(s1, s2, s2sz), \ + s1[s2len] - s2[s2len], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ \ if (invert_sign_errors) \ rc_ = assert_positive_as_expr(fn(s2, s3, s2sz), -1, \ @@ -210,14 +631,18 @@ gnuefi_good_strncmp ( rc_ = assert_negative_as_expr(fn(s2, s3, s2sz), -1, \ "\n"); \ status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s2, s3, s2sz), \ - s2[s2len - 1] - s3[s2len - 1], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ - rc_ = assert_equal_as_expr(fn(s2, s3, s2len), \ - s2[s2len - 1] - s3[s2len - 1], -1, \ - "expected %d got %d\n"); \ - status_ = MIN(status_, rc_); \ + if (test_cmp_magnitude) { \ + rc_ = assert_equal_as_expr( \ + fn(s2, s3, s2sz), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + rc_ = assert_equal_as_expr( \ + fn(s2, s3, s2len), \ + s2[s2len - 1] - s3[s2len - 1], -1, \ + "expected %d got %d\n"); \ + status_ = MIN(status_, rc_); \ + } \ if (invert_sign_errors) \ rc_ = assert_positive_as_expr(fn(s2, s3, s2len), -1, \ "\n"); \ @@ -243,7 +668,7 @@ gnuefi_good_strncmp ( status_; \ }) -int +static int test_strncmp(void) { int status = 0; @@ -252,36 +677,47 @@ test_strncmp(void) /* * shim's strncmp */ - rc = test_strncmp_helper(shim_strncmp, false, false); + rc = test_strncmp_helper(shim_strncmp, true, false, false); status = MIN(rc, status); /* * libc's strncmp */ - rc = test_strncmp_helper(strncmp, false, false); + /* + * Deliberately not testing the difference between these two + * comparisons for the symbol named "strncmp": + * strncmp("b", "a", 1) + * strncmp("c", "a", 1) + * glibc, shim_strncmp(), and even the gnuefi ones will give you 1 + * and 2, respectively, as will glibc's, but valgrind swaps in its + * own implementation, in case you're doing something that's both + * clever and dumb with the result, and it'll return 1 for both of + * them. + */ + rc = test_strncmp_helper(strncmp, false, false, false); status = MIN(rc, status); /* * gnu-efi's broken strncmpa */ +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" #pragma GCC diagnostic ignored "-Wsign-compare" - rc = test_strncmp_helper(gnuefi_strncmp, true, false); + rc = test_strncmp_helper(gnuefi_strncmp, true, true, false); status = MIN(rc, status); -#pragma GCC diagnostic error "-Wsign-compare" -#pragma GCC diagnostic error "-Wtype-limits" +#pragma GCC diagnostic pop /* * gnu-efi's broken strncmpa with the return type fixed */ - rc = test_strncmp_helper(gnuefi_signed_strncmp, false, true); + rc = test_strncmp_helper(gnuefi_signed_strncmp, true, false, true); status = MIN(rc, status); /* * gnu-efi's strncmpa with the return type fixed and unsigned * comparisons internally */ - rc = test_strncmp_helper(gnuefi_good_strncmp, false, false); + rc = test_strncmp_helper(gnuefi_good_strncmp, true, false, false); status = MIN(rc, status); return status; @@ -299,8 +735,322 @@ test_strncmp(void) #undef s3 #undef s3sz #undef s3len +#undef s4 +#undef s4sz +#undef s4len +#undef s5 +#undef s5sz +#undef s5len -int +/* + * Put -Wshadow back how it was + */ +#pragma GCC diagnostic pop + +static int +test_strchr(void) +{ + char s0[] = "abcbdbeb\0fbgb"; + + assert_equal_return(strchr(s0, 'a'), s0, -1, "got %p expected %p\n"); + assert_equal_return(strchr(s0, 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(strchr(&s0[1], 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(strchr(&s0[2], 'b'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(strchr(&s0[4], 'b'), &s0[5], -1, "got %p expected %p\n"); + assert_equal_return(strchr(&s0[6], 'b'), &s0[7], -1, "got %p expected %p\n"); + assert_equal_return(strchr(&s0[8], 'b'), NULL, -1, "got %p expected %p\n"); + + assert_equal_return(shim_strchr(s0, 'a'), s0, -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(s0, 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(&s0[1], 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(&s0[2], 'b'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(&s0[4], 'b'), &s0[5], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(&s0[6], 'b'), &s0[7], -1, "got %p expected %p\n"); + assert_equal_return(shim_strchr(&s0[8], 'b'), NULL, -1, "got %p expected %p\n"); + return 0; +} + +static int +test_stpcpy(void) +{ + char s0[] = "0123456789abcdef"; + char s1[] = "xxxxxxxxxxxxxxxx"; + char *s; + + s0[0xa] = 0; + assert_equal_return(stpcpy(s1, s0), &s1[0xa], -1, "got %p expected %p\n"); + assert_zero_return(memcmp(s0, s1, 11), -1, "\n"); + assert_zero_return(memcmp(&s1[11], "xxxxx", sizeof("xxxxx")), -1, "\n"); + + memset(s1, 'x', sizeof(s1)); + s1[16] = 0; + assert_equal_return(shim_stpcpy(s1, s0), &s1[0xa], -1, "got %p expected %p\n"); + assert_zero_return(memcmp(s0, s1, 11), -1, "\n"); + assert_zero_return(memcmp(&s1[11], "xxxxx", sizeof("xxxxx")), -1, "\n"); + + return 0; +} + +static int +test_strdup(void) +{ + char s0[] = "0123456789abcdef"; + char *s = NULL; + + s = strdup(s0); + assert_equal_goto(strcmp(s0, s), 0, err, "\n"); + free(s); + + s = shim_strdup(s0); + assert_equal_goto(strcmp(s0, s), 0, err, "\n"); + free(s); + + return 0; +err: + if (s) + free(s); + return -1; +} + +static int +test_strndup(void) +{ + char s0[] = "0123456789abcdef"; + char *s = NULL; + + s = strndup(s0, 18); + assert_equal_goto(strcmp(s0, s), 0, err, "\n"); + free(s); + s = strndup(s0, 15); + assert_positive_goto(strcmp(s0, s), err, "\n"); + free(s); + + s = shim_strndup(s0, 18); + assert_equal_goto(strcmp(s0, s), 0, err, "\n"); + free(s); + s = strndup(s0, 15); + assert_positive_goto(shim_strcmp(s0, s), err, "\n"); + free(s); + + return 0; +err: + if (s) + free(s); + return -1; +} + +static int +test_strchrnul_helper(__typeof__(strchrnul) fn) +{ + const char s0[] = "abcd\0fghi"; + + assert_equal_return(fn(s0, 'a'), &s0[0], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, 'd'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, '\000'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(fn(s0, 'i'), &s0[4], -1, "got %p expected %p\n"); + + return 0; +} + +static int +test_strchrnul(void) +{ + const char s0[] = "abcd\0fghi"; + + assert_equal_return(test_strchrnul_helper(shim_strchrnul), + test_strchrnul_helper(strchrnul), + -1, "got %d expected %d\n"); + + assert_equal_return(strnchrnul(s0, 0, 'b'), &s0[0], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, -1, 'b'), &s0[1], 1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 2, 'b'), &s0[1], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 4, 'f'), &s0[3], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 5, 'f'), &s0[4], -1, "got %p expected %p\n"); + assert_equal_return(strnchrnul(s0, 8, 'f'), &s0[4], -1, "got %p expected %p\n"); + + assert_equal_return(strnchrnul(&s0[4], 1, 'f'), &s0[4], -1, "got %p expected %p\n"); + + return 0; +} + +static int +test_strrchr(void) { + char s0[] = "abcbebfb"; + + assert_equal_return(shim_strrchr(s0, '\n'), NULL, -1, "got %p expected %p\n"); + assert_equal_return(strrchr(s0, '\n'), NULL, -1, "got %p expected %p\n"); + assert_equal_return(shim_strrchr(s0, 'b'), &s0[7], -1, "got %p expected %p\n"); + assert_equal_return(strrchr(s0, 'b'), &s0[7], -1, "got %p expected %p\n"); + assert_equal_return(shim_strrchr(s0, 'c'), &s0[2], -1, "got %p expected %p\n"); + assert_equal_return(strrchr(s0, 'c'), &s0[2], -1, "got %p expected %p\n"); + + return 0; +} + +static int +test_strcpy(void) +{ + char s0[] = "0123456789abcdef\0000"; + char s1[sizeof(s0)]; + + memset(s1, 0, sizeof(s1)); + assert_equal_return(strcpy(s1, s0), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s0), strlen(s1), -1, "got %d expected %d\n"); + + memset(s1, 0, sizeof(s1)); + assert_equal_return(shim_strcpy(s1, s0), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s0), strlen(s1), -1, "got %d expected %d\n"); + + memset(s1, 0, sizeof(s1)); + assert_equal_return(shim_strcpy(s1, s0), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s0), strlen(s1), -1, "got %d expected %d\n"); + + return 0; +} + +static int +test_strncpy(void) +{ + char s[] = "0123456789abcdef\0000"; + char s0[4096+4096]; + char *s1 = &s0[4096]; + + memset(s0, 0, sizeof(s0)); + memcpy(s0, s, sizeof(s)); + + memset(s1, 0, 4096); + assert_equal_return(strncpy(s1, s0, 0), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s1), 0, -1, "got %d expected %d\n"); + memset(s1, 0, 4096); + assert_equal_return(shim_strncpy(s1, s0, 0), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s1), 0, -1, "got %d expected %d\n"); + + memset(s1, 0, 4096); + assert_equal_return(strncpy(s1, s0, 1), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s1), 1, -1, "got %d expected %d\n"); + assert_equal_return(s1[0], s0[0], -1, "got %#02hhx, expected %#02hhx\n"); + assert_equal_return(s0[1], '1', -1, "got %#02hhx, expected %#02hhx\n"); + assert_equal_return(s1[1], '\0', -1, "got %#02hhx, expected %#02hhx\n"); + memset(s1, 0, 4096); + assert_equal_return(shim_strncpy(s1, s0, 1), s1, -1, "got %p expected %p\n"); + assert_equal_return(strlen(s1), 1, -1, "got %d expected %d\n"); + assert_equal_return(s1[0], s0[0], -1, "got %#02hhx, expected %#02hhx\n"); + assert_equal_return(s0[1], '1', -1, "got %#02hhx, expected %#02hhx\n"); + assert_equal_return(s1[1], '\0', -1, "got %#02hhx, expected %#02hhx\n"); + + memset(s1, 0, 4096); + assert_equal_return(strncpy(s1, s0, 15), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + memset(s1, 0, 4096); + assert_equal_return(shim_strncpy(s1, s0, 15), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + + memset(s1, 0, 4096); + assert_equal_return(strncpy(s1, s0, 16), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + memset(s1, 0, 4096); + assert_equal_return(shim_strncpy(s1, s0, 16), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + + memset(s1, 0, 4096); + s1[17] = '0'; + s1[18] = '1'; + assert_equal_return(strncpy(s1, s0, 4096), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + memset(s1, 0, 4096); + s1[17] = '0'; + s1[18] = '1'; + assert_equal_return(shim_strncpy(s1, s0, 4096), s1, -1, "got %p expected %p\n"); + assert_equal_return(s0[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s0[18], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[14], 'e', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[15], 'f', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[17], '0', -1, "got %#02hhx expected %02hhx\n"); + assert_equal_return(s1[18], '1', -1, "got %#02hhx expected %02hhx\n"); + + return 0; +} + +static int +test_strcat(void) +{ + char s[] = "0123456789abcdef\0000"; + char s0[8192]; + char *s1 = &s0[4096]; + char *s2; + char s3[] = "0123456789abcdef0123456789abcdef\000\000\000\000\000"; + + memset(s0, 0, 8192); + memcpy(s0, s, sizeof(s)); + + memset(s1, 0, 4096); + assert_equal_return(strcat(s1, s0), s1, -1, "got %p expected %p\n"); + assert_zero_return(strncmp(s1, s0, sizeof(s)-1), 0, -1, "\n"); + assert_negative_return(memcmp(s1, s0, sizeof(s)), 0, -1, "\n"); + + memset(s1, 0, 4096); + assert_equal_return(strcat(s1, s0), s1, -1, "got %p expected %p\n"); + s2 = s1 + strlen(s1); + assert_equal_return(s2, &s1[16], -1, "got %p expected %p\n"); + assert_equal_return(strcat(s2, s0), s2, -1, "got %p expected %p\n"); + assert_zero_return(strncmp(s1, s0, strlen(s)), -1, "got %p expected %p\n"); + assert_zero_return(strncmp(s2, s0, 2*(sizeof(s)-1)), -1, "\n"); + assert_positive_return(memcmp(s1, s0, 2*sizeof(s)-2), -1, "\n"); + assert_equal_return(memcmp(s1, s3, sizeof(s3)), 0, -1, "expected %d got %d\n"); + + return 0; +} + +static int test_strntoken_null(void) { bool ret; char *token = NULL; @@ -314,7 +1064,7 @@ test_strntoken_null(void) { return 0; } -int +static int test_strntoken_size_0(void) { const char s1[] = "abc,def,.,gh,"; @@ -334,7 +1084,7 @@ test_strntoken_size_0(void) return 0; } -int +static int test_strntoken_empty_size_1(void) { char s1[] = ""; @@ -367,7 +1117,7 @@ test_strntoken_empty_size_1(void) return 0; } -int +static int test_strntoken_size_1(void) { char s1[] = ","; @@ -401,7 +1151,7 @@ test_strntoken_size_1(void) return 0; } -int +static int test_strntoken_size_2(void) { char s1[] = ","; @@ -444,7 +1194,7 @@ test_strntoken_size_2(void) return 0; } -int +static int test_strntoken_no_ascii_nul(void) { const char s1[] = "abc,def,.,gh,"; @@ -557,7 +1307,7 @@ test_strntoken_no_ascii_nul(void) return 0; } -int +static int test_strntoken_with_ascii_nul(void) { const char s1[] = "abc,def,.,gh,"; @@ -680,8 +1430,21 @@ int main(void) { int status = 0; - test(test_strchrnul); + test(test_strlen); + test(test_strnlen); + test(test_strcmp); test(test_strncmp); + test(test_strcasecmp); + test(test_strncasecmp); + test(test_strrchr); + test(test_strcpy); + test(test_strncpy); + test(test_strcat); + test(test_stpcpy); + test(test_strdup); + test(test_strndup); + test(test_strchr); + test(test_strchrnul); test(test_strntoken_null); test(test_strntoken_size_0); test(test_strntoken_empty_size_1); -- cgit v1.2.3 From 4033d1fd0f25196f9a35e944679676277cd51960 Mon Sep 17 00:00:00 2001 From: Alex Burmashev Date: Wed, 10 Mar 2021 08:49:54 -0500 Subject: Fix compilation for older gcc Signed-off-by: Alex Burmashev --- Make.defaults | 2 +- include/compiler.h | 4 ++++ include/str.h | 2 ++ include/system/string.h | 6 +++++- shim.c | 2 +- 5 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Make.defaults b/Make.defaults index 20aa8cd4..95c24914 100644 --- a/Make.defaults +++ b/Make.defaults @@ -107,7 +107,7 @@ override DEFAULT_FEATUREFLAGS = \ -std=gnu11 \ -ggdb \ -ffreestanding \ - -fmacro-prefix-map='$(TOPDIR)/=$(DEBUGSRC)' \ + $(shell $(CC) -fmacro-prefix-map=./=./ -E -x c /dev/null >/dev/null 2>&1 && echo -fmacro-prefix-map='$(TOPDIR)/=$(DEBUGSRC)') \ -fno-stack-protector \ -fno-strict-aliasing \ -fpic \ diff --git a/include/compiler.h b/include/compiler.h index 40358a11..18576724 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -18,7 +18,11 @@ # define RETURNS_NONNULL #else # define NONNULL(first, args...) __attribute__((__nonnull__(first, ## args))) +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) # define RETURNS_NONNULL __attribute__((__returns_nonnull__)) +#else +# define RETURNS_NONNULL +#endif #endif #ifndef UNUSED diff --git a/include/str.h b/include/str.h index 189aceff..d433e6ec 100644 --- a/include/str.h +++ b/include/str.h @@ -3,11 +3,13 @@ #ifndef SHIM_STR_H #define SHIM_STR_H +#if __GNUC__ > 6 #ifdef SHIM_UNIT_TEST #pragma GCC diagnostic error "-Wnonnull-compare" #else #pragma GCC diagnostic ignored "-Wnonnull-compare" #endif +#endif static inline UNUSED RETURNS_NONNULL NONNULL(1) char * diff --git a/include/system/string.h b/include/system/string.h index 66e7d93e..2b366df7 100644 --- a/include/system/string.h +++ b/include/system/string.h @@ -64,7 +64,11 @@ mkbi3_(int, strncasecmp, const char *, s1, const char *, s2, size_t, n) mkbi3_(char *, strncat, char *, dest, const char *, src, size_t, n) mkbi3_(int, strncmp, const char *, s1, const char *, s2, size_t, n) mkbi3_(char *, strncpy, char *, dest, const char *, src, size_t, n) -mkbi2_(int, strnlen, const char *, s1, size_t, n) +#if defined(__GNUC__) && __GNUC__ >= 9 +mkbi2_(size_t, strnlen, const char *, s1, size_t, n) +#else +size_t strnlen(const char * s1, size_t n); +#endif mkdepbi2_(char *, strpbrk, const char *, s, const char *, accept) mkdepbi2_(char *, strrchr, const char *, s, int, c) mkdepbi2_(size_t, strspn, const char *, s, const char *, accept) diff --git a/shim.c b/shim.c index 6f627b1f..fd4c0322 100644 --- a/shim.c +++ b/shim.c @@ -1095,7 +1095,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) void *sourcebuffer = NULL; UINT64 sourcesize = 0; void *data = NULL; - int datasize; + int datasize = 0; /* * We need to refer to the loaded image protocol on the running -- cgit v1.2.3 From cf5efd5a982e597c9e767de1cf51f2ef1512c02e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Thu, 25 Feb 2021 14:30:43 -0800 Subject: If the SBAT UEFI variable is not set, initialize it as a bootservices variable. --- include/sbat.h | 11 ++++++++ sbat.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shim.c | 48 ++++++++++++++++++++++------------ 3 files changed, 123 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 5b64f20a..95fa6a56 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -6,6 +6,16 @@ #ifndef SBAT_H_ #define SBAT_H_ +#define SBAT_VAR_SIG "sbat," +#define SBAT_VAR_VERSION "1," +#define SBAT_VAR_DATE "2021030218" +#define SBAT_VAR SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_DATE "\n" + +#define UEFI_VAR_NV_BS \ + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) +#define UEFI_VAR_NV_BS_TIMEAUTH \ + (UEFI_VAR_NV_BS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) + extern UINTN _sbat, _esbat; struct sbat_var_entry { @@ -23,6 +33,7 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); +EFI_STATUS set_sbat_uefi_variable(void); struct sbat_section_entry { const CHAR8 *component_name; diff --git a/sbat.c b/sbat.c index fec22a73..3f67136b 100644 --- a/sbat.c +++ b/sbat.c @@ -293,4 +293,85 @@ parse_sbat_var(list_t *entries) return parse_sbat_var_data(entries, data, datasize+1); } +EFI_STATUS +set_sbat_uefi_variable(void) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + UINT32 attributes = 0; + + UINT8 *sbat = NULL; + UINTN sbatsize = 0; + + efi_status = get_variable_size(L"SBAT", SHIM_LOCK_GUID, &sbatsize); + if (EFI_ERROR(efi_status)) + dprint(L"SBAT size probe failed %r\n", efi_status); + + if (sbatsize != 0) { + sbat = AllocateZeroPool(sbatsize + 1); + if (!sbat) + return EFI_OUT_OF_RESOURCES; + + efi_status = get_variable_attr(L"SBAT", &sbat, &sbatsize, + SHIM_LOCK_GUID, &attributes); + } + + /* + * Always set the SBAT UEFI variable if it fails to read. + * + * Don't try to set the SBAT UEFI variable if attributes match, the + * signature matches and it has the same or newer version. + */ + if (EFI_ERROR(efi_status)) { + dprint(L"SBAT read failed %r\n", efi_status); + } else if ((attributes == UEFI_VAR_NV_BS || + attributes == UEFI_VAR_NV_BS_TIMEAUTH) && + strncmp(sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG)) == 0) { + FreePool(sbat); + return EFI_SUCCESS; + } + + /* delete previous variable */ + efi_status = set_variable(L"SBAT", SHIM_LOCK_GUID, attributes, 0, ""); + if (EFI_ERROR(efi_status)) + dprint(L"SBAT variable delete failed %r\n", efi_status); + + /* verify that it's gone */ + efi_status = get_variable(L"SBAT", &sbat, &sbatsize, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status) || (sbatsize != 0)) + dprint(L"SBAT variable clearing failed %r\n", efi_status); + + /* set variable */ + efi_status = set_variable(L"SBAT", SHIM_LOCK_GUID, + UEFI_VAR_NV_BS, sizeof (SBAT_VAR), SBAT_VAR); + if (EFI_ERROR(efi_status)) + dprint(L"SBAT variable writing failed %r\n", efi_status); + + FreePool(sbat); + + /* verify that the expected data is there */ + efi_status = get_variable_size(L"SBAT", SHIM_LOCK_GUID, &sbatsize); + if (EFI_ERROR(efi_status) || sbatsize == 0) { + dprint(L"SBAT read failed %r\n", efi_status); + return EFI_INVALID_PARAMETER; + } + + sbat = AllocateZeroPool(sbatsize); + if (!sbat) + return EFI_OUT_OF_RESOURCES; + + efi_status = get_variable(L"SBAT", &sbat, &sbatsize, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) + dprint(L"SBAT read failed %r\n", efi_status); + + if (strncmp(sbat, SBAT_VAR, strlen(SBAT_VAR)) != 0) { + efi_status = EFI_INVALID_PARAMETER; + } else { + dprint(L"SBAT variable initialization succeeded\n"); + efi_status = EFI_SUCCESS; + } + + FreePool(sbat); + return efi_status; +} + // vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 5975feb8..ad01a07f 100644 --- a/shim.c +++ b/shim.c @@ -1763,7 +1763,8 @@ shim_init(void) void shim_fini(void) { - cleanup_sbat_var(&sbat_var); + if (secure_mode()) + cleanup_sbat_var(&sbat_var); /* * Remove our protocols @@ -1869,6 +1870,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) L"shim_init() failed", L"import of SBAT data failed", L"SBAT self-check failed", + L"SBAT UEFI variable setting failed", NULL }; enum { @@ -1876,6 +1878,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) SHIM_INIT, IMPORT_SBAT, SBAT_SELF_CHECK, + SET_SBAT, } msg = IMPORT_MOK_STATE; /* @@ -1905,25 +1908,28 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); - INIT_LIST_HEAD(&sbat_var); - efi_status = parse_sbat_var(&sbat_var); - /* - * Until a SBAT variable is installed into the systems, it is expected that - * attempting to parse the variable will fail with an EFI_NOT_FOUND error. - * - * Do not consider that error fatal for now. - */ - if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) { - perror(L"Parsing SBAT variable failed: %r\n", - efi_status); - msg = IMPORT_SBAT; - goto die; - } - - if (secure_mode ()) { + if (secure_mode()) { char *sbat_start = (char *)&_sbat; char *sbat_end = (char *)&_esbat; + INIT_LIST_HEAD(&sbat_var); + efi_status = parse_sbat_var(&sbat_var); + if (EFI_ERROR(efi_status)) { + efi_status = set_sbat_uefi_variable(); + if (efi_status == EFI_INVALID_PARAMETER) { + perror(L"SBAT variable initialization failed\n"); + msg = SET_SBAT; + goto die; + } + efi_status = parse_sbat_var(&sbat_var); + if (EFI_ERROR(efi_status)) { + perror(L"Parsing SBAT variable failed: %r\n", + efi_status); + msg = IMPORT_SBAT; + goto die; + } + } + efi_status = handle_sbat(sbat_start, sbat_end - sbat_start); if (EFI_ERROR(efi_status)) { perror(L"Verifiying shim SBAT data failed: %r\n", @@ -1933,6 +1939,14 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) } } + efi_status = set_sbat_uefi_variable(); + if (efi_status == EFI_INVALID_PARAMETER) { + perror(L"SBAT variable initialization failed\n"); + msg = SET_SBAT; + if (secure_mode()) + goto die; + } + init_openssl(); /* -- cgit v1.2.3 From 6ebae16cbb6856f80e891b710d4f76b49ff48c6d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Mar 2021 11:40:24 -0500 Subject: Fix the compiler when invoking scan-build/fanalyzer/etc Signed-off-by: Peter Jones --- include/coverity.mk | 6 ++---- include/fanalyzer.mk | 8 ++------ include/scan-build.mk | 8 +++----- 3 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/coverity.mk b/include/coverity.mk index e1e5c874..93d83853 100644 --- a/include/coverity.mk +++ b/include/coverity.mk @@ -9,10 +9,7 @@ define prop $(if $(findstring undefined,$(origin $(1))),,$(1)="$($1)") endef -override CCACHE_DISABLE := 1 -export CCACHE_DISABLE - -PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR CC COMPILER CROSS_COMPILE MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) @@ -42,6 +39,7 @@ cov-build-unchecked-cryptlib : Cryptlib/libcryptlib.a cov-build-unchecked-openssl : | clean-openssl-objs cov-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a +cov-build-all : CCACHE_DISABLE=1 cov-build-all : | clean clean-shim-objs clean-cryptlib-objs clean-openssl-objs +cov-build --dir cov-int $(MAKE) $(MAKEARGS) CCACHE_DISABLE=1 all diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk index 7e31a082..e0bf4d75 100644 --- a/include/fanalyzer.mk +++ b/include/fanalyzer.mk @@ -6,12 +6,7 @@ define prop $(if $(findstring undefined,$(origin $(1))),,$(eval export $(1))) endef -override CCACHE_DISABLE := 1 -export CCACHE_DISABLE -override COMPILER := gcc -export COMPILER - -PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE DASHJ +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR CC COMPILER CROSS_COMPILE DASHJ MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) @@ -22,6 +17,7 @@ fanalyzer-build-unchecked-cryptlib : Cryptlib/libcryptlib.a fanalyzer-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a +fanalyzer-build-all : COMPILER=gcc fanalyzer-build-all : CCACHE_DISABLE=1 fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference diff --git a/include/scan-build.mk b/include/scan-build.mk index 2cb33e79..3ed7660e 100644 --- a/include/scan-build.mk +++ b/include/scan-build.mk @@ -6,11 +6,7 @@ define prop $(if $(findstring undefined,$(origin $(1))),,$(1)="$($1)") endef -override CCACHE_DISABLE := 1 -export CCACHE_DISABLE -override COMPILER = clang - -PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR COMPILER CROSS_COMPILE DASHJ +PROPOGATE_MAKE_FLAGS = ARCH ARCH_SUFFIX COLOR CC COMPILER CROSS_COMPILE DASHJ MAKEARGS = $(foreach x,$(PROPOGATE_MAKE_FLAGS),$(call prop,$(x))) @@ -24,6 +20,8 @@ scan-build-unchecked-cryptlib : Cryptlib/libcryptlib.a scan-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a +scan-build-all : CCACHE_DISABLE=1 +scan-build-all : COMPILER=clang scan-build-all : | scan-test scan-build-all : +scan-build -o scan-results make $(MAKEARGS) $(DASHJ) CCACHE_DISABLE=1 all -- cgit v1.2.3 From 4457d79ce0ea638e7732f5529bf13849e290940d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Mar 2021 16:48:44 -0500 Subject: More va_* work Be much more explicit about exactly which va_* stuff comes from which ABI in both shim and gnu-efi. This fixes the problem where we see: | (null):0:(null)() v->name:"(null)" v->rtname:"(null)" | (null):0:(null)() v->data_size:0 v->data:0x0 and similar messages where everything is NULL. Signed-off-by: Peter Jones --- Cryptlib/Include/OpenSslSupport.h | 2 +- Cryptlib/Include/openssl/bio.h | 4 +- Cryptlib/Include/openssl/err.h | 2 +- Cryptlib/OpenSSL/crypto/bio/b_print.c | 66 ++++++++++++++--------------- Cryptlib/OpenSSL/crypto/err/err.c | 10 ++--- Makefile | 1 + errlog.c | 22 +++++----- gnu-efi | 2 +- include/console.h | 2 +- include/hexdump.h | 8 ++-- include/system/efistdarg.h | 4 ++ include/system/stdarg.h | 80 ++++++++++++++++++++++++++--------- lib/console.c | 12 +++--- shim.h | 3 +- 14 files changed, 132 insertions(+), 86 deletions(-) (limited to 'include') diff --git a/Cryptlib/Include/OpenSslSupport.h b/Cryptlib/Include/OpenSslSupport.h index 1f475a32..b97149e2 100644 --- a/Cryptlib/Include/OpenSslSupport.h +++ b/Cryptlib/Include/OpenSslSupport.h @@ -227,7 +227,7 @@ size_t fwrite (const void *, size_t, size_t, FILE *); char *fgets (char *, int, FILE *); int fputs (const char *, FILE *); int fprintf (FILE *, const char *, ...); -int vfprintf (FILE *, const char *, va_list); +int vfprintf (FILE *, const char *, ms_va_list); int fflush (FILE *); int fclose (FILE *); DIR *opendir (const char *); diff --git a/Cryptlib/Include/openssl/bio.h b/Cryptlib/Include/openssl/bio.h index 2efa873d..da8c6580 100644 --- a/Cryptlib/Include/openssl/bio.h +++ b/Cryptlib/Include/openssl/bio.h @@ -794,11 +794,11 @@ void BIO_copy_next_retry(BIO *b); # endif int EFIAPI BIO_printf(BIO *bio, const char *format, ...) __bio_h__attr__((__format__(__printf__, 2, 3))); -int EFIAPI BIO_vprintf(BIO *bio, const char *format, va_list args) +int EFIAPI BIO_vprintf(BIO *bio, const char *format, ms_va_list args) __bio_h__attr__((__format__(__printf__, 2, 0))); int EFIAPI BIO_snprintf(char *buf, size_t n, const char *format, ...) __bio_h__attr__((__format__(__printf__, 3, 4))); -int EFIAPI BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) +int EFIAPI BIO_vsnprintf(char *buf, size_t n, const char *format, ms_va_list args) __bio_h__attr__((__format__(__printf__, 3, 0))); # undef __bio_h__attr__ diff --git a/Cryptlib/Include/openssl/err.h b/Cryptlib/Include/openssl/err.h index 32da8c37..5a019808 100644 --- a/Cryptlib/Include/openssl/err.h +++ b/Cryptlib/Include/openssl/err.h @@ -345,7 +345,7 @@ void ERR_print_errors_fp(FILE *fp); void ERR_print_errors(BIO *bp); # endif void EFIAPI ERR_add_error_data(int num, ...); -void EFIAPI ERR_add_error_vdata(int num, va_list args); +void EFIAPI ERR_add_error_vdata(int num, ms_va_list args); void ERR_load_strings(int lib, ERR_STRING_DATA str[]); void ERR_unload_strings(int lib, ERR_STRING_DATA str[]); void ERR_load_ERR_strings(void); diff --git a/Cryptlib/OpenSSL/crypto/bio/b_print.c b/Cryptlib/OpenSSL/crypto/bio/b_print.c index 34c8fca7..29da9036 100644 --- a/Cryptlib/OpenSSL/crypto/bio/b_print.c +++ b/Cryptlib/OpenSSL/crypto/bio/b_print.c @@ -136,7 +136,7 @@ static int fmtfp(char **, char **, size_t *, size_t *, static int doapr_outch(char **, char **, size_t *, size_t *, int); static int EFIAPI _dopr(char **sbuffer, char **buffer, size_t *maxlen, size_t *retlen, int *truncated, - const char *format, va_list args); + const char *format, ms_va_list args); /* format read states */ #define DP_S_DEFAULT 0 @@ -171,7 +171,7 @@ static int EFIAPI _dopr(char **sbuffer, char **buffer, size_t *maxlen, - size_t *retlen, int *truncated, const char *format, va_list args) + size_t *retlen, int *truncated, const char *format, ms_va_list args) { char ch; LLONG value; @@ -236,7 +236,7 @@ _dopr(char **sbuffer, min = 10 * min + char_to_int(ch); ch = *format++; } else if (ch == '*') { - min = va_arg(args, int); + min = ms_va_arg(args, int); ch = *format++; state = DP_S_DOT; } else @@ -256,7 +256,7 @@ _dopr(char **sbuffer, max = 10 * max + char_to_int(ch); ch = *format++; } else if (ch == '*') { - max = va_arg(args, int); + max = ms_va_arg(args, int); ch = *format++; state = DP_S_MOD; } else @@ -297,16 +297,16 @@ _dopr(char **sbuffer, case 'i': switch (cflags) { case DP_C_SHORT: - value = (short int)va_arg(args, int); + value = (short int)ms_va_arg(args, int); break; case DP_C_LONG: - value = va_arg(args, long int); + value = ms_va_arg(args, long int); break; case DP_C_LLONG: - value = va_arg(args, LLONG); + value = ms_va_arg(args, LLONG); break; default: - value = va_arg(args, int); + value = ms_va_arg(args, int); break; } if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, 10, min, @@ -322,16 +322,16 @@ _dopr(char **sbuffer, flags |= DP_F_UNSIGNED; switch (cflags) { case DP_C_SHORT: - value = (unsigned short int)va_arg(args, unsigned int); + value = (unsigned short int)ms_va_arg(args, unsigned int); break; case DP_C_LONG: - value = (LLONG) va_arg(args, unsigned long int); + value = (LLONG) ms_va_arg(args, unsigned long int); break; case DP_C_LLONG: - value = va_arg(args, unsigned LLONG); + value = ms_va_arg(args, unsigned LLONG); break; default: - value = (LLONG) va_arg(args, unsigned int); + value = (LLONG) ms_va_arg(args, unsigned int); break; } if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, @@ -342,9 +342,9 @@ _dopr(char **sbuffer, #ifndef OPENSSL_SYS_UEFI case 'f': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); + fvalue = ms_va_arg(args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = ms_va_arg(args, double); if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max, flags)) return 0; @@ -353,26 +353,26 @@ _dopr(char **sbuffer, flags |= DP_F_UP; case 'e': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); + fvalue = ms_va_arg(args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = ms_va_arg(args, double); break; case 'G': flags |= DP_F_UP; case 'g': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); + fvalue = ms_va_arg(args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = ms_va_arg(args, double); break; #endif case 'c': if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, - va_arg(args, int))) + ms_va_arg(args, int))) return 0; break; case 's': - strvalue = va_arg(args, char *); + strvalue = ms_va_arg(args, char *); if (max < 0) { if (buffer) max = INT_MAX; @@ -384,7 +384,7 @@ _dopr(char **sbuffer, return 0; break; case 'p': - value = (long)va_arg(args, void *); + value = (long)ms_va_arg(args, void *); if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, 16, min, max, flags | DP_F_NUM)) return 0; @@ -392,19 +392,19 @@ _dopr(char **sbuffer, case 'n': /* XXX */ if (cflags == DP_C_SHORT) { short int *num; - num = va_arg(args, short int *); + num = ms_va_arg(args, short int *); *num = currlen; } else if (cflags == DP_C_LONG) { /* XXX */ long int *num; - num = va_arg(args, long int *); + num = ms_va_arg(args, long int *); *num = (long int)currlen; } else if (cflags == DP_C_LLONG) { /* XXX */ LLONG *num; - num = va_arg(args, LLONG *); + num = ms_va_arg(args, LLONG *); *num = (LLONG) currlen; } else { int *num; - num = va_arg(args, int *); + num = ms_va_arg(args, int *); *num = currlen; } break; @@ -799,18 +799,18 @@ doapr_outch(char **sbuffer, int EFIAPI BIO_printf(BIO *bio, const char *format, ...) { - va_list args; + ms_va_list args; int ret; - va_start(args, format); + ms_va_start(args, format); ret = BIO_vprintf(bio, format, args); - va_end(args); + ms_va_end(args); return (ret); } -int EFIAPI BIO_vprintf(BIO *bio, const char *format, va_list args) +int EFIAPI BIO_vprintf(BIO *bio, const char *format, ms_va_list args) { int ret; size_t retlen; @@ -847,18 +847,18 @@ int EFIAPI BIO_vprintf(BIO *bio, const char *format, va_list args) */ int EFIAPI BIO_snprintf(char *buf, size_t n, const char *format, ...) { - va_list args; + ms_va_list args; int ret; - va_start(args, format); + ms_va_start(args, format); ret = BIO_vsnprintf(buf, n, format, args); - va_end(args); + ms_va_end(args); return (ret); } -int EFIAPI BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) +int EFIAPI BIO_vsnprintf(char *buf, size_t n, const char *format, ms_va_list args) { size_t retlen; int truncated; diff --git a/Cryptlib/OpenSSL/crypto/err/err.c b/Cryptlib/OpenSSL/crypto/err/err.c index d0752adf..e2251454 100644 --- a/Cryptlib/OpenSSL/crypto/err/err.c +++ b/Cryptlib/OpenSSL/crypto/err/err.c @@ -1077,13 +1077,13 @@ void ERR_set_error_data(char *data, int flags) void EFIAPI ERR_add_error_data(int num, ...) { - va_list args; - va_start(args, num); + ms_va_list args; + ms_va_start(args, num); ERR_add_error_vdata(num, args); - va_end(args); + ms_va_end(args); } -void EFIAPI ERR_add_error_vdata(int num, va_list args) +void EFIAPI ERR_add_error_vdata(int num, ms_va_list args) { int i, n, s; char *str, *p, *a; @@ -1096,7 +1096,7 @@ void EFIAPI ERR_add_error_vdata(int num, va_list args) n = 0; for (i = 0; i < num; i++) { - a = va_arg(args, char *); + a = ms_va_arg(args, char *); /* ignore NULLs, thanks to Bob Beck */ if (a != NULL) { n += strlen(a); diff --git a/Makefile b/Makefile index df2d8b6e..9a93d740 100644 --- a/Makefile +++ b/Makefile @@ -136,6 +136,7 @@ MokManager.o: $(MOK_SOURCES) $(MMSONAME): $(MOK_OBJS) $(LIBS) $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a +gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: CFLAGS+=-DGNU_EFI_USE_EXTERNAL_STDARG gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: $(MAKE) -C gnu-efi \ ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ diff --git a/errlog.c b/errlog.c index ac657151..cc6a89f5 100644 --- a/errlog.c +++ b/errlog.c @@ -11,25 +11,25 @@ static UINTN nerrs = 0; EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, - va_list args) + ms_va_list args) { - va_list args2; + ms_va_list args2; EFI_STATUS efi_status = EFI_SUCCESS; if (verbose) { - va_copy(args2, args); + ms_va_copy(args2, args); console_print(L"%a:%d:%a() ", file, line, func); efi_status = VPrint(fmt, args2); - va_end(args2); + ms_va_end(args2); } return efi_status; } EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, - va_list args) + ms_va_list args) { - va_list args2; + ms_va_list args2; CHAR16 **newerrs; newerrs = ReallocatePool(errs, (nerrs + 1) * sizeof(*errs), @@ -40,11 +40,11 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, newerrs[nerrs] = PoolPrint(L"%a:%d %a() ", file, line, func); if (!newerrs[nerrs]) return EFI_OUT_OF_RESOURCES; - va_copy(args2, args); + ms_va_copy(args2, args); newerrs[nerrs+1] = VPoolPrint(fmt, args2); if (!newerrs[nerrs+1]) return EFI_OUT_OF_RESOURCES; - va_end(args2); + ms_va_end(args2); nerrs += 2; newerrs[nerrs] = NULL; @@ -56,12 +56,12 @@ VLogError(const char *file, int line, const char *func, const CHAR16 *fmt, EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { - va_list args; + ms_va_list args; EFI_STATUS efi_status; - va_start(args, fmt); + ms_va_start(args, fmt); efi_status = VLogError(file, line, func, fmt, args); - va_end(args); + ms_va_end(args); return efi_status; } diff --git a/gnu-efi b/gnu-efi index 4444de49..f922aec7 160000 --- a/gnu-efi +++ b/gnu-efi @@ -1 +1 @@ -Subproject commit 4444de49c66b5b6758976ab1e3862bb17cff9d56 +Subproject commit f922aec7d6d60c245a4d1e1f82598d427c7765b5 diff --git a/include/console.h b/include/console.h index 036262ef..f56b1231 100644 --- a/include/console.h +++ b/include/console.h @@ -108,7 +108,7 @@ extern UINT32 verbose; #endif extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, - const char *func, va_list args); + const char *func, ms_va_list args); #define vdprint(fmt, ...) \ vdprint_(fmt, __FILE__, __LINE__ - 1, __func__, ##__VA_ARGS__) diff --git a/include/hexdump.h b/include/hexdump.h index f778de9a..381e1a68 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -81,7 +81,7 @@ prepare_text(const void *data, size_t size, char *buf, unsigned int position) */ static inline void UNUSED EFIAPI vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, - const void *data, unsigned long size, size_t at, va_list ap) + const void *data, unsigned long size, size_t at, ms_va_list ap) { unsigned long display_offset = at; unsigned long offset = 0; @@ -118,11 +118,11 @@ static inline void UNUSED EFIAPI hexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, const void *data, unsigned long size, size_t at, ...) { - va_list ap; + ms_va_list ap; - va_start(ap, at); + ms_va_start(ap, at); vhexdumpf(file, line, func, fmt, data, size, at, ap); - va_end(ap); + ms_va_end(ap); } static inline void UNUSED diff --git a/include/system/efistdarg.h b/include/system/efistdarg.h index 837c4f23..034977cc 100644 --- a/include/system/efistdarg.h +++ b/include/system/efistdarg.h @@ -8,6 +8,10 @@ #ifndef _EFISTDARG_H_ #define _EFISTDARG_H_ +#ifndef GNU_EFI_USE_EXTERNAL_STDARG +#define GNU_EFI_USE_EXTERNAL_STDARG +#endif + #include #endif /* !_EFISTDARG_H_ */ diff --git a/include/system/stdarg.h b/include/system/stdarg.h index af1ac59b..ce722249 100644 --- a/include/system/stdarg.h +++ b/include/system/stdarg.h @@ -2,39 +2,79 @@ /* * stdarg.h - try to make consistent va_* handling for EFI */ -#ifdef SHIM_UNIT_TEST -#include_next -#else #ifndef _STDARG_H -#define _STDARG_H + +/* + * clang doesn't know about __builtin_sysv_va_list, apparently. + */ +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wcpp" +typedef __builtin_va_list __builtin_sysv_va_list; +#warning clang builds may not work at all for anything other than scan-build +#pragma GCC diagnostic pop +#endif #ifndef GNU_EFI_USE_EXTERNAL_STDARG #define GNU_EFI_USE_EXTERNAL_STDARG #endif +#ifdef SHIM_UNIT_TEST +#include_next +#endif + #if defined(__aarch64__) || defined(__arm__) || defined(__i386__) || \ defined(__i486__) || defined(__i686__) || defined(SHIM_UNIT_TEST) -typedef __builtin_va_list va_list; -#define va_copy(dest, start) __builtin_va_copy(dest, start) -#define va_start(marker, arg) __builtin_va_start(marker, arg) -#define va_arg(marker, type) __builtin_va_arg(marker, type) -#define va_end(marker) __builtin_va_end(marker) + +typedef __builtin_va_list ms_va_list; +typedef __builtin_va_list __builtin_ms_va_list; +#define ms_va_copy(dest, start) __builtin_va_copy(dest, start) +#define ms_va_start(marker, arg) __builtin_va_start(marker, arg) +#define ms_va_arg(marker, type) __builtin_va_arg(marker, type) +#define ms_va_end(marker) __builtin_va_end(marker) + +typedef __builtin_va_list sysv_va_list; +#define sysv_va_copy(dest, start) __builtin_va_copy(dest, start) +#define sysv_va_start(marker, arg) __builtin_va_start(marker, arg) +#define sysv_va_arg(marker, type) __builtin_va_arg(marker, type) +#define sysv_va_end(marker) __builtin_va_end(marker) +/* + * OpenSSL's X509ConstructCertificateStack needs this. + */ +typedef __builtin_va_list VA_LIST; +#define VA_COPY(dest, start) __builtin_va_copy(dest, start) +#define VA_START(marker, arg) __builtin_va_start(marker, arg) +#define VA_END(marker) __builtin_va_end(marker) +#define VA_ARG(marker, type) __builtin_va_arg(marker, type) + #elif defined(__x86_64__) -typedef __builtin_ms_va_list va_list; -#define va_copy(dest, start) __builtin_ms_va_copy(dest, start) -#define va_start(marker, arg) __builtin_ms_va_start(marker, arg) -#define va_arg(marker, type) __builtin_va_arg(marker, type) -#define va_end(marker) __builtin_ms_va_end(marker) + +typedef __builtin_ms_va_list ms_va_list; +#define ms_va_copy(dest, start) __builtin_ms_va_copy(dest, start) +#define ms_va_start(marker, arg) __builtin_ms_va_start(marker, arg) +#define ms_va_arg(marker, type) __builtin_va_arg(marker, type) +#define ms_va_end(marker) __builtin_ms_va_end(marker) +typedef __builtin_sysv_va_list sysv_va_list; +#define sysv_va_copy(dest, start) __builtin_sysv_va_copy(dest, start) +#define sysv_va_start(marker, arg) __builtin_sysv_va_start(marker, arg) +#define sysv_va_arg(marker, type) __builtin_va_arg(marker, type) +#define sysv_va_end(marker) __builtin_sysv_va_end(marker) +/* + * OpenSSL's X509ConstructCertificateStack needs this. + */ +typedef __builtin_ms_va_list VA_LIST; +#define VA_COPY(dest, start) __builtin_ms_va_copy(dest, start) +#define VA_START(marker, arg) __builtin_ms_va_start(marker, arg) +#define VA_END(marker) __builtin_ms_va_end(marker) +#define VA_ARG(marker, type) __builtin_va_arg(marker, type) + #else #error what arch is this #endif -typedef va_list VA_LIST; -#define VA_COPY(dest, start) va_copy(dest, start) -#define VA_START(marker, arg) va_start(marker, arg) -#define VA_END(marker) va_end(marker) -#define VA_ARG(marker, type) va_arg(marker, type) +#ifndef _STDARG_H +#define _STDARG_H +#endif /* !_STDARG_H #2 */ #endif /* !_STDARG_H */ -#endif /* !SHIM_UNIT_TEST */ // vim:fenc=utf-8:tw=75:noet diff --git a/lib/console.c b/lib/console.c index 2da20b31..c310d213 100644 --- a/lib/console.c +++ b/lib/console.c @@ -86,15 +86,15 @@ VOID console_fini(VOID) UINTN EFIAPI console_print(const CHAR16 *fmt, ...) { - va_list args; + ms_va_list args; UINTN ret; if (!console_text_mode) setup_console(1); - va_start(args, fmt); + ms_va_start(args, fmt); ret = VPrint(fmt, args); - va_end(args); + ms_va_end(args); return ret; } @@ -103,7 +103,7 @@ UINTN EFIAPI console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) { SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; - va_list args; + ms_va_list args; UINTN ret; if (!console_text_mode) @@ -111,9 +111,9 @@ console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) co->SetCursorPosition(co, col, row); - va_start(args, fmt); + ms_va_start(args, fmt); ret = VPrint(fmt, args); - va_end(args); + ms_va_end(args); return ret; } diff --git a/shim.h b/shim.h index 44dddc7a..69ad2cc3 100644 --- a/shim.h +++ b/shim.h @@ -22,6 +22,7 @@ #if defined(__x86_64__) /* gcc 4.5.4 is the first documented release with -mabi=ms */ +/* gcc 4.7.1 is the first one with __builtin_ms_va_list */ #if !GNUC_PREREQ(4, 7) && !CLANG_PREREQ(3, 4) #error On x86_64 you must have a compiler new enough to support __attribute__((__ms_abi__)) #endif @@ -226,7 +227,7 @@ extern void shim_fini(void); extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...); extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, - const CHAR16 *fmt, va_list args); + const CHAR16 *fmt, ms_va_list args); extern VOID LogHexdump_(const char *file, int line, const char *func, const void *data, size_t sz); extern VOID PrintErrors(VOID); -- cgit v1.2.3 From b5a7c8ce6012ec8d5f9f2515537f918ef4ca9358 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Mar 2021 20:07:03 -0500 Subject: Don't even try to use builtins, just make sure we have the same types. For some reason when we try to ever use the builtins, even with the symbol there as a fallback, something goes horribly wrong somewhere around here: | (gdb) bt | #0 strcmp (s1=0x7d492359 "MD5", s2=0x7d492359 "MD5") at include/system/string.h:57 | #1 0x000000007d460419 in getrn (lh=lh@entry=0x7e081318, data=data@entry=0x7e084398, rhash=rhash@entry=0x7f7c9268) at crypto/lhash/lhash.c:415 | #2 0x000000007d46076e in lh_insert (lh=0x7e081318, data=data@entry=0x7e084398) at crypto/lhash/lhash.c:188 | #3 0x000000007d43e027 in OBJ_NAME_add (name=name@entry=0x7d492359 "MD5", type=type@entry=1, data=data@entry=0x7d4ad3a0 "\004") at crypto/objects/o_names.c:202 As much as I love a Sisyphean challenge, in the interest of not having bugs or time, this patch changes it to just not use them for anything other than guaranteeing our implementations have the exact same types as you would expect. Signed-off-by: Peter Jones --- include/system/builtins_begin_.h | 89 ++++++++++++++-------------------------- include/system/builtins_end_.h | 11 +++-- 2 files changed, 35 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/include/system/builtins_begin_.h b/include/system/builtins_begin_.h index 92ea5e3a..2686c41c 100644 --- a/include/system/builtins_begin_.h +++ b/include/system/builtins_begin_.h @@ -1,94 +1,65 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent /** - * macros to build builtin wrappers + * macros to build function declarations with the same types as builtins + * that we apparently really cannot depend on. */ -#ifndef mkbi_cat_ -#define mkbi_cat_(a, b) a##b -#endif -#ifdef SHIM_STRING_C_ + +/* + * Clang's __builtin_whatever and __typeof__ are broken thusly: + * In file included from MokManager.c:2: + * In file included from shim.h:47: + * include/system/string.h:29:1: error: builtin functions must be directly called + * mkbi1_(long int, ffsl, long int, x) + * ^ + */ +#if defined(__clang__) #ifndef mkbi1_ -#define mkbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; +#define mkbi1_(rtype, x, typea, a) rtype x(typea a); #endif + #ifndef mkbi2_ -#define mkbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; +#define mkbi2_(rtype, x, typea, a, typeb, b) rtype x(typea a, typeb b); #endif #ifndef mkbi3_ -#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) __typeof__(mkbi_cat_(__builtin_, x)) x; +#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) rtype x(typea a, typeb b, typec c); #endif #ifndef mkdepbi1_ -#define mkdepbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; +#define mkdepbi1_(rtype, x, typea, a) rtype x(typea a); #endif #ifndef mkdepbi2_ -#define mkdepbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; +#define mkdepbi2_(rtype, x, typea, a, typeb, b) rtype x(typea a, typeb b); #endif -#else /* ! SHIM_STRING_C_ */ +#else /* !__clang__ */ + +#ifndef mkbi_cat_ +#define mkbi_cat_(a, b) a##b +#endif #ifndef mkbi1_ -#define mkbi1_(rtype, x, typea, a) \ - static inline __attribute__((__unused__)) \ - rtype \ - x(typea a) \ - { \ - return mkbi_cat_(__builtin_, x)(a); \ - } +#define mkbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; #endif #ifndef mkbi2_ -#define mkbi2_(rtype, x, typea, a, typeb, b) \ - static inline __attribute__((__unused__)) \ - rtype \ - x(typea a, typeb b) \ - { \ - return mkbi_cat_(__builtin_, x)(a, b); \ - } +#define mkbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; #endif #ifndef mkbi3_ -#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) \ - static inline __attribute__((__unused__)) \ - rtype \ - x(typea a, typeb b, typec c) \ - { \ - return mkbi_cat_(__builtin_, x)(a, b,c); \ - } -#endif - -#ifdef SHIM_DEPRECATE_STRLEN -#ifndef mkdepbi_dep_ -#define mkdepbi_dep_ __attribute__((__deprecated__)) -#endif -#else /* !SHIM_DEPRECATE_STRLEN */ -#ifndef mkdepbi_dep_ -#define mkdepbi_dep_ +#define mkbi3_(rtype, x, typea, a, typeb, b, typec, c) __typeof__(mkbi_cat_(__builtin_, x)) x; #endif -#endif /* SHIM_DEPRECATE_STRLEN */ #ifndef mkdepbi1_ -#define mkdepbi1_(rtype, x, typea, a) \ - static inline __attribute__((__unused__)) \ - mkdepbi_dep_ \ - rtype \ - x(typea a) \ - { \ - return mkbi_cat_(__builtin_, x)(a); \ - } +#define mkdepbi1_(rtype, x, typea, a) __typeof__(mkbi_cat_(__builtin_, x)) x; #endif #ifndef mkdepbi2_ -#define mkdepbi2_(rtype, x, typea, a, typeb, b) \ - static inline __attribute__((__unused__)) \ - mkdepbi_dep_ \ - rtype \ - x(typea a, typeb b) \ - { \ - return mkbi_cat_(__builtin_, x)(a, b); \ - } +#define mkdepbi2_(rtype, x, typea, a, typeb, b) __typeof__(mkbi_cat_(__builtin_, x)) x; #endif -#endif /* SHIM_STRING_C_ */ + +#endif /* !__clang__ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/system/builtins_end_.h b/include/system/builtins_end_.h index 0a6ad60a..0bcd7661 100644 --- a/include/system/builtins_end_.h +++ b/include/system/builtins_end_.h @@ -3,26 +3,25 @@ #ifdef mkbi1_ #undef mkbi1_ #endif + #ifdef mkbi2_ #undef mkbi2_ #endif + #ifdef mkbi3_ #undef mkbi3_ #endif + #ifdef mkdepbi1_ #undef mkdepbi1_ #endif + #ifdef mkdepbi2_ #undef mkdepbi2_ #endif -#ifdef mkdepbi_dep_ -#undef mkdepbi_dep_ -#endif + #ifdef mkbi_cat_ #undef mkbi_cat_ #endif -#ifdef _BUILTINS_BEGIN__H -#undef _BUILTINS_BEGIN__H -#endif // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 76f35c00ef9df3958c5479d74f8d6605c32901ec Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Mar 2021 17:19:10 -0500 Subject: sbat variable: use UEFI_VAR_NV_BS_RT when we've got ENABLE_SHIM_DEVEL This makes it so that if you build with ENABLE_SHIM_DEVEL, the SBAT we use is named SBAT_DEVEL instead of SBAT, and it's expected to have EFI_VARIABLE_RUNTIME_ACCESS set. Signed-off-by: Peter Jones --- include/sbat.h | 17 +++++++++++++++++ mok.c | 8 ++++---- sbat.c | 26 +++++++++++++++++++------- 3 files changed, 40 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 95fa6a56..5db82379 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -13,9 +13,26 @@ #define UEFI_VAR_NV_BS \ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) +#define UEFI_VAR_NV_BS_RT \ + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS) #define UEFI_VAR_NV_BS_TIMEAUTH \ (UEFI_VAR_NV_BS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) +#if defined(ENABLE_SHIM_DEVEL) +#define SBAT_VAR_NAME L"SBAT_DEVEL" +#define SBAT_VAR_NAME8 "SBAT_DEVEL" +#define SBAT_RT_VAR_NAME L"SbatRT_DEVEL" +#define SBAT_RT_VAR_NAME8 "SbatRT_DEVEL" +#define SBAT_VAR_ATTRS UEFI_VAR_NV_BS_RT +#else +#define SBAT_VAR_NAME L"SBAT" +#define SBAT_VAR_NAME8 "SBAT" +#define SBAT_RT_VAR_NAME L"SbatRT" +#define SBAT_RT_VAR_NAME8 "SbatRT" +#define SBAT_VAR_ATTRS UEFI_VAR_NV_BS +#endif + extern UINTN _sbat, _esbat; struct sbat_var_entry { diff --git a/mok.c b/mok.c index 048d38d5..e3c3d9ee 100644 --- a/mok.c +++ b/mok.c @@ -225,10 +225,10 @@ struct mok_state_variable mok_state_variables[] = { .no_attr = EFI_VARIABLE_RUNTIME_ACCESS, .state = &ignore_db, }, - {.name = L"SBAT", - .name8 = "SBAT", - .rtname = L"SbatRT", - .rtname8 = "SbatRT", + {.name = SBAT_VAR_NAME, + .name8 = SBAT_VAR_NAME8, + .rtname = SBAT_RT_VAR_NAME, + .rtname8 = SBAT_RT_VAR_NAME8, .guid = &SHIM_LOCK_GUID, .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, diff --git a/sbat.c b/sbat.c index 77b6f5ab..f6be6cb6 100644 --- a/sbat.c +++ b/sbat.c @@ -280,7 +280,7 @@ parse_sbat_var(list_t *entries) if (!entries) return EFI_INVALID_PARAMETER; - efi_status = get_variable(L"SBAT", &data, &datasize, SHIM_LOCK_GUID); + efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { LogError(L"Failed to read SBAT variable\n", efi_status); return efi_status; @@ -293,6 +293,17 @@ parse_sbat_var(list_t *entries) return parse_sbat_var_data(entries, data, datasize+1); } +static bool +check_sbat_var_attributes(UINT32 attributes) +{ +#ifdef ENABLE_SHIM_DEVEL + return attributes == UEFI_VAR_NV_BS_RT; +#else + return attributes == UEFI_VAR_NV_BS || + attributes == UEFI_VAR_NV_BS_TIMEAUTH; +#endif +} + EFI_STATUS set_sbat_uefi_variable(void) { @@ -302,7 +313,7 @@ set_sbat_uefi_variable(void) UINT8 *sbat = NULL; UINTN sbatsize = 0; - efi_status = get_variable_attr(L"SBAT", &sbat, &sbatsize, + efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, SHIM_LOCK_GUID, &attributes); /* * Always set the SBAT UEFI variable if it fails to read. @@ -312,8 +323,7 @@ set_sbat_uefi_variable(void) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if ((attributes == UEFI_VAR_NV_BS || - attributes == UEFI_VAR_NV_BS_TIMEAUTH) && + } else if (check_sbat_var_attributes(attributes) && sbatsize >= strlen(SBAT_VAR_SIG "1") && strncmp((const char *)sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG))) { @@ -323,7 +333,8 @@ set_sbat_uefi_variable(void) FreePool(sbat); /* delete previous variable */ - efi_status = set_variable(L"SBAT", SHIM_LOCK_GUID, attributes, 0, ""); + efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, + attributes, 0, ""); if (EFI_ERROR(efi_status)) { dprint(L"SBAT variable delete failed %r\n", efi_status); return efi_status; @@ -331,7 +342,7 @@ set_sbat_uefi_variable(void) } /* set variable */ - efi_status = set_variable(L"SBAT", SHIM_LOCK_GUID, UEFI_VAR_NV_BS, + efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, sizeof(SBAT_VAR), SBAT_VAR); if (EFI_ERROR(efi_status)) { dprint(L"SBAT variable writing failed %r\n", efi_status); @@ -339,7 +350,8 @@ set_sbat_uefi_variable(void) } /* verify that the expected data is there */ - efi_status = get_variable(L"SBAT", &sbat, &sbatsize, SHIM_LOCK_GUID); + efi_status = get_variable(SBAT_VAR_NAME, &sbat, &sbatsize, + SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); return efi_status; -- cgit v1.2.3 From 39b96c01bfd4547f38c9e573ff5d551057ea272c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 11 Mar 2021 21:56:37 -0500 Subject: 'make test': try harder to make it build in the right order. Signed-off-by: Peter Jones --- include/test.mk | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 542be176..5d74bdc2 100644 --- a/include/test.mk +++ b/include/test.mk @@ -29,6 +29,8 @@ CFLAGS = -O2 -ggdb -std=gnu11 \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" $(wildcard test-*.c) :: %.c : test-random.h +$(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h +$(patsubst %.c,%.o,$(wildcard test-*.c)) : | test-random.h test-random.h: dd if=/dev/urandom bs=512 count=17 of=random.bin -- cgit v1.2.3 From f5a3de3264f87f48186f80297fb1dbcddfd6d945 Mon Sep 17 00:00:00 2001 From: Steve McIntyre Date: Mon, 15 Mar 2021 23:12:08 +0000 Subject: Fix up build of test code using gcc 8 Don't check SHIM_UNIT_TEST. This fixes conflicting declarations for __builtin_ms_va_list on amd64: In file included from shim.h:47, from test.c:10: ../include/system/stdarg.h:30:27: error: conflicting types for '__builtin_ms_va_list' typedef __builtin_va_list __builtin_ms_va_list; ^~~~~~~~~~~~~~~~~~~~ cc1: note: previous declaration of '__builtin_ms_va_list' was here In file included from shim.h:47, from test-csv.c:9: ../include/system/stdarg.h:30:27: error: conflicting types for '__builtin_ms_va_list' typedef __builtin_va_list __builtin_ms_va_list; ^~~~~~~~~~~~~~~~~~~~ cc1: note: previous declaration of '__builtin_ms_va_list' was here In file included from shim.h:47, from csv.c:6: ../include/system/stdarg.h:30:27: error: conflicting types for '__builtin_ms_va_list' typedef __builtin_va_list __builtin_ms_va_list; ^~~~~~~~~~~~~~~~~~~~ cc1: note: previous declaration of '__builtin_ms_va_list' was here Signed-off-by: Steve McIntyre <93sam@debian.org> --- include/system/stdarg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/system/stdarg.h b/include/system/stdarg.h index ce722249..4c956f70 100644 --- a/include/system/stdarg.h +++ b/include/system/stdarg.h @@ -24,7 +24,7 @@ typedef __builtin_va_list __builtin_sysv_va_list; #endif #if defined(__aarch64__) || defined(__arm__) || defined(__i386__) || \ - defined(__i486__) || defined(__i686__) || defined(SHIM_UNIT_TEST) + defined(__i486__) || defined(__i686__) typedef __builtin_va_list ms_va_list; typedef __builtin_va_list __builtin_ms_va_list; -- cgit v1.2.3 From 3dd40ade68c6ff63e776b5f9acbd811a3c345d01 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Thu, 18 Mar 2021 14:32:24 +0000 Subject: Ensure that MOK variable mirroring creates well formed ESLs The MOK variable mirroring makes use of variable_create_esl, which can only create a well-formed EFI_SIGNATURE_LIST containing a single signature. Fix fill_esl and variable_create_esl to support creating a EFI_SIGNATURE_LIST with one or more supplied EFI_SIGNATURE_DATA structures. Introduce variable_create_esl_with_one_signature and fill_esl_with_one_signature for code that does want to create a EFI_SIGNATURE_LIST containing a single signature constructed from a supplied signature data buffer and owner GUID. --- include/variables.h | 16 ++++++++--- lib/variables.c | 81 +++++++++++++++++++++++++++++++++++++++++++---------- mok.c | 56 ++++++++++++++++++------------------ 3 files changed, 106 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/variables.h b/include/variables.h index 31cfcb65..493f433f 100644 --- a/include/variables.h +++ b/include/variables.h @@ -64,12 +64,20 @@ EFI_STATUS variable_enroll_hash(const CHAR16 * const var, EFI_GUID owner, UINT8 hash[SHA256_DIGEST_SIZE]); EFI_STATUS -variable_create_esl(const uint8_t *cert, const size_t cert_len, - const EFI_GUID *type, const EFI_GUID *owner, +variable_create_esl(const EFI_SIGNATURE_DATA *first_sig, const size_t howmany, + const EFI_GUID *type, const UINT32 sig_size, uint8_t **out, size_t *outlen); EFI_STATUS -fill_esl(const uint8_t *data, const size_t data_len, - const EFI_GUID *type, const EFI_GUID *owner, +variable_create_esl_with_one_signature(const uint8_t* data, const size_t data_len, + const EFI_GUID *type, const EFI_GUID *owner, + uint8_t **out, size_t *outlen); +EFI_STATUS +fill_esl(const EFI_SIGNATURE_DATA *first_sig, const size_t howmany, + const EFI_GUID *type, const UINT32 sig_size, uint8_t *out, size_t *outlen); +EFI_STATUS +fill_esl_with_one_signature(const uint8_t *data, const uint32_t data_len, + const EFI_GUID *type, const EFI_GUID *owner, + uint8_t *out, size_t *outlen); #endif /* SHIM_VARIABLES_H */ diff --git a/lib/variables.c b/lib/variables.c index 9f5b2b57..53a5d14a 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -13,18 +13,24 @@ #include "shim.h" EFI_STATUS -fill_esl(const uint8_t *data, const size_t data_len, - const EFI_GUID *type, const EFI_GUID *owner, +fill_esl(const EFI_SIGNATURE_DATA *first_sig, const size_t howmany, + const EFI_GUID *type, const UINT32 sig_size, uint8_t *out, size_t *outlen) { EFI_SIGNATURE_LIST *sl; EFI_SIGNATURE_DATA *sd; size_t needed = 0; + size_t data_len = howmany * sig_size; - if (!data || !data_len || !type || !outlen) + dprint(L"fill_esl: data=%p, data_len=%lu", first_sig, data_len); + + if ((out && !first_sig) || !howmany || !type || !sig_size || !outlen) + return EFI_INVALID_PARAMETER; + + if (howmany > (UINT32_MAX - sizeof(EFI_SIGNATURE_LIST)) / sig_size) return EFI_INVALID_PARAMETER; - needed = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) + data_len; + needed = sizeof(EFI_SIGNATURE_LIST) + data_len; if (!out || *outlen < needed) { *outlen = needed; return EFI_BUFFER_TOO_SMALL; @@ -35,27 +41,70 @@ fill_esl(const uint8_t *data, const size_t data_len, sl->SignatureHeaderSize = 0; sl->SignatureType = *type; - sl->SignatureSize = sizeof(EFI_GUID) + data_len; + sl->SignatureSize = sig_size; sl->SignatureListSize = needed; sd = (EFI_SIGNATURE_DATA *)(out + sizeof(EFI_SIGNATURE_LIST)); - if (owner) - sd->SignatureOwner = *owner; - - CopyMem(sd->SignatureData, data, data_len); + CopyMem(sd, first_sig, data_len); return EFI_SUCCESS; } EFI_STATUS -variable_create_esl(const uint8_t *data, const size_t data_len, - const EFI_GUID *type, const EFI_GUID *owner, +fill_esl_with_one_signature(const uint8_t *data, const uint32_t data_len, + const EFI_GUID *type, const EFI_GUID *owner, + uint8_t *out, size_t *outlen) +{ + EFI_STATUS efi_status; + EFI_SIGNATURE_DATA *sd = NULL; + UINT32 sig_size = sizeof(EFI_SIGNATURE_DATA) - 1 + data_len; + + if (data_len > UINT32_MAX - sizeof(EFI_SIGNATURE_DATA) + 1) + return EFI_INVALID_PARAMETER; + + if (out) { + sd = AllocateZeroPool(sig_size); + if (owner) + CopyMem(sd, owner, sizeof(EFI_GUID)); + CopyMem(sd->SignatureData, data, data_len); + } + + efi_status = fill_esl(sd, 1, type, sig_size, out, outlen); + + if (out) + FreePool(sd); + return efi_status; +} + +EFI_STATUS +variable_create_esl(const EFI_SIGNATURE_DATA *first_sig, const size_t howmany, + const EFI_GUID *type, const UINT32 sig_size, uint8_t **out, size_t *outlen) { EFI_STATUS efi_status; *outlen = 0; - efi_status = fill_esl(data, data_len, type, owner, NULL, outlen); + efi_status = fill_esl(first_sig, howmany, type, sig_size, NULL, outlen); + if (efi_status != EFI_BUFFER_TOO_SMALL) + return efi_status; + + *out = AllocateZeroPool(*outlen); + if (!*out) + return EFI_OUT_OF_RESOURCES; + + return fill_esl(first_sig, howmany, type, sig_size, *out, outlen); +} + +EFI_STATUS +variable_create_esl_with_one_signature(const uint8_t* data, const size_t data_len, + const EFI_GUID *type, const EFI_GUID *owner, + uint8_t **out, size_t *outlen) +{ + EFI_STATUS efi_status; + + *outlen = 0; + efi_status = fill_esl_with_one_signature(data, data_len, type, owner, + NULL, outlen); if (efi_status != EFI_BUFFER_TOO_SMALL) return efi_status; @@ -63,7 +112,8 @@ variable_create_esl(const uint8_t *data, const size_t data_len, if (!*out) return EFI_OUT_OF_RESOURCES; - return fill_esl(data, data_len, type, owner, *out, outlen); + return fill_esl_with_one_signature(data, data_len, type, owner, *out, + outlen); } EFI_STATUS @@ -153,8 +203,9 @@ SetSecureVariable(const CHAR16 * const var, UINT8 *Data, UINTN len, if (createtimebased) { size_t ds; - efi_status = variable_create_esl(Data, len, &X509_GUID, NULL, - (uint8_t **)&Cert, &ds); + efi_status = variable_create_esl_with_one_signature( + Data, len, &X509_GUID, NULL, + (uint8_t **)&Cert, &ds); if (EFI_ERROR(efi_status)) { console_print(L"Failed to create %s certificate %d\n", var, efi_status); diff --git a/mok.c b/mok.c index e3c3d9ee..3d2b398c 100644 --- a/mok.c +++ b/mok.c @@ -303,8 +303,8 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN *newsz, SIZE_T maxsz) { EFI_STATUS efi_status; - SIZE_T howmany, varsz = 0, esdsz; - UINT8 *var, *data; + SIZE_T howmany, varsz = 0; + UINT8 *var; howmany = MIN((maxsz - sizeof(*esl)) / esl->SignatureSize, (esl->SignatureListSize - sizeof(*esl)) / esl->SignatureSize); @@ -316,8 +316,6 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, * We always assume esl->SignatureHeaderSize is 0 (and so far, * that's true as per UEFI 2.8) */ - esdsz = howmany * esl->SignatureSize; - data = (UINT8 *)esd; dprint(L"Trying to add %lx signatures to \"%s\" of size %lx\n", howmany, name, esl->SignatureSize); @@ -327,10 +325,9 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, * * Compensate here. */ - efi_status = variable_create_esl(data + sizeof(EFI_GUID), - esdsz - sizeof(EFI_GUID), + efi_status = variable_create_esl(esd, howmany, &esl->SignatureType, - &esd->SignatureOwner, + esl->SignatureSize, &var, &varsz); if (EFI_ERROR(efi_status) || !var || !varsz) { LogError(L"Couldn't allocate %lu bytes for mok variable \"%s\": %r\n", @@ -349,7 +346,7 @@ mirror_one_esl(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, return efi_status; } - *newsz = esdsz; + *newsz = howmany * esl->SignatureSize; return efi_status; } @@ -507,7 +504,7 @@ mirror_mok_db(CHAR16 *name, CHAR8 *name8, EFI_GUID *guid, UINT32 attrs, UINT8 *var; UINTN varsz; - efi_status = variable_create_esl( + efi_status = variable_create_esl_with_one_signature( null_sha256, sizeof(null_sha256), &EFI_CERT_SHA256_GUID, &SHIM_LOCK_GUID, &var, &varsz); @@ -592,10 +589,12 @@ mirror_one_mok_variable(struct mok_state_variable *v, FullDataSize, FullData); break; case VENDOR_ADDEND_X509: - efi_status = fill_esl(*v->addend, *v->addend_size, - &EFI_CERT_TYPE_X509_GUID, - &SHIM_LOCK_GUID, - NULL, &addend_esl_sz); + efi_status = fill_esl_with_one_signature(*v->addend, + *v->addend_size, + &EFI_CERT_TYPE_X509_GUID, + &SHIM_LOCK_GUID, + NULL, + &addend_esl_sz); if (efi_status != EFI_BUFFER_TOO_SMALL) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); @@ -616,11 +615,11 @@ mirror_one_mok_variable(struct mok_state_variable *v, * then the build cert if it's there */ if (should_mirror_build_cert(v)) { - efi_status = fill_esl(*v->build_cert, - *v->build_cert_size, - &EFI_CERT_TYPE_X509_GUID, - &SHIM_LOCK_GUID, - NULL, &build_cert_esl_sz); + efi_status = fill_esl_with_one_signature(*v->build_cert, + *v->build_cert_size, + &EFI_CERT_TYPE_X509_GUID, + &SHIM_LOCK_GUID, + NULL, &build_cert_esl_sz); if (efi_status != EFI_BUFFER_TOO_SMALL) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); @@ -703,10 +702,11 @@ mirror_one_mok_variable(struct mok_state_variable *v, FullDataSize, FullData, p, p-(uintptr_t)FullData); break; case VENDOR_ADDEND_X509: - efi_status = fill_esl(*v->addend, *v->addend_size, - &EFI_CERT_TYPE_X509_GUID, - &SHIM_LOCK_GUID, - p, &addend_esl_sz); + efi_status = fill_esl_with_one_signature(*v->addend, + *v->addend_size, + &EFI_CERT_TYPE_X509_GUID, + &SHIM_LOCK_GUID, + p, &addend_esl_sz); if (EFI_ERROR(efi_status)) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); @@ -729,11 +729,11 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); if (should_mirror_build_cert(v)) { - efi_status = fill_esl(*v->build_cert, - *v->build_cert_size, - &EFI_CERT_TYPE_X509_GUID, - &SHIM_LOCK_GUID, - p, &build_cert_esl_sz); + efi_status = fill_esl_with_one_signature(*v->build_cert, + *v->build_cert_size, + &EFI_CERT_TYPE_X509_GUID, + &SHIM_LOCK_GUID, + p, &build_cert_esl_sz); if (EFI_ERROR(efi_status)) { perror(L"Could not add built-in cert to %s: %r\n", v->name, efi_status); @@ -765,7 +765,7 @@ mirror_one_mok_variable(struct mok_state_variable *v, * need a dummy entry */ if ((v->flags & MOK_MIRROR_KEYDB) && FullDataSize == 0) { - efi_status = variable_create_esl( + efi_status = variable_create_esl_with_one_signature( null_sha256, sizeof(null_sha256), &EFI_CERT_SHA256_GUID, &SHIM_LOCK_GUID, &FullData, &FullDataSize); -- cgit v1.2.3 From 33db42def2ce6fe040b5f77642347e8b3c6420e5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sun, 21 Mar 2021 15:57:03 -0400 Subject: Make 'make test' work on gcc 4.8.5 --- include/endian.h | 10 ++++++---- include/test.mk | 2 +- lib/string.c | 21 +++++++++++++++++++++ test-str.c | 23 +++++++++++++++++++++++ test.c | 3 +++ 5 files changed, 54 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/endian.h b/include/endian.h index 267fbf00..1c4a21de 100644 --- a/include/endian.h +++ b/include/endian.h @@ -3,9 +3,11 @@ * endian.h - bswap decls that can't go in compiler.h * Copyright Peter Jones */ - -#ifndef ENDIAN_H_ -#define ENDIAN_H_ +#ifdef SHIM_UNIT_TEST +#include_next +#endif +#ifndef SHIM_ENDIAN_H_ +#define SHIM_ENDIAN_H_ #include @@ -15,5 +17,5 @@ mkbi1_(uint32_t, bswap32, uint32_t, x) mkbi1_(uint64_t, bswap64, uint64_t, x) #include "system/builtins_end_.h" -#endif /* !ENDIAN_H_ */ +#endif /* !SHIM_ENDIAN_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test.mk b/include/test.mk index 5d74bdc2..62cf983a 100644 --- a/include/test.mk +++ b/include/test.mk @@ -21,7 +21,7 @@ CFLAGS = -O2 -ggdb -std=gnu11 \ -Wno-unused \ -Werror \ -Werror=nonnull \ - -Werror=nonnull-compare \ + $(shell $(CC) -Werror=nonnull-compare -E -x c /dev/null >/dev/null 2>&1 && echo -Werror=nonnull-compare) \ $(ARCH_DEFINES) \ -DEFI_FUNCTION_WRAPPER \ -DGNU_EFI_USE_MS_ABI -DPAGE_SIZE=4096 \ diff --git a/lib/string.c b/lib/string.c index 37eabe8c..d941cd56 100644 --- a/lib/string.c +++ b/lib/string.c @@ -7,7 +7,13 @@ #ifdef SHIM_UNIT_TEST #define strlen shim_strlen +#ifdef strcmp +#undef strcmp +#endif #define strcmp shim_strcmp +#ifdef strncmp +#undef strncmp +#endif #define strncmp shim_strncmp #define strncasecmp shim_strncasecmp #define strcasecmp shim_strcasecmp @@ -15,11 +21,26 @@ #define strlen shim_strlen #define strnlen shim_strnlen #define strcpy shim_strcpy +#ifdef strncpy +#undef strncpy +#endif #define strncpy shim_strncpy +#ifdef strdup +#undef strdup +#endif #define strdup shim_strdup +#ifdef strndup +#undef strndup +#endif #define strndup shim_strndup +#ifdef stpcpy +#undef stpcpy +#endif #define stpcpy shim_stpcpy #define strchrnul shim_strchrnul +#ifdef strchr +#undef strchr +#endif #define strchr shim_strchr #endif diff --git a/test-str.c b/test-str.c index 87605b26..70d1637c 100644 --- a/test-str.c +++ b/test-str.c @@ -6,6 +6,8 @@ #pragma GCC diagnostic error "-Wnonnull" #pragma GCC diagnostic error "-Wunused-function" +#pragma GCC diagnostic warning "-Wcpp" + #ifndef SHIM_UNIT_TEST #define SHIM_UNIT_TEST #endif @@ -1046,8 +1048,29 @@ test_strcat(void) memset(s1, 0, 4096); assert_equal_return(strcat(s1, s0), s1, -1, "got %p expected %p\n"); + /* For unknown reasons, gcc 4.8.5 gives us this here: + * | In file included from shim.h:64:0, + * | from test-str.c:14: + * | test-str.c: In function 'test_strcat': + * | include/test.h:85:10: warning: array subscript is below array bounds [-Warray-bounds] + * | printf("%s:%d:got %lld, expected zero " fmt, __func__, \ + * | ^ + * | include/test.h:112:10: warning: array subscript is below array bounds [-Warray-bounds] + * | printf("%s:%d:got %lld, expected < 0 " fmt, __func__, \ + * | ^ + * + * This clearly isn't a useful error message, as it doesn't tell us + * /anything about the problem/, but also it isn't reported on + * later compilers, and it isn't clear that there's any problem + * when examining these functions. + * + * I don't know. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Warray-bounds" assert_zero_return(strncmp(s1, s0, sizeof(s)-1), 0, -1, "\n"); assert_negative_return(memcmp(s1, s0, sizeof(s)), 0, -1, "\n"); +#pragma GCC diagnostic pop memset(s1, 0, 4096); assert_equal_return(strcat(s1, s0), s1, -1, "got %p expected %p\n"); diff --git a/test.c b/test.c index aa0da1fd..dc71941f 100644 --- a/test.c +++ b/test.c @@ -12,6 +12,9 @@ UINT8 in_protocol = 0; int debug = DEFAULT_DEBUG_PRINT_STATE; +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-function" + EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { -- cgit v1.2.3 From 08a0ce01dbe9945287f37a9b139b25f46c53f878 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 26 Mar 2021 21:19:14 -0700 Subject: Move the check for the SBAT variable properties to its own function. This moves the check for the SBAT variable's attributes and contents into its own function, so that test cases can be written against it. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 1 + sbat.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 5db82379..656bf8d7 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -51,6 +51,7 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); EFI_STATUS set_sbat_uefi_variable(void); +bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes); struct sbat_section_entry { const CHAR8 *component_name; diff --git a/sbat.c b/sbat.c index 89c08417..7bd0e4ec 100644 --- a/sbat.c +++ b/sbat.c @@ -304,6 +304,14 @@ check_sbat_var_attributes(UINT32 attributes) #endif } +bool +preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes) +{ + return check_sbat_var_attributes(attributes) && + sbatsize >= strlen(SBAT_VAR_SIG "1") && + strncmp((const char *)sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG)); +} + EFI_STATUS set_sbat_uefi_variable(void) { @@ -323,10 +331,7 @@ set_sbat_uefi_variable(void) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if (check_sbat_var_attributes(attributes) && - sbatsize >= strlen(SBAT_VAR_SIG "1") && - strncmp((const char *)sbat, SBAT_VAR_SIG, - strlen(SBAT_VAR_SIG))) { + } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes)) { dprint("SBAT variable is %d bytes, attributes are 0x%08x\n", sbatsize, attributes); FreePool(sbat); -- cgit v1.2.3 From 27da4170f0fb30acde91a37e0256dfcfe76ea69e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Sat, 27 Mar 2021 11:09:52 -0700 Subject: Change SBAT variable name to SbatLevel Because a few shim builds were signed that did not properly initialize the SBAT variable, and in doing so deleted valid SBAT variables, we need to use a different name. This changes the name from "SBAT" to "SbatLevel". Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 16 ++++++++-------- sbat.c | 26 ++++++++++++++------------ shim.c | 12 ++++++------ 3 files changed, 28 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 656bf8d7..8551b74a 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -20,16 +20,16 @@ (UEFI_VAR_NV_BS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) #if defined(ENABLE_SHIM_DEVEL) -#define SBAT_VAR_NAME L"SBAT_DEVEL" -#define SBAT_VAR_NAME8 "SBAT_DEVEL" -#define SBAT_RT_VAR_NAME L"SbatRT_DEVEL" -#define SBAT_RT_VAR_NAME8 "SbatRT_DEVEL" +#define SBAT_VAR_NAME L"SbatLevel_DEVEL" +#define SBAT_VAR_NAME8 "SbatLevel_DEVEL" +#define SBAT_RT_VAR_NAME L"SbatLevelRT_DEVEL" +#define SBAT_RT_VAR_NAME8 "SbatLevelRT_DEVEL" #define SBAT_VAR_ATTRS UEFI_VAR_NV_BS_RT #else -#define SBAT_VAR_NAME L"SBAT" -#define SBAT_VAR_NAME8 "SBAT" -#define SBAT_RT_VAR_NAME L"SbatRT" -#define SBAT_RT_VAR_NAME8 "SbatRT" +#define SBAT_VAR_NAME L"SbatLevel" +#define SBAT_VAR_NAME8 "SbatLevel" +#define SBAT_RT_VAR_NAME L"SbatLevelRT" +#define SBAT_RT_VAR_NAME8 "SbatLevelRT" #define SBAT_VAR_ATTRS UEFI_VAR_NV_BS #endif diff --git a/sbat.c b/sbat.c index 4d6ddd22..5821475b 100644 --- a/sbat.c +++ b/sbat.c @@ -120,8 +120,8 @@ verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sba sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation); if (sbat_gen < sbat_var_gen) { - dprint(L"component %a, generation %d, was revoked by SBAT variable", - entry->component_name, sbat_gen); + dprint(L"component %a, generation %d, was revoked by %s variable", + entry->component_name, sbat_gen, SBAT_VAR_NAME); LogError(L"image did not pass SBAT verification\n"); return EFI_SECURITY_VIOLATION; } @@ -157,7 +157,7 @@ verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry * struct sbat_var_entry *sbat_var_entry; if (list_empty(local_sbat_var)) { - dprint(L"SBAT variable not present\n"); + dprint(L"%s variable not present\n", SBAT_VAR_NAME); return EFI_SUCCESS; } @@ -324,16 +324,16 @@ set_sbat_uefi_variable(void) efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, SHIM_LOCK_GUID, &attributes); /* - * Always set the SBAT UEFI variable if it fails to read. + * Always set the SbatLevel UEFI variable if it fails to read. * - * Don't try to set the SBAT UEFI variable if attributes match and - * the signature matches. + * Don't try to set the SbatLevel UEFI variable if attributes match + * and the signature matches. */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes)) { - dprint("SBAT variable is %d bytes, attributes are 0x%08x\n", - sbatsize, attributes); + dprint(L"%s variable is %d bytes, attributes are 0x%08x\n", + SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); return EFI_SUCCESS; } else { @@ -346,7 +346,8 @@ set_sbat_uefi_variable(void) efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, attributes, 0, ""); if (EFI_ERROR(efi_status)) { - dprint(L"SBAT variable delete failed %r\n", efi_status); + dprint(L"%s variable delete failed %r\n", SBAT_VAR_NAME, + efi_status); return efi_status; } } @@ -355,7 +356,8 @@ set_sbat_uefi_variable(void) efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, sizeof(SBAT_VAR)-1, SBAT_VAR); if (EFI_ERROR(efi_status)) { - dprint(L"SBAT variable writing failed %r\n", efi_status); + dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME, + efi_status); return efi_status; } @@ -363,7 +365,7 @@ set_sbat_uefi_variable(void) efi_status = get_variable(SBAT_VAR_NAME, &sbat, &sbatsize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { - dprint(L"SBAT read failed %r\n", efi_status); + dprint(L"%s read failed %r\n", SBAT_VAR_NAME, efi_status); return efi_status; } @@ -373,7 +375,7 @@ set_sbat_uefi_variable(void) strlen(SBAT_VAR)); efi_status = EFI_INVALID_PARAMETER; } else { - dprint(L"SBAT variable initialization succeeded\n"); + dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME); } FreePool(sbat); diff --git a/shim.c b/shim.c index 117c8f42..5bed2446 100644 --- a/shim.c +++ b/shim.c @@ -1895,7 +1895,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) L"shim_init() failed", L"import of SBAT data failed", L"SBAT self-check failed", - L"SBAT UEFI variable setting failed", + SBAT_VAR_NAME L" UEFI variable setting failed", NULL }; enum { @@ -1935,12 +1935,12 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) efi_status = set_sbat_uefi_variable(); if (EFI_ERROR(efi_status) && secure_mode()) { - perror(L"SBAT variable initialization failed\n"); + perror(L"%s variable initialization failed\n", SBAT_VAR_NAME); msg = SET_SBAT; goto die; } else if (EFI_ERROR(efi_status)) { - dprint(L"SBAT variable initialization failed: %r\n", - efi_status); + dprint(L"%s variable initialization failed: %r\n", + SBAT_VAR_NAME, efi_status); } if (secure_mode()) { @@ -1950,8 +1950,8 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) INIT_LIST_HEAD(&sbat_var); efi_status = parse_sbat_var(&sbat_var); if (EFI_ERROR(efi_status)) { - perror(L"Parsing SBAT variable failed: %r\n", - efi_status); + perror(L"Parsing %s variable failed: %r\n", + SBAT_VAR_NAME, efi_status); msg = IMPORT_SBAT; goto die; } -- cgit v1.2.3 From 4d64389c6c941d21548b06423b8131c872e3c3c7 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 7 Jun 2021 16:34:18 +0100 Subject: shim: another attempt to fix load options handling The load options handling is quite complicated and tries to accomodate several scenarios, but there are currently multiple issues: - If the supplied LoadOptions is an EFI_LOAD_OPTION structure, second_stage gets initialized to the entire contents of the OptionalData field and load_options is initialized to NULL, which means it isn't possible to pass additional options to the second stage loader (and it looks like the intention is for this to be supported). - If the supplied LoadOptions contains 2 or more strings, the code seems to assume that shim was executed from the UEFI shell and that the first argument is the path of the shim executable, so it's ignored. But this breaks the ability to pass additional options to the second stage loader from BDS on firmware implementations that initialize LoadOptions to just the OptionalData field of the EFI_LOAD_OPTION, which is what EDK2 seems to do. This is moot anyway because this case (strings == 2) doesn't actually seem to work, as nothing sets loader_len and therefore second_stage is not set to the custom loader path. - If the supplied LoadOptions contains a single string that isn't shim's path, nothing sets loader_len and therefore second_stage isn't set at the end of set_second_stage. - set_second_stage replaces L' ' characters with L'\0' - whilst this is useful to NULL terminate the path for the second stage, it doesn't seem quite right to do this for the remaining LoadOptions data. Grub's chainloader command supplies additional arguments as a NULL-terminated space-delimited string via LoadOptions. Making it NULL-delimited seems to be incompatible with the kernel's commandline handling, which wouldn't work for scenarios where you might want to direct-boot a kernel image (wrapped in systemd's EFI stub) from shim. - handle_image passes the original LoadOptions to the second stage if load_options is NULL, which means that the second stage currently always gets shim's load options. I've made an attempt to try to fix things. After the initial checks in set_second_stage, it now does this: - Tries to parse LoadOptions as an EFI_LOAD_OPTION in order to extract the OptionalData if it is. - If it's not an EFI_LOAD_OPTION, check if the first string is the current shim path and ignore it if it is (the UEFI shell case). - Split LoadOptions in to a single NULL terminated string (used to initialize second_stage) and the unmodified remaining data (used to initialize load_options and load_options_size). I've also modified handle_image to always set LoadOptions and LoadOptionsSize. If shim is executed with no options, or is only executed with a single option to override the second stage loader path, the second stage is executed with LoadOptions = NULL and LoadOptionsSize = 0 now. I've tested this on EDK2 and I can load a custom loader with extra options from both BDS and the UEFI shell: FS0:\> shimx64.efi test.efi LoadOptionsSize: 0 LoadOptions: (null) FS0:\> shimx64.efi test.efi LoadOptionsSize: 0 LoadOptions: (null) FS0:\> shimx64.efi test.efi foo bar LoadOptionsSize: 16 LoadOptions: foo bar --- include/ucs2.h | 27 -------- pe.c | 6 +- shim.c | 200 ++++++++++++++++++++++++++------------------------------- 3 files changed, 92 insertions(+), 141 deletions(-) (limited to 'include') diff --git a/include/ucs2.h b/include/ucs2.h index e43c341f..ee038ce7 100644 --- a/include/ucs2.h +++ b/include/ucs2.h @@ -81,31 +81,4 @@ is_all_nuls(UINT8 *data, UINTN data_size) return true; } -static inline UINTN -__attribute__((__unused__)) -count_ucs2_strings(UINT8 *data, UINTN data_size) -{ - UINTN pos = 0; - UINTN last_nul_pos = 0; - UINTN num_nuls = 0; - UINTN i; - - if (data_size % 2 != 0) - return 0; - - for (i = pos; i < data_size; i++) { - if (i % 2 != 0) { - if (data[i] != 0) - return 0; - } else if (data[i] == 0) { - last_nul_pos = i; - num_nuls++; - } - pos = i; - } - if (num_nuls > 0 && last_nul_pos != pos - 1) - return 0; - return num_nuls; -} - #endif /* SHIM_UCS2_H */ diff --git a/pe.c b/pe.c index 365e32aa..13bc3975 100644 --- a/pe.c +++ b/pe.c @@ -1144,10 +1144,8 @@ handle_image (void *data, unsigned int datasize, li->ImageSize = context.ImageSize; /* Pass the load options to the second stage loader */ - if ( load_options ) { - li->LoadOptions = load_options; - li->LoadOptionsSize = load_options_size; - } + li->LoadOptions = load_options; + li->LoadOptionsSize = load_options_size; if (!found_entry_point) { perror(L"Entry point is not within sections\n"); diff --git a/shim.c b/shim.c index 40e48948..ecf6ee59 100644 --- a/shim.c +++ b/shim.c @@ -1241,9 +1241,13 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) return efi_status; } +/* + * Extract the OptionalData and OptionalData fields from an + * EFI_LOAD_OPTION. + */ static inline EFI_STATUS -get_load_option_optional_data(UINT8 *data, UINTN data_size, - UINT8 **od, UINTN *ods) +get_load_option_optional_data(VOID *data, UINT32 data_size, + VOID **od, UINT32 *ods) { /* * If it's not at least Attributes + FilePathListLength + @@ -1253,7 +1257,8 @@ get_load_option_optional_data(UINT8 *data, UINTN data_size, if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4)) return EFI_INVALID_PARAMETER; - UINT8 *cur = data + sizeof(UINT32); + UINT8 *start = (UINT8 *)data; + UINT8 *cur = start + sizeof(UINT32); UINT16 fplistlen = *(UINT16 *)cur; /* * If there's not enough space for the file path list and the @@ -1263,8 +1268,8 @@ get_load_option_optional_data(UINT8 *data, UINTN data_size, return EFI_INVALID_PARAMETER; cur += sizeof(UINT16); - UINTN limit = data_size - (cur - data) - fplistlen; - UINTN i; + UINT32 limit = data_size - (cur - start) - fplistlen; + UINT32 i; for (i = 0; i < limit ; i++) { /* If the description isn't valid UCS2-LE, it's not valid. */ if (i % 2 != 0) { @@ -1380,6 +1385,57 @@ done: return ret; } +/* + * Split the supplied load options in to a NULL terminated + * string representing the path of the second stage loader, + * and return a pointer to the remaining load options data + * and its remaining size. + * + * This expects the supplied load options to begin with a + * string that is either NULL terminated or terminated with + * a space and some optional data. It will return NULL if + * the supplied load options contains no spaces or NULL + * terminators. + */ +static CHAR16 * +split_load_options(VOID *in, UINT32 in_size, + VOID **remaining, + UINT32 *remaining_size) { + UINTN i; + CHAR16 *arg0 = NULL; + CHAR16 *start = (CHAR16 *)in; + + /* Skip spaces */ + for (i = 0; i < in_size / sizeof(CHAR16); i++) { + if (*start != L' ') + break; + + start++; + } + + in_size -= ((VOID *)start - in); + + /* + * Ensure that the first argument is NULL terminated by + * replacing L' ' with L'\0'. + */ + for (i = 0; i < in_size / sizeof(CHAR16); i++) { + if (start[i] == L' ' || start[i] == L'\0') { + start[i] = L'\0'; + arg0 = (CHAR16 *)start; + break; + } + } + + if (arg0) { + UINTN skip = i + 1; + *remaining_size = in_size - (skip * sizeof(CHAR16)); + *remaining = *remaining_size > 0 ? start + skip : NULL; + } + + return arg0; +} + /* * Check the load options to specify the second stage loader */ @@ -1387,20 +1443,11 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) { EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; - CHAR16 *start = NULL; - UINTN remaining_size = 0; + VOID *remaining = NULL; + UINT32 remaining_size; CHAR16 *loader_str = NULL; - UINTN loader_len = 0; - unsigned int i; - UINTN second_stage_len; - second_stage_len = (StrLen(DEFAULT_LOADER) + 1) * sizeof(CHAR16); - second_stage = AllocatePool(second_stage_len); - if (!second_stage) { - perror(L"Could not allocate %lu bytes\n", second_stage_len); - return EFI_OUT_OF_RESOURCES; - } - StrCpy(second_stage, DEFAULT_LOADER); + second_stage = DEFAULT_LOADER; load_options = NULL; load_options_size = 0; @@ -1499,105 +1546,44 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) return EFI_SUCCESS; /* - * Check and see if this is just a list of strings. If it's an - * EFI_LOAD_OPTION, it'll be 0, since we know EndEntire device path - * won't pass muster as UCS2-LE. - * - * If there are 3 strings, we're launched from the shell most likely, - * But we actually only care about the second one. + * See if this is an EFI_LOAD_OPTION and extract the optional + * data if it is. This will return an error if it is not a valid + * EFI_LOAD_OPTION. */ - UINTN strings = count_ucs2_strings(li->LoadOptions, - li->LoadOptionsSize); - - /* - * In some cases we get strings == 1 because BDS is using L' ' as the - * delimeter: - * 0000:74 00 65 00 73 00 74 00 2E 00 65 00 66 00 69 00 t.e.s.t...e.f.i. - * 0016:20 00 6F 00 6E 00 65 00 20 00 74 00 77 00 6F 00 ..o.n.e...t.w.o. - * 0032:20 00 74 00 68 00 72 00 65 00 65 00 00 00 ..t.h.r.e.e... - * - * If so replace it with NULs since the code already handles that - * case. - */ - if (strings == 1) { - UINT16 *cur = start = li->LoadOptions; - - /* replace L' ' with L'\0' if we find any */ - for (i = 0; i < li->LoadOptionsSize / 2; i++) { - if (cur[i] == L' ') - cur[i] = L'\0'; - } - - /* redo the string count */ - strings = count_ucs2_strings(li->LoadOptions, - li->LoadOptionsSize); - } - - /* - * If it's not string data, try it as an EFI_LOAD_OPTION. - */ - if (strings == 0) { - /* - * We at least didn't find /enough/ strings. See if it works - * as an EFI_LOAD_OPTION. - */ - efi_status = get_load_option_optional_data(li->LoadOptions, - li->LoadOptionsSize, - (UINT8 **)&start, - &loader_len); - if (EFI_ERROR(efi_status)) - return EFI_SUCCESS; - - remaining_size = 0; - } else if (strings >= 2) { + efi_status = get_load_option_optional_data(li->LoadOptions, + li->LoadOptionsSize, + &li->LoadOptions, + &li->LoadOptionsSize); + if (EFI_ERROR(efi_status)) { /* + * it's not an EFI_LOAD_OPTION, so it's probably just a string + * or list of strings. + * * UEFI shell copies the whole line of the command into - * LoadOptions. We ignore the string before the first L'\0', - * i.e. the name of this program. + * LoadOptions. We ignore the first string, i.e. the name of this + * program in this case. */ - UINT16 *cur = li->LoadOptions; - for (i = 1; i < li->LoadOptionsSize / 2; i++) { - if (cur[i - 1] == L'\0') { - start = &cur[i]; - remaining_size = li->LoadOptionsSize - (i * 2); - break; - } + CHAR16 *loader_str = split_load_options(li->LoadOptions, + li->LoadOptionsSize, + &remaining, + &remaining_size); + + if (loader_str && is_our_path(li, loader_str)) { + li->LoadOptions = remaining; + li->LoadOptionsSize = remaining_size; } - - remaining_size -= i * 2 + 2; - } else if (strings == 1 && is_our_path(li, start)) { - /* - * And then I found a version of BDS that gives us our own path - * in LoadOptions: - -77162C58 5c 00 45 00 46 00 49 00 |\.E.F.I.| -77162C60 5c 00 42 00 4f 00 4f 00 54 00 5c 00 42 00 4f 00 |\.B.O.O.T.\.B.O.| -77162C70 4f 00 54 00 58 00 36 00 34 00 2e 00 45 00 46 00 |O.T.X.6.4...E.F.| -77162C80 49 00 00 00 |I...| - - * which is just cruel... So yeah, just don't use it. - */ - return EFI_SUCCESS; } + loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize, + &remaining, &remaining_size); + /* * Set up the name of the alternative loader and the LoadOptions for * the loader */ - if (loader_len > 0) { - /* we might not always have a NULL at the end */ - loader_str = AllocatePool(loader_len + 2); - if (!loader_str) { - perror(L"Failed to allocate loader string\n"); - return EFI_OUT_OF_RESOURCES; - } - - for (i = 0; i < loader_len / 2; i++) - loader_str[i] = start[i]; - loader_str[loader_len/2] = L'\0'; - + if (loader_str) { second_stage = loader_str; - load_options = remaining_size ? start + (loader_len/2) : NULL; + load_options = remaining; load_options_size = remaining_size; } @@ -1777,12 +1763,6 @@ shim_fini(void) unhook_exit(); - /* - * Free the space allocated for the alternative 2nd stage loader - */ - if (load_options_size > 0 && second_stage) - FreePool(second_stage); - console_fini(); } -- cgit v1.2.3 From 3ecfa301f4a8c4c2168f95f27d0313f4158ab53d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 13:11:59 -0400 Subject: shim: rename pause() to wait_for_debug() pause() is a posix function, and having it named the same as this makes it hard to include the asm.h header in some test cases. Signed-off-by: Peter Jones --- include/asm.h | 6 +++--- shim.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm.h b/include/asm.h index 8458d5d2..03b06557 100644 --- a/include/asm.h +++ b/include/asm.h @@ -26,17 +26,17 @@ static inline uint64_t read_counter(void) } #if defined(__x86_64__) || defined(__i386__) || defined(__i686__) -static inline void pause(void) +static inline void wait_for_debug(void) { __asm__ __volatile__("pause"); } #elif defined(__aarch64__) -static inline void pause(void) +static inline void wait_for_debug(void) { __asm__ __volatile__("wfi"); } #else -static inline void pause(void) +static inline void wait_for_debug(void) { uint64_t a, b; int x; diff --git a/shim.c b/shim.c index ecf6ee59..2ae60243 100644 --- a/shim.c +++ b/shim.c @@ -1817,7 +1817,7 @@ debug_hook(void) if (x > 12000) break; #endif - pause(); + wait_for_debug(); } x = 1; } -- cgit v1.2.3 From 32697356c6a99a53bf0027ceb3e348278799b9ef Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 13:10:38 -0400 Subject: test.h: make some of the asserts a little more friendly to pointer types. Signed-off-by: Peter Jones --- include/test.h | 87 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index 012ffc51..1d9c7be3 100644 --- a/include/test.h +++ b/include/test.h @@ -63,24 +63,24 @@ extern int debug; assert(cond); \ }) -#define assert_true_as_expr(a, status, fmt, ...) \ - ({ \ - int rc_ = 0; \ - if (!(a)) { \ - printf("%s:%d:got %lld, expected nonzero " fmt, \ - __func__, __LINE__, (long long)(a), \ - ##__VA_ARGS__); \ - printf("%s:%d:Assertion `%s' failed.\n", __func__, \ - __LINE__, __stringify(!(a))); \ - rc_ = status; \ - } \ - rc_; \ +#define assert_true_as_expr(a, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = 0; \ + if (!(a)) { \ + printf("%s:%d:got %lld, expected nonzero " fmt, \ + __func__, __LINE__, (long long)(uintptr_t)(a), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(!(a))); \ + rc_ = status; \ + } \ + rc_; \ }) #define assert_nonzero_as_expr(a, ...) assert_true_as_expr(a, ##__VA_ARGS__) #define assert_false_as_expr(a, status, fmt, ...) \ ({ \ - int rc_ = 0; \ + __typeof__(status) rc_ = (__typeof__(status))0; \ if (a) { \ printf("%s:%d:got %lld, expected zero " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ @@ -94,7 +94,7 @@ extern int debug; #define assert_positive_as_expr(a, status, fmt, ...) \ ({ \ - int rc_ = 0; \ + __typeof__(status) rc_ = (__typeof__(status))0; \ if ((a) <= 0) { \ printf("%s:%d:got %lld, expected > 0 " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ @@ -107,7 +107,7 @@ extern int debug; #define assert_negative_as_expr(a, status, fmt, ...) \ ({ \ - int rc_ = 0; \ + __typeof__(status) rc_ = (__typeof__(status))0; \ if ((a) >= 0) { \ printf("%s:%d:got %lld, expected < 0 " fmt, __func__, \ __LINE__, (long long)(a), ##__VA_ARGS__); \ @@ -120,7 +120,7 @@ extern int debug; #define assert_equal_as_expr(a, b, status, fmt, ...) \ ({ \ - int rc_ = 0; \ + __typeof__(status) rc_ = (__typeof__(status))0; \ if (!((a) == (b))) { \ printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ ##__VA_ARGS__); \ @@ -133,7 +133,7 @@ extern int debug; #define assert_as_expr(cond, status, fmt, ...) \ ({ \ - int rc_ = 0; \ + __typeof__(status) rc_ = (__typeof__(status))0; \ if (!(cond)) { \ printf("%s:%d:" fmt, __func__, __LINE__, \ ##__VA_ARGS__); \ @@ -144,51 +144,54 @@ extern int debug; rc_; \ }) -#define assert_true_return(a, status, fmt, ...) \ - ({ \ - int rc_ = assert_true_as_expr(a, status, fmt, ##__VA_ARGS__); \ - if (rc_ != 0) \ - return rc_; \ +#define assert_true_return(a, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = \ + assert_true_as_expr(a, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ }) #define assert_nonzero_return(a, ...) assert_true_return(a, ##__VA_ARGS__) -#define assert_false_return(a, status, fmt, ...) \ - ({ \ - int rc_ = assert_false_as_expr(a, status, fmt, ##__VA_ARGS__); \ - if (rc_ != 0) \ - return rc_; \ +#define assert_false_return(a, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = \ + assert_false_as_expr(a, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ }) #define assert_zero_return(a, ...) assert_false_return(a, ##__VA_ARGS__) #define assert_positive_return(a, status, fmt, ...) \ ({ \ - int rc_ = assert_positive_as_expr(a, status, fmt, \ - ##__VA_ARGS__); \ + __typeof__(status) rc_ = assert_positive_as_expr( \ + a, status, fmt, ##__VA_ARGS__); \ if (rc_ != 0) \ return rc_; \ }) #define assert_negative_return(a, status, fmt, ...) \ ({ \ - int rc_ = assert_negative_as_expr(a, status, fmt, \ - ##__VA_ARGS__); \ + __typeof__(status) rc_ = assert_negative_as_expr( \ + a, status, fmt, ##__VA_ARGS__); \ if (rc_ != 0) \ return rc_; \ }) -#define assert_equal_return(a, b, status, fmt, ...) \ - ({ \ - int rc_ = assert_equal_as_expr(a, b, status, fmt, \ - ##__VA_ARGS__); \ - if (rc_ != 0) \ - return rc_; \ +#define assert_equal_return(a, b, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = assert_equal_as_expr( \ + a, b, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ }) -#define assert_return(cond, status, fmt, ...) \ - ({ \ - int rc_ = assert_as_expr(cond, status, fmt, ##__VA_ARGS__); \ - if (rc_ != 0) \ - return rc_; \ +#define assert_return(cond, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = \ + assert_as_expr(cond, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ }) #define assert_goto(cond, label, fmt, ...) \ -- cgit v1.2.3 From 5f08e671e4eb4ec43c1bf667e67f02b7454e13b0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 13:57:40 -0400 Subject: test.h: add some decls for some of the stuff in efilib.h In some test cases, it's useful to be able to call some of the very common stuff in gnu-efi's efilib.h (i.e. CompareGuid()), but including that header itself is too big for me to tackle right now. This patch adds a few more decls to test.h. Signed-off-by: Peter Jones --- include/test.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/test.h b/include/test.h index 1d9c7be3..811f92bf 100644 --- a/include/test.h +++ b/include/test.h @@ -48,6 +48,17 @@ #define FreePool(x) free(x) #define ReallocatePool(old, oldsz, newsz) realloc(old, newsz) +INTN StrnCmp(IN CONST CHAR16 *s1, + IN CONST CHAR16 *s2, + IN UINTN len); +CHAR16 *StrDuplicate(IN CONST CHAR16 *Src); +UINTN StrLen(IN CONST CHAR16 *s1); +UINTN StrSize(IN CONST CHAR16 *s1); +VOID StrCat(IN CHAR16 *Dest, IN CONST CHAR16 *Src); +CHAR16 *DevicePathToStr(EFI_DEVICE_PATH *DevPath); + +#define CompareGuid(a, b) memcmp(a, b, sizeof(a)) + extern int debug; #ifdef dprint #undef dprint -- cgit v1.2.3 From f1ef8dffd374048fbf8dd584923c42e5a784bbf2 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 15 Jul 2021 10:07:14 -0400 Subject: Make test cases link against libefi.a This allows us to use library functions from libefi.a in our test programs. Signed-off-by: Peter Jones --- include/test.mk | 20 +++++++++++++++++--- lib/variables.c | 48 ++++++++++++++++++++++++++---------------------- test.c | 37 ++++++------------------------------- 3 files changed, 49 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 62cf983a..c66b46de 100644 --- a/include/test.mk +++ b/include/test.mk @@ -5,6 +5,8 @@ .SUFFIXES: +include Make.defaults + CC = gcc VALGRIND ?= DEBUG_PRINTS ?= 0 @@ -28,6 +30,15 @@ CFLAGS = -O2 -ggdb -std=gnu11 \ -DSHIM_UNIT_TEST \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" +libefi-test.a : + $(MAKE) -C gnu-efi ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + -f $(TOPDIR)/gnu-efi/Makefile \ + clean lib + mv gnu-efi/$(ARCH)/lib/libefi.a $@ + $(MAKE) -C gnu-efi ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + -f $(TOPDIR)/gnu-efi/Makefile \ + clean + $(wildcard test-*.c) :: %.c : test-random.h $(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h $(patsubst %.c,%.o,$(wildcard test-*.c)) : | test-random.h @@ -36,19 +47,22 @@ test-random.h: dd if=/dev/urandom bs=512 count=17 of=random.bin xxd -i random.bin test-random.h -test-sbat_FILES = csv.c +test-sbat_FILES = csv.c lib/variables.c lib/guid.c +test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID + test-str_FILES = lib/string.c tests := $(patsubst %.c,%,$(wildcard test-*.c)) +$(tests) :: test-% : | libefi-test.a $(tests) :: test-% : test.c test-%.c $(test-%_FILES) - $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) + $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) libefi-test.a $(VALGRIND) ./$@ test : $(tests) clean : - @rm -vf test-random.h random.bin + @rm -vf test-random.h random.bin libefi-test.a all : clean test diff --git a/lib/variables.c b/lib/variables.c index f606e248..ff61d0e2 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -12,6 +12,10 @@ */ #include "shim.h" +extern EFI_SYSTEM_TABLE *ST; +extern EFI_BOOT_SERVICES *BS; +extern EFI_RUNTIME_SERVICES *RT; + EFI_STATUS fill_esl(const EFI_SIGNATURE_DATA *first_sig, const size_t howmany, const EFI_GUID *type, const UINT32 sig_size, @@ -154,7 +158,7 @@ CreateTimeBasedPayload(IN OUT UINTN * DataSize, IN OUT UINT8 ** Data) DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData); ZeroMem(&Time, sizeof(EFI_TIME)); - efi_status = gRT->GetTime(&Time, NULL); + efi_status = RT->GetTime(&Time, NULL); if (EFI_ERROR(efi_status)) { FreePool(NewData); return efi_status; @@ -225,7 +229,7 @@ SetSecureVariable(const CHAR16 * const var, UINT8 *Data, UINTN len, return efi_status; } - efi_status = gRT->SetVariable((CHAR16 *)var, &owner, + efi_status = RT->SetVariable((CHAR16 *)var, &owner, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | @@ -241,8 +245,8 @@ GetOSIndications(void) UINTN DataSize = sizeof(indications); EFI_STATUS efi_status; - efi_status = gRT->GetVariable(L"OsIndicationsSupported", &GV_GUID, - NULL, &DataSize, &indications); + efi_status = RT->GetVariable(L"OsIndicationsSupported", &GV_GUID, + NULL, &DataSize, &indications); if (EFI_ERROR(efi_status)) return 0; @@ -255,15 +259,15 @@ SETOSIndicationsAndReboot(UINT64 indications) UINTN DataSize = sizeof(indications); EFI_STATUS efi_status; - efi_status = gRT->SetVariable(L"OsIndications", &GV_GUID, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_RUNTIME_ACCESS | - EFI_VARIABLE_BOOTSERVICE_ACCESS, - DataSize, &indications); + efi_status = RT->SetVariable(L"OsIndications", &GV_GUID, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, &indications); if (EFI_ERROR(efi_status)) return efi_status; - gRT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); /* does not return */ return EFI_SUCCESS; @@ -280,7 +284,7 @@ get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, *len = 0; - efi_status = gRT->GetVariable((CHAR16 *)var, &owner, NULL, len, NULL); + efi_status = RT->GetVariable((CHAR16 *)var, &owner, NULL, len, NULL); if (efi_status != EFI_BUFFER_TOO_SMALL) { if (!EFI_ERROR(efi_status)) /* this should never happen */ return EFI_PROTOCOL_ERROR; @@ -298,7 +302,7 @@ get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, if (!*data) return EFI_OUT_OF_RESOURCES; - efi_status = gRT->GetVariable((CHAR16 *)var, &owner, attributes, len, *data); + efi_status = RT->GetVariable((CHAR16 *)var, &owner, attributes, len, *data); if (EFI_ERROR(efi_status)) { FreePool(*data); *data = NULL; @@ -341,7 +345,7 @@ EFI_STATUS set_variable(CHAR16 *var, EFI_GUID owner, UINT32 attributes, UINTN datasize, void *data) { - return gRT->SetVariable(var, &owner, attributes, datasize, data); + return RT->SetVariable(var, &owner, attributes, datasize, data); } EFI_STATUS @@ -394,8 +398,8 @@ variable_is_setupmode(int default_return) UINTN DataSize = sizeof(SetupMode); EFI_STATUS efi_status; - efi_status = gRT->GetVariable(L"SetupMode", &GV_GUID, NULL, - &DataSize, &SetupMode); + efi_status = RT->GetVariable(L"SetupMode", &GV_GUID, NULL, + &DataSize, &SetupMode); if (EFI_ERROR(efi_status)) return default_return; @@ -411,8 +415,8 @@ variable_is_secureboot(void) EFI_STATUS efi_status; DataSize = sizeof(SecureBoot); - efi_status = gRT->GetVariable(L"SecureBoot", &GV_GUID, NULL, - &DataSize, &SecureBoot); + efi_status = RT->GetVariable(L"SecureBoot", &GV_GUID, NULL, + &DataSize, &SecureBoot); if (EFI_ERROR(efi_status)) return 0; @@ -445,10 +449,10 @@ variable_enroll_hash(const CHAR16 * const var, EFI_GUID owner, efi_status = SetSecureVariable(var, sig, sizeof(sig), owner, EFI_VARIABLE_APPEND_WRITE, 0); else - efi_status = gRT->SetVariable((CHAR16 *)var, &owner, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_APPEND_WRITE, - sizeof(sig), sig); + efi_status = RT->SetVariable((CHAR16 *)var, &owner, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_APPEND_WRITE, + sizeof(sig), sig); return efi_status; } diff --git a/test.c b/test.c index 9da5cf5b..11e0c642 100644 --- a/test.c +++ b/test.c @@ -22,37 +22,6 @@ LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) return EFI_SUCCESS; } -#ifndef HAVE_STRCMP -INTN -StrCmp(CONST CHAR16 *s1, CONST CHAR16 *s2) { - assert(s1 != NULL); - assert(s2 != NULL); - - int i; - for (i = 0; s1[i] && s2[i]; i++) { - if (s1[i] != s2[i]) - return s2[i] - s1[i]; - } - return 0; -} -#endif - -#ifndef HAVE_STRNCMP -INTN -StrnCmp(CONST CHAR16 *s1, CONST CHAR16 *s2, UINTN len) { - assert(s1 != NULL); - assert(s2 != NULL); - - UINTN i; - for (i = 0; i < len && s1[i] && s2[i]; i++) { - if (s1[i] != s2[i]) - return s2[i] - s1[i]; - - } - return 0; -} -#endif - #ifndef HAVE_GET_VARIABLE_ATTR EFI_STATUS get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, @@ -74,4 +43,10 @@ get_variable(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner) EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; #endif +UINTN EFIAPI +console_print(const CHAR16 *fmt, ...) +{ + return 0; +} + // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 8600b635624c52f24459c82848ae5907992571c7 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 14:24:12 -0400 Subject: test.c: add some simple mock functions for BS->{Allocate,Free}* In some test cases, it may be useful to call libefi.a functions, such as the device path parsing functions, which allocate pages via BS->AllocatePool() or BS->AllocatePages. This patch ads a simple mock implementation of those functions, as well as the EFI_SYSTEM_TABLE, EFI_BOOT_SERVICES, and EFI_RUNTIME_SERVICES variables *ST, *BS, and *RT (respectively), and initializes them before the test cases run. Signed-off-by: Peter Jones --- include/test.h | 4 ++++ test.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) (limited to 'include') diff --git a/include/test.h b/include/test.h index 811f92bf..9a4fdb1b 100644 --- a/include/test.h +++ b/include/test.h @@ -57,6 +57,10 @@ UINTN StrSize(IN CONST CHAR16 *s1); VOID StrCat(IN CHAR16 *Dest, IN CONST CHAR16 *Src); CHAR16 *DevicePathToStr(EFI_DEVICE_PATH *DevPath); +extern EFI_SYSTEM_TABLE *ST; +extern EFI_BOOT_SERVICES *BS; +extern EFI_RUNTIME_SERVICES *RT; + #define CompareGuid(a, b) memcmp(a, b, sizeof(a)) extern int debug; diff --git a/test.c b/test.c index 11e0c642..81578c5a 100644 --- a/test.c +++ b/test.c @@ -15,6 +15,80 @@ int debug = DEFAULT_DEBUG_PRINT_STATE; #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-function" +static EFI_STATUS EFIAPI +mock_efi_allocate_pages(EFI_ALLOCATE_TYPE type, + EFI_MEMORY_TYPE memory_type, + UINTN nmemb, + EFI_PHYSICAL_ADDRESS *memory) +{ + /* + * XXX so far this does not honor the type at all, and there's no + * tracking for memory_type either. + */ + *memory = (EFI_PHYSICAL_ADDRESS)(uintptr_t)calloc(nmemb, 4096); + if ((void *)(uintptr_t)(*memory) == NULL) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +mock_efi_free_pages(EFI_PHYSICAL_ADDRESS memory, + UINTN nmemb) +{ + free((void *)(uintptr_t)memory); + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +mock_efi_allocate_pool(EFI_MEMORY_TYPE pool_type, + UINTN size, + VOID **buf) +{ + *buf = calloc(1, size); + if (*buf == NULL) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +mock_efi_free_pool(void *buf) +{ + free(buf); + + return EFI_SUCCESS; +} + +EFI_BOOT_SERVICES mock_bs = { + .RaiseTPL = NULL, + .RestoreTPL = NULL, + + .AllocatePages = mock_efi_allocate_pages, + .FreePages = mock_efi_free_pages, + .AllocatePool = mock_efi_allocate_pool, + .FreePool = mock_efi_free_pool, +}; + +EFI_RUNTIME_SERVICES mock_rt = { + .Hdr = { 0, }, + .GetVariable = NULL, +}; + +EFI_SYSTEM_TABLE mock_st = { + .Hdr = { 0, }, + .BootServices = &mock_bs, + .RuntimeServices = &mock_rt, +}; + +void CONSTRUCTOR init_efi_system_table(void) +{ + ST = &mock_st; + BS = &mock_bs; + RT = &mock_rt; +} + EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) { -- cgit v1.2.3 From fea0a3fd026f1f4ec82a513269764b8349ffd4a5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 14:56:10 -0400 Subject: test.h: add assert_not_equal_*() This test helper was conspicuously missing, so this patch just adds it at the obvious place. Signed-off-by: Peter Jones --- include/test.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'include') diff --git a/include/test.h b/include/test.h index 9a4fdb1b..49a27a94 100644 --- a/include/test.h +++ b/include/test.h @@ -146,6 +146,19 @@ extern int debug; rc_; \ }) +#define assert_not_equal_as_expr(a, b, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = (__typeof__(status))0; \ + if (!((a) != (b))) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a != b)); \ + rc_ = status; \ + } \ + rc_; \ + }) + #define assert_as_expr(cond, status, fmt, ...) \ ({ \ __typeof__(status) rc_ = (__typeof__(status))0; \ @@ -201,6 +214,14 @@ extern int debug; return rc_; \ }) +#define assert_not_equal_return(a, b, status, fmt, ...) \ + ({ \ + __typeof__(status) rc_ = assert_not_equal_as_expr( \ + a, b, status, fmt, ##__VA_ARGS__); \ + if (rc_ != 0) \ + return rc_; \ + }) + #define assert_return(cond, status, fmt, ...) \ ({ \ __typeof__(status) rc_ = \ @@ -231,6 +252,41 @@ extern int debug; } \ }) +#define assert_not_equal_goto(a, b, label, fmt, ...) \ + ({ \ + if (!((a) != (b))) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), (b), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a != b)); \ + goto label; \ + } \ + }) + +#define assert_true_goto(a, label, fmt, ...) \ + ({ \ + if (!(a)) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(!(a))); \ + goto label; \ + } \ + }) +#define assert_nonzero_goto(a, ...) assert_true_goto(a, ##__VA_ARGS__) + +#define assert_false_goto(a, label, fmt, ...) \ + ({ \ + if (a) { \ + printf("%s:%d:" fmt, __func__, __LINE__, (a), \ + ##__VA_ARGS__); \ + printf("%s:%d:Assertion `%s' failed.\n", __func__, \ + __LINE__, __stringify(a)); \ + goto label; \ + } \ + }) +#define assert_zero_goto(a, ...) assert_false_goto(a, ##__VA_ARGS__) + #define assert_negative_goto(a, label, fmt, ...) \ ({ \ int rc_ = assert_negative_as_expr(a, -1, fmt, ##__VA_ARGS__); \ -- cgit v1.2.3 From cedfa69e6afe381a074d22bdf1e6f2d9e6731038 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 14 Jul 2021 16:36:44 -0400 Subject: test: Add a basic traceback printer Some tests have some complex flows, and it's useful to be able to see the call path when there's a failure. This patch adds a very simple traceback printer, along with changing the test build arguments to include more debug information. The result you get from this traceback printer just gives you a function name and the index into its .txt content, so to use it for more than "which function calls which", you'll need to use eu-addr2line with the output. Signed-off-by: Peter Jones --- include/test.h | 2 ++ include/test.mk | 21 ++++++++++++++++----- test.c | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index 49a27a94..fedeb782 100644 --- a/include/test.h +++ b/include/test.h @@ -69,6 +69,8 @@ extern int debug; #define dprint(fmt, ...) {( if (debug) printf("%s:%d:" fmt, __func__, __LINE__, ##__VA_ARGS__); }) #endif +void print_traceback(int skip); + #define eassert(cond, fmt, ...) \ ({ \ if (!(cond)) { \ diff --git a/include/test.mk b/include/test.mk index c66b46de..23bf805c 100644 --- a/include/test.mk +++ b/include/test.mk @@ -14,14 +14,25 @@ CFLAGS = -O2 -ggdb -std=gnu11 \ -isystem $(TOPDIR)/include/system \ $(EFI_INCLUDES) \ -Iinclude -iquote . \ - -fshort-wchar -flto -fno-builtin \ - -Wall \ - -Wextra \ + -isystem /usr/include \ + -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \ + $(ARCH_CFLAGS) \ + -fshort-wchar \ + -flto \ + -fno-builtin \ + -rdynamic \ + -fno-inline \ + -fno-eliminate-unused-debug-types \ + -fno-eliminate-unused-debug-symbols \ + -gpubnames \ + -grecord-gcc-switches \ + $(DEFAULT_WARNFLAGS) \ -Wsign-compare \ -Wno-deprecated-declarations \ + -Wno-unused-but-set-variable \ + -Wno-unused-variable \ -Wno-pointer-sign \ - -Wno-unused \ - -Werror \ + $(DEFAULT_WERRFLAGS) \ -Werror=nonnull \ $(shell $(CC) -Werror=nonnull-compare -E -x c /dev/null >/dev/null 2>&1 && echo -Werror=nonnull-compare) \ $(ARCH_DEFINES) \ diff --git a/test.c b/test.c index 81578c5a..d9902ebc 100644 --- a/test.c +++ b/test.c @@ -9,9 +9,35 @@ #endif #include "shim.h" +#include +#include +#include + +#define BT_BUF_SIZE (4096/sizeof(void *)) + +static void *frames[BT_BUF_SIZE] = { 0, }; + UINT8 in_protocol = 0; int debug = DEFAULT_DEBUG_PRINT_STATE; +void +print_traceback(int skip) +{ + int nptrs; + char **strings; + + nptrs = backtrace(frames, BT_BUF_SIZE); + if (nptrs < skip) + return; + + strings = backtrace_symbols(frames, nptrs); + for (int i = skip; strings != NULL && i < nptrs; i++) { + printf("%p %s\n", (void *)frames[i], strings[i]); + } + if (strings) + free(strings); +} + #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-function" -- cgit v1.2.3 From 9e4f38abe61b600826360c14729cc54ea3ec7c3c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 14:22:55 -0400 Subject: shim: move the bulk of set_second_stage() to its own file This moves set_second_stage() and some of the helper functions it uses out of shim.c, so that it's easier to write test cases for. Signed-off-by: Peter Jones --- Makefile | 2 +- include/load-options.h | 20 +++ load-options.c | 445 +++++++++++++++++++++++++++++++++++++++++++++++++ shim.c | 424 +--------------------------------------------- shim.h | 1 + 5 files changed, 470 insertions(+), 422 deletions(-) create mode 100644 include/load-options.h create mode 100644 load-options.c (limited to 'include') diff --git a/Makefile b/Makefile index d80aea82..050c921d 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o +OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer ORIG_SOURCES = shim.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o diff --git a/include/load-options.h b/include/load-options.h new file mode 100644 index 00000000..d2bee3bb --- /dev/null +++ b/include/load-options.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * load-options.h - all the stuff we need to parse the load options + */ + +#ifndef SHIM_ARGV_H_ +#define SHIM_ARGV_H_ + +EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, + CHAR16 *ImagePath, + CHAR16 **PathName); + +EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li); + +extern CHAR16 *second_stage; +extern void *load_options; +extern UINT32 load_options_size; + +#endif /* !SHIM_ARGV_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/load-options.c b/load-options.c new file mode 100644 index 00000000..6126358d --- /dev/null +++ b/load-options.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * load-options.c - all the stuff we need to parse the load options + */ + +#include "shim.h" + +CHAR16 *second_stage; +void *load_options; +UINT32 load_options_size; + +/* + * Generate the path of an executable given shim's path and the name + * of the executable + */ +EFI_STATUS +generate_path_from_image_path(EFI_LOADED_IMAGE *li, + CHAR16 *ImagePath, + CHAR16 **PathName) +{ + EFI_DEVICE_PATH *devpath; + unsigned int i; + int j, last = -1; + unsigned int pathlen = 0; + EFI_STATUS efi_status = EFI_SUCCESS; + CHAR16 *bootpath; + + /* + * Suuuuper lazy technique here, but check and see if this is a full + * path to something on the ESP. Backwards compatibility demands + * that we don't just use \\, because we (not particularly brightly) + * used to require that the relative file path started with that. + * + * If it is a full path, don't try to merge it with the directory + * from our Loaded Image handle. + */ + if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) { + *PathName = StrDuplicate(ImagePath); + if (!*PathName) { + perror(L"Failed to allocate path buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; + } + + devpath = li->FilePath; + + bootpath = DevicePathToStr(devpath); + + pathlen = StrLen(bootpath); + + /* + * DevicePathToStr() concatenates two nodes with '/'. + * Convert '/' to '\\'. + */ + for (i = 0; i < pathlen; i++) { + if (bootpath[i] == '/') + bootpath[i] = '\\'; + } + + for (i=pathlen; i>0; i--) { + if (bootpath[i] == '\\' && bootpath[i-1] == '\\') + bootpath[i] = '/'; + else if (last == -1 && bootpath[i] == '\\') + last = i; + } + + if (last == -1 && bootpath[0] == '\\') + last = 0; + bootpath[last+1] = '\0'; + + if (last > 0) { + for (i = 0, j = 0; bootpath[i] != '\0'; i++) { + if (bootpath[i] != '/') { + bootpath[j] = bootpath[i]; + j++; + } + } + bootpath[j] = '\0'; + } + + for (i = 0, last = 0; i < StrLen(ImagePath); i++) + if (ImagePath[i] == '\\') + last = i + 1; + + ImagePath = ImagePath + last; + *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath)); + + if (!*PathName) { + perror(L"Failed to allocate path buffer\n"); + efi_status = EFI_OUT_OF_RESOURCES; + goto error; + } + + *PathName[0] = '\0'; + if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath))) + StrCat(*PathName, bootpath); + StrCat(*PathName, ImagePath); + +error: + FreePool(bootpath); + + return efi_status; +} + +/* + * Extract the OptionalData and OptionalData fields from an + * EFI_LOAD_OPTION. + */ +static inline EFI_STATUS +get_load_option_optional_data(VOID *data, UINT32 data_size, + VOID **od, UINT32 *ods) +{ + /* + * If it's not at least Attributes + FilePathListLength + + * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't + * be valid. + */ + if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4)) + return EFI_INVALID_PARAMETER; + + UINT8 *start = (UINT8 *)data; + UINT8 *cur = start + sizeof(UINT32); + UINT16 fplistlen = *(UINT16 *)cur; + /* + * If there's not enough space for the file path list and the + * smallest possible description (L""), it's not valid. + */ + if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4)) + return EFI_INVALID_PARAMETER; + + cur += sizeof(UINT16); + UINT32 limit = data_size - (cur - start) - fplistlen; + UINT32 i; + for (i = 0; i < limit ; i++) { + /* If the description isn't valid UCS2-LE, it's not valid. */ + if (i % 2 != 0) { + if (cur[i] != 0) + return EFI_INVALID_PARAMETER; + } else if (cur[i] == 0) { + /* we've found the end */ + i++; + if (i >= limit || cur[i] != 0) + return EFI_INVALID_PARAMETER; + break; + } + } + i++; + if (i > limit) + return EFI_INVALID_PARAMETER; + + /* + * If i is limit, we know the rest of this is the FilePathList and + * there's no optional data. So just bail now. + */ + if (i == limit) { + *od = NULL; + *ods = 0; + return EFI_SUCCESS; + } + + cur += i; + limit -= i; + limit += fplistlen; + i = 0; + while (limit - i >= 4) { + struct { + UINT8 type; + UINT8 subtype; + UINT16 len; + } dp = { + .type = cur[i], + .subtype = cur[i+1], + /* + * it's a little endian UINT16, but we're not + * guaranteed alignment is sane, so we can't just + * typecast it directly. + */ + .len = (cur[i+3] << 8) | cur[i+2], + }; + + /* + * We haven't found an EndEntire, so this has to be a valid + * EFI_DEVICE_PATH in order for the data to be valid. That + * means it has to fit, and it can't be smaller than 4 bytes. + */ + if (dp.len < 4 || dp.len > limit) + return EFI_INVALID_PARAMETER; + + /* + * see if this is an EndEntire node... + */ + if (dp.type == 0x7f && dp.subtype == 0xff) { + /* + * if we've found the EndEntire node, it must be 4 + * bytes + */ + if (dp.len != 4) + return EFI_INVALID_PARAMETER; + + i += dp.len; + break; + } + + /* + * It's just some random DP node; skip it. + */ + i += dp.len; + } + if (i != fplistlen) + return EFI_INVALID_PARAMETER; + + /* + * if there's any space left, it's "optional data" + */ + *od = cur + i; + *ods = limit - i; + return EFI_SUCCESS; +} + +static int +is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path) +{ + CHAR16 *dppath = NULL; + CHAR16 *PathName = NULL; + EFI_STATUS efi_status; + int ret = 1; + + dppath = DevicePathToStr(li->FilePath); + if (!dppath) + return 0; + + efi_status = generate_path_from_image_path(li, path, &PathName); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to generate path %s: %r\n", path, + efi_status); + goto done; + } + + dprint(L"dppath: %s\n", dppath); + dprint(L"path: %s\n", path); + if (StrnCaseCmp(dppath, PathName, StrLen(dppath))) + ret = 0; + +done: + FreePool(dppath); + FreePool(PathName); + return ret; +} + +/* + * Split the supplied load options in to a NULL terminated + * string representing the path of the second stage loader, + * and return a pointer to the remaining load options data + * and its remaining size. + * + * This expects the supplied load options to begin with a + * string that is either NULL terminated or terminated with + * a space and some optional data. It will return NULL if + * the supplied load options contains no spaces or NULL + * terminators. + */ +static CHAR16 * +split_load_options(VOID *in, UINT32 in_size, + VOID **remaining, + UINT32 *remaining_size) { + UINTN i; + CHAR16 *arg0 = NULL; + CHAR16 *start = (CHAR16 *)in; + + /* Skip spaces */ + for (i = 0; i < in_size / sizeof(CHAR16); i++) { + if (*start != L' ') + break; + + start++; + } + + in_size -= ((VOID *)start - in); + + /* + * Ensure that the first argument is NULL terminated by + * replacing L' ' with L'\0'. + */ + for (i = 0; i < in_size / sizeof(CHAR16); i++) { + if (start[i] == L' ' || start[i] == L'\0') { + start[i] = L'\0'; + arg0 = (CHAR16 *)start; + break; + } + } + + if (arg0) { + UINTN skip = i + 1; + *remaining_size = in_size - (skip * sizeof(CHAR16)); + *remaining = *remaining_size > 0 ? start + skip : NULL; + } + + return arg0; +} + +/* + * Check the load options to specify the second stage loader + */ +EFI_STATUS +parse_load_options(EFI_LOADED_IMAGE *li) +{ + EFI_STATUS efi_status; + VOID *remaining = NULL; + UINT32 remaining_size; + CHAR16 *loader_str = NULL; + + /* Sanity check since we make several assumptions about the length */ + if (li->LoadOptionsSize % 2 != 0) + return EFI_INVALID_PARAMETER; + + /* So, load options are a giant pain in the ass. If we're invoked + * from the EFI shell, we get something like this: + +00000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| +00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.| +00000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .| +00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| +00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.| +00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .| +00000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...| + + * + * which is just some paths rammed together separated by a UCS-2 NUL. + * But if we're invoked from BDS, we get something more like: + * + +00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.| +00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.| +00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.| +00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........| +00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.| +00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....| +00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.| +00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.| +00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...| +00000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.| +000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.| +000000b0 00 00 |..| + + * + * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably. + * In short, the UEFI shell is still a useless piece of junk. + * + * But then on some versions of BDS, we get: + +00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.| +00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...| +0000001a + + * which as you can see is one perfectly normal UCS2-EL string + * containing the load option from the Boot#### variable. + * + * We also sometimes find a guid or partial guid at the end, because + * BDS will add that, but we ignore that here. + */ + + /* + * Maybe there just aren't any options... + */ + if (li->LoadOptionsSize == 0) + return EFI_SUCCESS; + + /* + * In either case, we've got to have at least a UCS2 NUL... + */ + if (li->LoadOptionsSize < 2) + return EFI_BAD_BUFFER_SIZE; + + /* + * Some awesome versions of BDS will add entries for Linux. On top + * of that, some versions of BDS will "tag" any Boot#### entries they + * create by putting a GUID at the very end of the optional data in + * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who + * tries to actually *use* the optional data for anything. Why they + * did this instead of adding a flag to the spec to /say/ it's + * created by BDS, I do not know. For shame. + * + * Anyway, just nerf that out from the start. It's always just + * garbage at the end. + */ + if (li->LoadOptionsSize > 16) { + if (CompareGuid((EFI_GUID *)(li->LoadOptions + + (li->LoadOptionsSize - 16)), + &BDS_GUID) == 0) + li->LoadOptionsSize -= 16; + } + + /* + * Apparently sometimes we get L"\0\0"? Which isn't useful at all. + */ + if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize)) + return EFI_SUCCESS; + + /* + * See if this is an EFI_LOAD_OPTION and extract the optional + * data if it is. This will return an error if it is not a valid + * EFI_LOAD_OPTION. + */ + efi_status = get_load_option_optional_data(li->LoadOptions, + li->LoadOptionsSize, + &li->LoadOptions, + &li->LoadOptionsSize); + if (EFI_ERROR(efi_status)) { + /* + * it's not an EFI_LOAD_OPTION, so it's probably just a string + * or list of strings. + * + * UEFI shell copies the whole line of the command into + * LoadOptions. We ignore the first string, i.e. the name of this + * program in this case. + */ + CHAR16 *loader_str = split_load_options(li->LoadOptions, + li->LoadOptionsSize, + &remaining, + &remaining_size); + + if (loader_str && is_our_path(li, loader_str)) { + li->LoadOptions = remaining; + li->LoadOptionsSize = remaining_size; + } + } + + loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize, + &remaining, &remaining_size); + + /* + * Set up the name of the alternative loader and the LoadOptions for + * the loader + */ + if (loader_str) { + second_stage = loader_str; + load_options = remaining; + load_options_size = remaining_size; + } + + return EFI_SUCCESS; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 2ae60243..94a51768 100644 --- a/shim.c +++ b/shim.c @@ -40,10 +40,6 @@ static EFI_HANDLE global_image_handle; static EFI_LOADED_IMAGE *shim_li; static EFI_LOADED_IMAGE shim_li_bak; -static CHAR16 *second_stage; -void *load_options; -UINT32 load_options_size; - list_t sbat_var; /* @@ -788,99 +784,6 @@ error: return ret; } -/* - * Generate the path of an executable given shim's path and the name - * of the executable - */ -static EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, - CHAR16 *ImagePath, - CHAR16 **PathName) -{ - EFI_DEVICE_PATH *devpath; - unsigned int i; - int j, last = -1; - unsigned int pathlen = 0; - EFI_STATUS efi_status = EFI_SUCCESS; - CHAR16 *bootpath; - - /* - * Suuuuper lazy technique here, but check and see if this is a full - * path to something on the ESP. Backwards compatibility demands - * that we don't just use \\, because we (not particularly brightly) - * used to require that the relative file path started with that. - * - * If it is a full path, don't try to merge it with the directory - * from our Loaded Image handle. - */ - if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) { - *PathName = StrDuplicate(ImagePath); - if (!*PathName) { - perror(L"Failed to allocate path buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - return EFI_SUCCESS; - } - - devpath = li->FilePath; - - bootpath = DevicePathToStr(devpath); - - pathlen = StrLen(bootpath); - - /* - * DevicePathToStr() concatenates two nodes with '/'. - * Convert '/' to '\\'. - */ - for (i = 0; i < pathlen; i++) { - if (bootpath[i] == '/') - bootpath[i] = '\\'; - } - - for (i=pathlen; i>0; i--) { - if (bootpath[i] == '\\' && bootpath[i-1] == '\\') - bootpath[i] = '/'; - else if (last == -1 && bootpath[i] == '\\') - last = i; - } - - if (last == -1 && bootpath[0] == '\\') - last = 0; - bootpath[last+1] = '\0'; - - if (last > 0) { - for (i = 0, j = 0; bootpath[i] != '\0'; i++) { - if (bootpath[i] != '/') { - bootpath[j] = bootpath[i]; - j++; - } - } - bootpath[j] = '\0'; - } - - for (i = 0, last = 0; i < StrLen(ImagePath); i++) - if (ImagePath[i] == '\\') - last = i + 1; - - ImagePath = ImagePath + last; - *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath)); - - if (!*PathName) { - perror(L"Failed to allocate path buffer\n"); - efi_status = EFI_OUT_OF_RESOURCES; - goto error; - } - - *PathName[0] = '\0'; - if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath))) - StrCat(*PathName, bootpath); - StrCat(*PathName, ImagePath); - -error: - FreePool(bootpath); - - return efi_status; -} - /* * Open the second stage bootloader and read it into a buffer */ @@ -1241,201 +1144,6 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) return efi_status; } -/* - * Extract the OptionalData and OptionalData fields from an - * EFI_LOAD_OPTION. - */ -static inline EFI_STATUS -get_load_option_optional_data(VOID *data, UINT32 data_size, - VOID **od, UINT32 *ods) -{ - /* - * If it's not at least Attributes + FilePathListLength + - * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't - * be valid. - */ - if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4)) - return EFI_INVALID_PARAMETER; - - UINT8 *start = (UINT8 *)data; - UINT8 *cur = start + sizeof(UINT32); - UINT16 fplistlen = *(UINT16 *)cur; - /* - * If there's not enough space for the file path list and the - * smallest possible description (L""), it's not valid. - */ - if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4)) - return EFI_INVALID_PARAMETER; - - cur += sizeof(UINT16); - UINT32 limit = data_size - (cur - start) - fplistlen; - UINT32 i; - for (i = 0; i < limit ; i++) { - /* If the description isn't valid UCS2-LE, it's not valid. */ - if (i % 2 != 0) { - if (cur[i] != 0) - return EFI_INVALID_PARAMETER; - } else if (cur[i] == 0) { - /* we've found the end */ - i++; - if (i >= limit || cur[i] != 0) - return EFI_INVALID_PARAMETER; - break; - } - } - i++; - if (i > limit) - return EFI_INVALID_PARAMETER; - - /* - * If i is limit, we know the rest of this is the FilePathList and - * there's no optional data. So just bail now. - */ - if (i == limit) { - *od = NULL; - *ods = 0; - return EFI_SUCCESS; - } - - cur += i; - limit -= i; - limit += fplistlen; - i = 0; - while (limit - i >= 4) { - struct { - UINT8 type; - UINT8 subtype; - UINT16 len; - } dp = { - .type = cur[i], - .subtype = cur[i+1], - /* - * it's a little endian UINT16, but we're not - * guaranteed alignment is sane, so we can't just - * typecast it directly. - */ - .len = (cur[i+3] << 8) | cur[i+2], - }; - - /* - * We haven't found an EndEntire, so this has to be a valid - * EFI_DEVICE_PATH in order for the data to be valid. That - * means it has to fit, and it can't be smaller than 4 bytes. - */ - if (dp.len < 4 || dp.len > limit) - return EFI_INVALID_PARAMETER; - - /* - * see if this is an EndEntire node... - */ - if (dp.type == 0x7f && dp.subtype == 0xff) { - /* - * if we've found the EndEntire node, it must be 4 - * bytes - */ - if (dp.len != 4) - return EFI_INVALID_PARAMETER; - - i += dp.len; - break; - } - - /* - * It's just some random DP node; skip it. - */ - i += dp.len; - } - if (i != fplistlen) - return EFI_INVALID_PARAMETER; - - /* - * if there's any space left, it's "optional data" - */ - *od = cur + i; - *ods = limit - i; - return EFI_SUCCESS; -} - -static int is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path) -{ - CHAR16 *dppath = NULL; - CHAR16 *PathName = NULL; - EFI_STATUS efi_status; - int ret = 1; - - dppath = DevicePathToStr(li->FilePath); - if (!dppath) - return 0; - - efi_status = generate_path_from_image_path(li, path, &PathName); - if (EFI_ERROR(efi_status)) { - perror(L"Unable to generate path %s: %r\n", path, - efi_status); - goto done; - } - - dprint(L"dppath: %s\n", dppath); - dprint(L"path: %s\n", path); - if (StrnCaseCmp(dppath, PathName, StrLen(dppath))) - ret = 0; - -done: - FreePool(dppath); - FreePool(PathName); - return ret; -} - -/* - * Split the supplied load options in to a NULL terminated - * string representing the path of the second stage loader, - * and return a pointer to the remaining load options data - * and its remaining size. - * - * This expects the supplied load options to begin with a - * string that is either NULL terminated or terminated with - * a space and some optional data. It will return NULL if - * the supplied load options contains no spaces or NULL - * terminators. - */ -static CHAR16 * -split_load_options(VOID *in, UINT32 in_size, - VOID **remaining, - UINT32 *remaining_size) { - UINTN i; - CHAR16 *arg0 = NULL; - CHAR16 *start = (CHAR16 *)in; - - /* Skip spaces */ - for (i = 0; i < in_size / sizeof(CHAR16); i++) { - if (*start != L' ') - break; - - start++; - } - - in_size -= ((VOID *)start - in); - - /* - * Ensure that the first argument is NULL terminated by - * replacing L' ' with L'\0'. - */ - for (i = 0; i < in_size / sizeof(CHAR16); i++) { - if (start[i] == L' ' || start[i] == L'\0') { - start[i] = L'\0'; - arg0 = (CHAR16 *)start; - break; - } - } - - if (arg0) { - UINTN skip = i + 1; - *remaining_size = in_size - (skip * sizeof(CHAR16)); - *remaining = *remaining_size > 0 ? start + skip : NULL; - } - - return arg0; -} - /* * Check the load options to specify the second stage loader */ @@ -1443,9 +1151,6 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) { EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; - VOID *remaining = NULL; - UINT32 remaining_size; - CHAR16 *loader_str = NULL; second_stage = DEFAULT_LOADER; load_options = NULL; @@ -1458,133 +1163,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) return efi_status; } - /* Sanity check since we make several assumptions about the length */ - if (li->LoadOptionsSize % 2 != 0) - return EFI_INVALID_PARAMETER; - - /* So, load options are a giant pain in the ass. If we're invoked - * from the EFI shell, we get something like this: - -00000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| -00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.| -00000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .| -00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| -00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.| -00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .| -00000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...| - - * - * which is just some paths rammed together separated by a UCS-2 NUL. - * But if we're invoked from BDS, we get something more like: - * - -00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.| -00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.| -00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.| -00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........| -00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.| -00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....| -00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.| -00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.| -00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...| -00000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.| -000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.| -000000b0 00 00 |..| - - * - * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably. - * In short, the UEFI shell is still a useless piece of junk. - * - * But then on some versions of BDS, we get: - -00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.| -00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...| -0000001a - - * which as you can see is one perfectly normal UCS2-EL string - * containing the load option from the Boot#### variable. - * - * We also sometimes find a guid or partial guid at the end, because - * BDS will add that, but we ignore that here. - */ - - /* - * Maybe there just aren't any options... - */ - if (li->LoadOptionsSize == 0) - return EFI_SUCCESS; - - /* - * In either case, we've got to have at least a UCS2 NUL... - */ - if (li->LoadOptionsSize < 2) - return EFI_BAD_BUFFER_SIZE; - - /* - * Some awesome versions of BDS will add entries for Linux. On top - * of that, some versions of BDS will "tag" any Boot#### entries they - * create by putting a GUID at the very end of the optional data in - * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who - * tries to actually *use* the optional data for anything. Why they - * did this instead of adding a flag to the spec to /say/ it's - * created by BDS, I do not know. For shame. - * - * Anyway, just nerf that out from the start. It's always just - * garbage at the end. - */ - if (li->LoadOptionsSize > 16) { - if (CompareGuid((EFI_GUID *)(li->LoadOptions - + (li->LoadOptionsSize - 16)), - &BDS_GUID) == 0) - li->LoadOptionsSize -= 16; - } - - /* - * Apparently sometimes we get L"\0\0"? Which isn't useful at all. - */ - if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize)) - return EFI_SUCCESS; - - /* - * See if this is an EFI_LOAD_OPTION and extract the optional - * data if it is. This will return an error if it is not a valid - * EFI_LOAD_OPTION. - */ - efi_status = get_load_option_optional_data(li->LoadOptions, - li->LoadOptionsSize, - &li->LoadOptions, - &li->LoadOptionsSize); + efi_status = parse_load_options(li); if (EFI_ERROR(efi_status)) { - /* - * it's not an EFI_LOAD_OPTION, so it's probably just a string - * or list of strings. - * - * UEFI shell copies the whole line of the command into - * LoadOptions. We ignore the first string, i.e. the name of this - * program in this case. - */ - CHAR16 *loader_str = split_load_options(li->LoadOptions, - li->LoadOptionsSize, - &remaining, - &remaining_size); - - if (loader_str && is_our_path(li, loader_str)) { - li->LoadOptions = remaining; - li->LoadOptionsSize = remaining_size; - } - } - - loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize, - &remaining, &remaining_size); - - /* - * Set up the name of the alternative loader and the LoadOptions for - * the loader - */ - if (loader_str) { - second_stage = loader_str; - load_options = remaining; - load_options_size = remaining_size; + perror (L"Failed to get load options: %r\n", efi_status); + return efi_status; } return EFI_SUCCESS; diff --git a/shim.h b/shim.h index 69ad2cc3..94b48bcc 100644 --- a/shim.h +++ b/shim.h @@ -167,6 +167,7 @@ #include "include/httpboot.h" #include "include/ip4config2.h" #include "include/ip6config.h" +#include "include/load-options.h" #include "include/netboot.h" #include "include/passwordcrypt.h" #include "include/peimage.h" -- cgit v1.2.3 From 3ea1f37c70c571537c92e2be0d78a1ad4629af8f Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 2 Jul 2021 14:36:58 -0400 Subject: Add a tester for parse_load_options() This adds tests for all the cases we've documented in the set_second_stage() comments. Each test checks that all of second_stage, loader_str, and loader_str_size are set correctly. Note that this adds a dependency on libefivar to build device paths to test against. Signed-off-by: Peter Jones --- include/test.mk | 16 +++- load-options.c | 8 +- test-load-options.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 test-load-options.c (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 23bf805c..68230b20 100644 --- a/include/test.mk +++ b/include/test.mk @@ -10,7 +10,8 @@ include Make.defaults CC = gcc VALGRIND ?= DEBUG_PRINTS ?= 0 -CFLAGS = -O2 -ggdb -std=gnu11 \ +OPTIMIZATIONS=-O2 -ggdb +CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -isystem $(TOPDIR)/include/system \ $(EFI_INCLUDES) \ -Iinclude -iquote . \ @@ -50,13 +51,19 @@ libefi-test.a : -f $(TOPDIR)/gnu-efi/Makefile \ clean +test-random.h: + dd if=/dev/urandom bs=512 count=17 of=random.bin + xxd -i random.bin test-random.h + $(wildcard test-*.c) :: %.c : test-random.h $(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h $(patsubst %.c,%.o,$(wildcard test-*.c)) : | test-random.h -test-random.h: - dd if=/dev/urandom bs=512 count=17 of=random.bin - xxd -i random.bin test-random.h +test-load-options_FILES = lib/guid.c \ + libefi-test.a \ + -lefivar +test-load-options :: libefi-test.a +test-load-options : CFLAGS+=-DHAVE_SHIM_LOCK_GUID test-sbat_FILES = csv.c lib/variables.c lib/guid.c test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID @@ -66,6 +73,7 @@ test-str_FILES = lib/string.c tests := $(patsubst %.c,%,$(wildcard test-*.c)) $(tests) :: test-% : | libefi-test.a + $(tests) :: test-% : test.c test-%.c $(test-%_FILES) $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) libefi-test.a $(VALGRIND) ./$@ diff --git a/load-options.c b/load-options.c index 6126358d..480832c1 100644 --- a/load-options.c +++ b/load-options.c @@ -415,10 +415,10 @@ parse_load_options(EFI_LOADED_IMAGE *li) * LoadOptions. We ignore the first string, i.e. the name of this * program in this case. */ - CHAR16 *loader_str = split_load_options(li->LoadOptions, - li->LoadOptionsSize, - &remaining, - &remaining_size); + loader_str = split_load_options(li->LoadOptions, + li->LoadOptionsSize, + &remaining, + &remaining_size); if (loader_str && is_our_path(li, loader_str)) { li->LoadOptions = remaining; diff --git a/test-load-options.c b/test-load-options.c new file mode 100644 index 00000000..daf02d93 --- /dev/null +++ b/test-load-options.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-argv.c - test our loader_opts parsing + */ +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic error "-Wnonnull" +#pragma GCC diagnostic error "-Wunused-function" + +#pragma GCC diagnostic warning "-Wcpp" + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif + +#include "shim.h" + +#include +#include +#include + +EFI_DEVICE_PATH * +make_file_dp(char *filename) +{ + void *filedp = NULL; + size_t filedpsz = 0, filedpneeded = 0; + + filedpneeded = efidp_make_file(filedp, filedpneeded, filename); + assert_positive_return(filedpneeded, NULL, "\n"); + + filedp = calloc(1, filedpneeded + 4); + assert_nonzero_return(filedp, NULL, "\n"); + + filedpsz = efidp_make_file(filedp, filedpneeded, filename); + assert_equal_goto(filedpsz, filedpneeded, err, "got %zu expected %zu\n"); + + efidp_make_end_entire((uint8_t *)filedp + filedpneeded, 4); + + return filedp; +err: + free(filedp); + return NULL; +} + +int +test_parse_load_options(char *load_option_data, + size_t load_option_data_size, + char *file_path, + CHAR16 *target_second_stage, + char *target_remaining, + size_t target_remaining_size) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_LOADED_IMAGE li = { + .LoadOptions = load_option_data, + .LoadOptionsSize = load_option_data_size, + .FilePath = make_file_dp(file_path), + }; + CHAR16 *dummy_second_stage = calloc(1, 8); + int rc = -1; + + assert_nonzero_goto(li.FilePath, err, "\n"); + assert_nonzero_goto(dummy_second_stage, err, "\n"); + + StrCat(dummy_second_stage, L"foo"); + second_stage = dummy_second_stage; + + status = parse_load_options(&li); + assert_false_goto(EFI_ERROR(status), err, "\n"); + + assert_nonzero_goto(second_stage, err, "\n"); + assert_not_equal_goto(second_stage, dummy_second_stage, err, "%p == %p\n"); + assert_zero_goto(StrnCmp(second_stage, target_second_stage, 90), + err_print_second_stage, "%d != 0\n"); + + assert_equal_goto(load_options_size, target_remaining_size, err_remaining, "%zu != %zu\n"); + assert_equal_goto(load_options, target_remaining, err_remaining, "%p != %p\n"); + assert_zero_goto(memcmp(load_options, target_remaining, load_options_size), err_remaining, "\n"); + + rc = 0; +err_remaining: + if (rc != 0) { + printf("expected remaining:%p\n", target_remaining); + for (size_t i = 0; i < target_remaining_size; i++) + printf("0x%02hhx ", target_remaining[i]); + printf("\n"); + printf("actual remaining:%p\n", load_options); + for (size_t i = 0; i < load_options_size; i++) + printf("0x%02hhx ", ((char *)load_options)[i]); + printf("\n"); + } +err_print_second_stage: + if (rc != 0) { + printf("second stage:\""); + for(int i = 0; second_stage[i] != 0; i++) + printf("%c", second_stage[i]); + printf("\"\nexpected:\""); + for(int i = 0; target_second_stage[i] != 0; i++) + printf("%c", target_second_stage[i]); + printf("\"\n"); + } +err: + if (rc != 0) { + print_traceback(0); + } + if (li.FilePath) { + free(li.FilePath); + } + if (dummy_second_stage && dummy_second_stage != second_stage) { + free(dummy_second_stage); + } + second_stage = NULL; + + return rc; +} + +int __attribute__((__flatten__)) +test_efi_shell_0(void) +{ +/* +00000000 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| +00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.| +00000020 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 20 00 |x.6.4...e.f.i. .| +00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.| +00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.| +00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 69 00 |d.a.t.e...e.f.i.| +00000060 20 00 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 | ...f.s.0.:.\...| +*/ + + char load_option_data[] = { + 0x5c, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, + 0x5c, 0x00, 0x66, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x5c, 0x00, + 0x73, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2e, 0x00, + 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, 0x20, 0x00, + 0x5c, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, + 0x5c, 0x00, 0x66, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x5c, 0x00, + 0x66, 0x00, 0x77, 0x00, 0x75, 0x00, 0x70, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x66, 0x00, 0x73, 0x00, + 0x30, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x00, 0x00, + }; + size_t load_option_data_size = sizeof(load_option_data); + char *remaining = &load_option_data[sizeof(load_option_data)-14]; + size_t remaining_size = 14; + + return test_parse_load_options(load_option_data, + load_option_data_size, + "\\EFI\\fedora\\shimx64.efi", + L"\\EFI\\fedora\\fwupdate.efi", + remaining, remaining_size); +} + +int __attribute__((__flatten__)) +test_bds_0(void) +{ +/* +00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.| +00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.| +00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.| +00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........| +00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.| +00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....| +00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.\.f.e.| +00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |d.o.r.a.\.s.h.i.| +00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |m.x.6.4...e.f.i.| +00000090 00 00 7f ff 04 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.| +000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.| +000000b0 00 00 |..| +*/ + char load_option_data [] = { + 0x01, 0x00, 0x00, 0x00, 0x62, 0x00, 0x4c, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x20, 0x00, 0x46, 0x00, 0x69, 0x00, 0x72, 0x00, + 0x6d, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x70, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x40, 0x01, 0x2a, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9e, 0x55, 0xbf, + 0x04, 0x57, 0xf2, 0x4f, 0xb4, 0x4a, 0xed, 0x26, + 0x4a, 0x40, 0x6a, 0x94, 0x02, 0x02, 0x04, 0x04, + 0x34, 0x00, 0x5c, 0x00, 0x45, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x5c, 0x00, 0x66, 0x00, 0x65, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x5c, 0x00, 0x73, 0x00, 0x68, 0x00, 0x69, 0x00, + 0x6d, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, + 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x00, 0x00, 0x7f, 0xff, 0x04, 0x00, 0x20, 0x00, + 0x5c, 0x00, 0x66, 0x00, 0x77, 0x00, 0x75, 0x00, + 0x70, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, + 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x00, 0x00 + }; + size_t load_option_data_size = sizeof(load_option_data); + + return test_parse_load_options(load_option_data, + load_option_data_size, + "\\EFI\\fedora\\shimx64.efi", + L"\\fwupx64.efi", + NULL, 0); +} + +int __attribute__((__flatten__)) +test_bds_1(void) +{ +/* +00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.| +00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...| +0000001a +*/ + char load_option_data [] = { + 0x5c, 0x00, 0x66, 0x00, 0x77, 0x00, 0x75, 0x00, + 0x70, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, + 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x00, 0x00 + }; + size_t load_option_data_size = sizeof(load_option_data); + + return test_parse_load_options(load_option_data, + load_option_data_size, + "\\EFI\\fedora\\shimx64.efi", + L"\\fwupx64.efi", + NULL, 0); +} + +int +test_bds_2(void) +{ +/* +00000000 74 00 65 00 73 00 74 00 2E 00 65 00 66 00 69 00 |t.e.s.t...e.f.i.| +00000010 20 00 6F 00 6E 00 65 00 20 00 74 00 77 00 6F 00 |..o.n.e...t.w.o.| +00000020 20 00 74 00 68 00 72 00 65 00 65 00 00 00 |..t.h.r.e.e...| +*/ + char load_option_data [] = { + 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x2E, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, + 0x20, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x74, 0x00, 0x77, 0x00, 0x6F, 0x00, + 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x00, 0x00 + }; + size_t load_option_data_size = sizeof(load_option_data); + char *target_remaining = &load_option_data[26]; + size_t target_remaining_size = 20; + + return test_parse_load_options(load_option_data, + load_option_data_size, + "test.efi", + L"one", + target_remaining, + target_remaining_size); +} + +int +main(void) +{ + int status = 0; + test(test_efi_shell_0); + test(test_bds_0); + test(test_bds_1); + test(test_bds_2); + return status; +} + +// vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 44f186354b6c9262e9a2b9f99cb5233d8fc19abf Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 29 Aug 2021 19:01:04 +0200 Subject: str: remove duplicate parameter check There is no need to check the parameters of strntoken() twice. Fixes: c7bb10cf154a ("Tidy up our string primitives...") Signed-off-by: Heinrich Schuchardt --- include/str.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/str.h b/include/str.h index d433e6ec..1d911e66 100644 --- a/include/str.h +++ b/include/str.h @@ -72,8 +72,6 @@ strntoken(char *str, size_t max, const char *delims, char **token, char *state) return 0; tokend = &str[max-1]; - if (!str || max == 0 || !delims || !token) - return 0; /* * the very special case of "" with max=1, where we have no prior -- cgit v1.2.3 From 116a8310ab93d803fa51f9ba9f3d6d0cb691e2bf Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 2 Aug 2021 13:09:28 -0400 Subject: More minor improvements to support for COMPILER=clang A couple of places snuck in where building with COMPILER=clang didn't work right; this makes them work again. Signed-off-by: Peter Jones --- Make.defaults | 7 ++++++- Makefile | 19 ++++++++++++++++--- include/test.mk | 14 +++++++++++--- shim.h | 4 ++++ 4 files changed, 37 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Make.defaults b/Make.defaults index 1b929a71..d041f0d5 100644 --- a/Make.defaults +++ b/Make.defaults @@ -52,7 +52,11 @@ override EFI_INCLUDES := $(EFI_INCLUDES) EFI_CRT_OBJS = $(LOCAL_EFI_PATH)/crt0-efi-$(ARCH_GNUEFI).o EFI_LDS = $(TOPDIR)/elf_$(ARCH)_efi.lds -CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) +CLANG_WARNINGS = -Wno-pointer-bool-conversion \ + -Wno-unknown-attributes + +CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) \ + $(if $(findstring clang,$(CC)),$(CLANG_WARNINGS)) COMMIT_ID ?= $(shell if [ -e .git ] ; then git log -1 --pretty=format:%H ; elif [ -f commit ]; then cat commit ; else echo master; fi) @@ -131,6 +135,7 @@ $(call update-variable,WERRFLAGS) CFLAGS = $(FEATUREFLAGS) \ $(OPTIMIZATIONS) \ $(WARNFLAGS) \ + $(if $(findstring clang,$(CC)),$(CLANG_WARNINGS)) \ $(ARCH_CFLAGS) \ $(WERRFLAGS) \ $(INCLUDES) \ diff --git a/Makefile b/Makefile index 48db1af6..f4aad593 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,10 @@ gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: C gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: mkdir -p gnu-efi/lib gnu-efi/gnuefi $(MAKE) -C gnu-efi \ - ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + COMPILER="$(COMPILER)" \ + CC="$(CC)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ -f $(TOPDIR)/gnu-efi/Makefile \ lib gnuefi inc @@ -285,7 +288,13 @@ else endif test : - @make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" all + @make -f $(TOPDIR)/include/test.mk \ + COMPILER="$(COMPILER)" \ + CROSS_COMPILE="$(CROSS_COMPILE)" \ + CLANG_WARNINGS="$(CLANG_WARNINGS)" \ + ARCH_DEFINES="$(ARCH_DEFINES)" \ + EFI_INCLUDES="$(EFI_INCLUDES)" \ + all $(patsubst %.c,%,$(wildcard test-*.c)) : @make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@ @@ -298,7 +307,11 @@ clean-test-objs: clean-gnu-efi: @if [ -d gnu-efi ] ; then \ $(MAKE) -C gnu-efi \ - ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + CC="$(CC)" \ + HOSTCC="$(HOSTCC)" \ + COMPILER="$(COMPILER)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ -f $(TOPDIR)/gnu-efi/Makefile \ clean ; \ fi diff --git a/include/test.mk b/include/test.mk index 68230b20..b99283f3 100644 --- a/include/test.mk +++ b/include/test.mk @@ -27,10 +27,11 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -fno-eliminate-unused-debug-symbols \ -gpubnames \ -grecord-gcc-switches \ + $(if $(findstring clang,$(CC)),-Wno-unknown-warning-option) \ $(DEFAULT_WARNFLAGS) \ -Wsign-compare \ -Wno-deprecated-declarations \ - -Wno-unused-but-set-variable \ + $(if $(findstring gcc,$(CC)),-Wno-unused-but-set-variable) \ -Wno-unused-variable \ -Wno-pointer-sign \ $(DEFAULT_WERRFLAGS) \ @@ -43,11 +44,18 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" libefi-test.a : - $(MAKE) -C gnu-efi ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + $(MAKE) -C gnu-efi \ + COMPILER="$(COMPILER)" \ + CC="$(CC)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ -f $(TOPDIR)/gnu-efi/Makefile \ clean lib mv gnu-efi/$(ARCH)/lib/libefi.a $@ - $(MAKE) -C gnu-efi ARCH=$(ARCH_GNUEFI) TOPDIR=$(TOPDIR)/gnu-efi \ + $(MAKE) -C gnu-efi \ + COMPILER="$(COMPILER)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ -f $(TOPDIR)/gnu-efi/Makefile \ clean diff --git a/shim.h b/shim.h index 94b48bcc..d54fc6e9 100644 --- a/shim.h +++ b/shim.h @@ -27,6 +27,10 @@ #error On x86_64 you must have a compiler new enough to support __attribute__((__ms_abi__)) #endif +#if CLANG_PREREQ(3, 4) +#pragma GCC diagnostic ignored "-Wpointer-bool-conversion" +#endif + #if !defined(GNU_EFI_USE_EXTERNAL_STDARG) #define GNU_EFI_USE_EXTERNAL_STDARG #endif -- cgit v1.2.3 From 284f30607aaaab3a8ad502c557e31f7f5dbc0b64 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 2 Aug 2021 13:58:22 -0400 Subject: tests: clean up temp data after running tests This makes sure we clean up the builds that aren't for the EFI environment after we build and run the unit tests. Signed-off-by: Peter Jones --- Makefile | 4 ++-- include/test.mk | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index f4aad593..097cbd30 100644 --- a/Makefile +++ b/Makefile @@ -287,14 +287,14 @@ else $(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f endif -test : +test test-clean : @make -f $(TOPDIR)/include/test.mk \ COMPILER="$(COMPILER)" \ CROSS_COMPILE="$(CROSS_COMPILE)" \ CLANG_WARNINGS="$(CLANG_WARNINGS)" \ ARCH_DEFINES="$(ARCH_DEFINES)" \ EFI_INCLUDES="$(EFI_INCLUDES)" \ - all + test-clean $@ $(patsubst %.c,%,$(wildcard test-*.c)) : @make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@ diff --git a/include/test.mk b/include/test.mk index b99283f3..c11d0c74 100644 --- a/include/test.mk +++ b/include/test.mk @@ -87,11 +87,14 @@ $(tests) :: test-% : test.c test-%.c $(test-%_FILES) $(VALGRIND) ./$@ test : $(tests) + $(MAKE) -f include/test.mk test-clean -clean : - @rm -vf test-random.h random.bin libefi-test.a +test-clean : + @rm -vf test-random.h random.bin libefi-test.a vgcore.* -all : clean test +clean : test-clean + +all : test-clean test .PHONY: $(tests) all test clean .SECONDARY: random.bin -- cgit v1.2.3 From 1f434aa623420f9768c327229053841d4fc904c0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 2 Aug 2021 14:55:02 -0400 Subject: tests: make the CompareGuid() comparison size be less stupid 5f08e671e4eb introduced a CompareGuid() call in the unit test harness, but unfortunately it has a typo and thus only ever compares the first pointer-sized word of the guid. With 4-GUIDs, this will usually produce the correct results; with 1-GUIDs it often won't. A second issue is that the memcmp() implementation of CompareGuid() produces a different sort order than comparing field-by-field, and also a different sort order than comparing the string representation. This is often not a problem (edk2, for example, never compares anything except equality of two GUIDs), but when writing test cases it is extremely helpful to be able to look at a list that is sorted in an intuitive order. This patch introduces a guidcmp() function in the test suite, which compares the binary data in the same order that comparing the two GUIDs' string representations would. Signed-off-by: Peter Jones --- include/test.h | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index fedeb782..6d90ef18 100644 --- a/include/test.h +++ b/include/test.h @@ -8,7 +8,13 @@ #ifndef TEST_H_ #define TEST_H_ +#define _GNU_SOURCE + #include +#include +#include +#include +#include #if defined(__aarch64__) #include @@ -61,7 +67,118 @@ extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RT; -#define CompareGuid(a, b) memcmp(a, b, sizeof(a)) +#define GUID_FMT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" +#define GUID_ARGS(guid) \ + ((EFI_GUID)guid).Data1, ((EFI_GUID)guid).Data2, ((EFI_GUID)guid).Data3, \ + ((EFI_GUID)guid).Data4[1], ((EFI_GUID)guid).Data4[0], \ + ((EFI_GUID)guid).Data4[2], ((EFI_GUID)guid).Data4[3], \ + ((EFI_GUID)guid).Data4[4], ((EFI_GUID)guid).Data4[5], \ + ((EFI_GUID)guid).Data4[6], ((EFI_GUID)guid).Data4[7] + +static inline INT64 +guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) +{ +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): Comparing "GUID_FMT" to "GUID_FMT"\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(*guid0), GUID_ARGS(*guid1)); +#endif + + if (guid0->Data1 != guid1->Data1) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data1, (INT64)guid1->Data1, + (INT64)guid0->Data1 - (INT64)guid1->Data1); +#endif + return (INT64)guid0->Data1 - (INT64)guid1->Data1; + } + + if (guid0->Data2 != guid1->Data2) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data2, (INT64)guid1->Data2, + (INT64)guid0->Data2 - (INT64)guid1->Data2); +#endif + return (INT64)guid0->Data2 - (INT64)guid1->Data2; + } + + if (guid0->Data3 != guid1->Data3) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data3, (INT64)guid1->Data3, + (INT64)guid0->Data3 - (INT64)guid1->Data3); +#endif + return (INT64)guid0->Data3 - (INT64)guid1->Data3; + } + + /* + * This is out of order because that's also true in the string + * representation of it. + */ + if (guid0->Data4[1] != guid1->Data4[1]) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data4[1], (INT64)guid1->Data4[1], + (INT64)guid0->Data4[1] - (INT64)guid1->Data4[1]); +#endif + return (INT64)guid0->Data4[1] - (INT64)guid1->Data4[1]; + } + + if (guid0->Data4[0] != guid1->Data4[0]) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data4[0], (INT64)guid1->Data4[0], + (INT64)guid0->Data4[0] - (INT64)guid1->Data4[0]); +#endif + return (INT64)guid0->Data4[0] - (INT64)guid1->Data4[0]; + } + + for (UINTN i = 2; i < 8; i++) { + if (guid0->Data4[i] != guid1->Data4[i]) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", + __FILE__, __LINE__-1, __func__, + (INT64)guid0->Data4[i], (INT64)guid1->Data4[i], + (INT64)guid0->Data4[i] - (INT64)guid1->Data4[i]); +#endif + return (INT64)guid0->Data4[i] - (INT64)guid1->Data4[i]; + } + } + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning 0x0\n", + __FILE__, __LINE__-1, __func__); +#endif + return 0; +} + +static inline int +guidcmp(const EFI_GUID * const guid0, const EFI_GUID * const guid1) +{ + INT64 cmp; + int ret; + EFI_GUID empty; + const EFI_GUID * const guida = guid0 ? guid0 : ∅ + const EFI_GUID * const guidb = guid1 ? guid1 : ∅ + + SetMem(&empty, sizeof(EFI_GUID), 0); + + cmp = guidcmp_helper(guida, guidb); + ret = cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():CompareGuid("GUID_FMT","GUID_FMT")->%lld (%d)\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(*guida), GUID_ARGS(*guidb), cmp, ret); +#endif + return ret; +} + +#define CompareGuid(a, b) guidcmp(a, b) extern int debug; #ifdef dprint -- cgit v1.2.3 From 3cc53c788ed2c540889da8b01d681103dda39e92 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 23 Jul 2021 13:18:57 -0400 Subject: mok: move the mok_state_variables definitions to their own header This lets us access the definitions for this structure, and the data being used at runtime, from unit tests. Signed-off-by: Peter Jones --- include/mok.h | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mok.c | 89 ++------------------------------------------------- shim.h | 1 + 3 files changed, 105 insertions(+), 86 deletions(-) create mode 100644 include/mok.h (limited to 'include') diff --git a/include/mok.h b/include/mok.h new file mode 100644 index 00000000..96da397a --- /dev/null +++ b/include/mok.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * mok.h - structs for MoK data + * Copyright Peter Jones + */ + +#ifndef SHIM_MOK_H_ +#define SHIM_MOK_H_ + +#include "shim.h" + +typedef enum { + VENDOR_ADDEND_DB, + VENDOR_ADDEND_X509, + VENDOR_ADDEND_NONE, +} vendor_addend_category_t; + +struct mok_state_variable; +typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *); + +/* + * MoK variables that need to have their storage validated. + * + * The order here is important, since this is where we measure for the + * tpm as well. + */ +struct mok_state_variable { + CHAR16 *name; /* UCS-2 BS|NV variable name */ + char *name8; /* UTF-8 BS|NV variable name */ + CHAR16 *rtname; /* UCS-2 RT variable name */ + char *rtname8; /* UTF-8 RT variable name */ + EFI_GUID *guid; /* variable GUID */ + + /* + * these are used during processing, they shouldn't be filled out + * in the static table below. + */ + UINT8 *data; + UINTN data_size; + + /* + * addend are added to the input variable, as part of the runtime + * variable, so that they're visible to the kernel. These are + * where we put vendor_cert / vendor_db / vendor_dbx + * + * These are indirect pointers just to make initialization saner... + */ + vendor_addend_categorizer_t *categorize_addend; /* determines format */ + /* + * we call categorize_addend() and it determines what kind of thing + * this is. That is, if this shim was built with VENDOR_CERT, for + * the DB entry it'll return VENDOR_ADDEND_X509; if you used + * VENDOR_DB instead, it'll return VENDOR_ADDEND_DB. If you used + * neither, it'll do VENDOR_ADDEND_NONE. + * + * The existing categorizers are for db and dbx; they differ + * because we don't currently support a CERT for dbx. + */ + UINT8 **addend; + UINT32 *addend_size; + + /* + * build_cert is our build-time cert. Like addend, this is added + * to the input variable, as part of the runtime variable, so that + * they're visible to the kernel. This is the ephemeral cert used + * for signing MokManager.efi and fallback.efi. + * + * These are indirect pointers just to make initialization saner... + */ + UINT8 **build_cert; + UINT32 *build_cert_size; + + UINT32 yes_attr; /* var attrs that must be set */ + UINT32 no_attr; /* var attrs that must not be set */ + UINT32 flags; /* flags on what and how to mirror */ + /* + * MOK_MIRROR_KEYDB mirror this as a key database + * MOK_MIRROR_DELETE_FIRST delete any existing variable first + * MOK_VARIABLE_MEASURE extend PCR 7 and log the hash change + * MOK_VARIABLE_LOG measure into whatever .pcr says and log + */ + UINTN pcr; /* PCR to measure and hash to */ + + /* + * if this is a state value, a pointer to our internal state to be + * mirrored. + */ + UINT8 *state; +}; + +extern size_t n_mok_state_variables; +extern struct mok_state_variable *mok_state_variables; + +struct mok_variable_config_entry { + CHAR8 name[256]; + UINT64 data_size; + UINT8 data[]; +}; + +#endif /* !SHIM_MOK_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/mok.c b/mok.c index af1756c2..801379ee 100644 --- a/mok.c +++ b/mok.c @@ -58,85 +58,6 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) return EFI_SUCCESS; } -typedef enum { - VENDOR_ADDEND_DB, - VENDOR_ADDEND_X509, - VENDOR_ADDEND_NONE, -} vendor_addend_category_t; - -struct mok_state_variable; -typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *); - -/* - * MoK variables that need to have their storage validated. - * - * The order here is important, since this is where we measure for the - * tpm as well. - */ -struct mok_state_variable { - CHAR16 *name; /* UCS-2 BS|NV variable name */ - char *name8; /* UTF-8 BS|NV variable name */ - CHAR16 *rtname; /* UCS-2 RT variable name */ - char *rtname8; /* UTF-8 RT variable name */ - EFI_GUID *guid; /* variable GUID */ - - /* - * these are used during processing, they shouldn't be filled out - * in the static table below. - */ - UINT8 *data; - UINTN data_size; - - /* - * addend are added to the input variable, as part of the runtime - * variable, so that they're visible to the kernel. These are - * where we put vendor_cert / vendor_db / vendor_dbx - * - * These are indirect pointers just to make initialization saner... - */ - vendor_addend_categorizer_t *categorize_addend; /* determines format */ - /* - * we call categorize_addend() and it determines what kind of thing - * this is. That is, if this shim was built with VENDOR_CERT, for - * the DB entry it'll return VENDOR_ADDEND_X509; if you used - * VENDOR_DB instead, it'll return VENDOR_ADDEND_DB. If you used - * neither, it'll do VENDOR_ADDEND_NONE. - * - * The existing categorizers are for db and dbx; they differ - * because we don't currently support a CERT for dbx. - */ - UINT8 **addend; - UINT32 *addend_size; - - /* - * build_cert is our build-time cert. Like addend, this is added - * to the input variable, as part of the runtime variable, so that - * they're visible to the kernel. This is the ephemeral cert used - * for signing MokManager.efi and fallback.efi. - * - * These are indirect pointers just to make initialization saner... - */ - UINT8 **build_cert; - UINT32 *build_cert_size; - - UINT32 yes_attr; /* var attrs that must be set */ - UINT32 no_attr; /* var attrs that must not be set */ - UINT32 flags; /* flags on what and how to mirror */ - /* - * MOK_MIRROR_KEYDB mirror this as a key database - * MOK_MIRROR_DELETE_FIRST delete any existing variable first - * MOK_VARIABLE_MEASURE extend PCR 7 and log the hash change - * MOK_VARIABLE_LOG measure into whatever .pcr says and log - */ - UINTN pcr; /* PCR to measure and hash to */ - - /* - * if this is a state value, a pointer to our internal state to be - * mirrored. - */ - UINT8 *state; -}; - static vendor_addend_category_t categorize_authorized(struct mok_state_variable *v) { @@ -164,7 +85,7 @@ categorize_deauthorized(struct mok_state_variable *v) #define MOK_VARIABLE_MEASURE 0x04 #define MOK_VARIABLE_LOG 0x08 -struct mok_state_variable mok_state_variables[] = { +struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", .name8 = "MokList", .rtname = L"MokListRT", @@ -247,6 +168,8 @@ struct mok_state_variable mok_state_variables[] = { }, { NULL, } }; +size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); +struct mok_state_variable *mok_state_variables = &mok_state_variable_data[0]; #define should_mirror_addend(v) (((v)->categorize_addend) && ((v)->categorize_addend(v) != VENDOR_ADDEND_NONE)) @@ -894,12 +817,6 @@ maybe_mirror_one_mok_variable(struct mok_state_variable *v, return ret; } -struct mok_variable_config_entry { - CHAR8 name[256]; - UINT64 data_size; - UINT8 data[]; -}; - EFI_STATUS import_one_mok_state(struct mok_state_variable *v, BOOLEAN only_first) { diff --git a/shim.h b/shim.h index d54fc6e9..5e1ab36b 100644 --- a/shim.h +++ b/shim.h @@ -172,6 +172,7 @@ #include "include/ip4config2.h" #include "include/ip6config.h" #include "include/load-options.h" +#include "include/mok.h" #include "include/netboot.h" #include "include/passwordcrypt.h" #include "include/peimage.h" -- cgit v1.2.3 From e5a406b4655f75bb1a691acc73a18ac69cc87cb0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 16 Aug 2021 17:24:07 -0400 Subject: tests: Disable some logging when SHIM_UNIT_TEST is enabled None of this should ever actually get called when we're running any of the unit tests we've got, but some older compilers (i.e. Centos 7's gcc) fail to remove some of the intermediate functions, and that causes a link error with the functions they call. This patch makes the top level call go away as well, so that the intermediates never have linkage to the underlying implementation functions. Signed-off-by: Peter Jones --- include/console.h | 4 ++++ include/hexdump.h | 7 +++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/console.h b/include/console.h index f56b1231..c832b20e 100644 --- a/include/console.h +++ b/include/console.h @@ -109,8 +109,12 @@ extern UINT32 verbose; extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, const char *func, ms_va_list args); +#if defined(SHIM_UNIT_TEST) +#define vdprint(fmt, ...) +#else #define vdprint(fmt, ...) \ vdprint_(fmt, __FILE__, __LINE__ - 1, __func__, ##__VA_ARGS__) +#endif extern EFI_STATUS print_crypto_errors(EFI_STATUS rc, char *file, const char *func, int line); #define crypterr(rc) print_crypto_errors((rc), __FILE__, __func__, __LINE__) diff --git a/include/hexdump.h b/include/hexdump.h index 381e1a68..1a20339b 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -137,12 +137,19 @@ hexdumpat(const char *file, int line, const char *func, const void *data, unsign hexdumpf(file, line, func, L"", data, size, at); } +#if defined(SHIM_UNIT_TEST) +#define LogHexDump(data, ...) +#define dhexdump(data, ...) +#define dhexdumpat(data, ...) +#define dhexdumpf(fmt, ...) +#else #define LogHexdump(data, sz) LogHexdump_(__FILE__, __LINE__, __func__, data, sz) #define dhexdump(data, sz) hexdump(__FILE__, __LINE__, __func__, data, sz) #define dhexdumpat(data, sz, at) \ hexdumpat(__FILE__, __LINE__ - 1, __func__, data, sz, at) #define dhexdumpf(fmt, data, sz, at, ...) \ hexdumpf(__FILE__, __LINE__ - 1, __func__, fmt, data, sz, at, ##__VA_ARGS__) +#endif #endif /* STATIC_HEXDUMP_H */ // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 97350bd6e02ceb0818e8bc0a2b2bc6adaf9c2092 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 23 Jul 2021 14:31:46 -0400 Subject: tests: link all tests against libefivar When writing new tests, if we get to the point where we have to use libefivar for something, it's very common that I accidentally link it in twice. When that happens, I typically spend an unfortunate amount of time staring at FLTO's mangled names before I figure out what I've done wrong. This patch makes all the tests link against libefivar, thereby avoiding the issue. Signed-off-by: Peter Jones --- include/efiauthenticated.h | 6 ++++++ include/test.mk | 7 ++----- test.c | 17 ----------------- 3 files changed, 8 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/efiauthenticated.h b/include/efiauthenticated.h index f2bcefdb..d5c3204f 100644 --- a/include/efiauthenticated.h +++ b/include/efiauthenticated.h @@ -124,9 +124,15 @@ typedef struct { /* * Attributes of Authenticated Variable */ +#ifndef EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS #define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 +#endif +#ifndef EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS #define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#endif +#ifndef EFI_VARIABLE_APPEND_WRITE #define EFI_VARIABLE_APPEND_WRITE 0x00000040 +#endif /* * AuthInfo is a WIN_CERTIFICATE using the wCertificateType diff --git a/include/test.mk b/include/test.mk index c11d0c74..60a7e6fa 100644 --- a/include/test.mk +++ b/include/test.mk @@ -67,10 +67,7 @@ $(wildcard test-*.c) :: %.c : test-random.h $(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h $(patsubst %.c,%.o,$(wildcard test-*.c)) : | test-random.h -test-load-options_FILES = lib/guid.c \ - libefi-test.a \ - -lefivar -test-load-options :: libefi-test.a +test-load-options_FILES = lib/guid.c test-load-options : CFLAGS+=-DHAVE_SHIM_LOCK_GUID test-sbat_FILES = csv.c lib/variables.c lib/guid.c @@ -83,7 +80,7 @@ tests := $(patsubst %.c,%,$(wildcard test-*.c)) $(tests) :: test-% : | libefi-test.a $(tests) :: test-% : test.c test-%.c $(test-%_FILES) - $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) libefi-test.a + $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) libefi-test.a -lefivar $(VALGRIND) ./$@ test : $(tests) diff --git a/test.c b/test.c index d9902ebc..39725a72 100644 --- a/test.c +++ b/test.c @@ -122,23 +122,6 @@ LogError_(const char *file, int line, const char *func, const CHAR16 *fmt, ...) return EFI_SUCCESS; } -#ifndef HAVE_GET_VARIABLE_ATTR -EFI_STATUS -get_variable_attr(const CHAR16 * const var, UINT8 **data, UINTN *len, - EFI_GUID owner, UINT32 *attributes) -{ - return EFI_UNSUPPORTED; -} -#endif - -#ifndef HAVE_GET_VARIABLE -EFI_STATUS -get_variable(const CHAR16 * const var, UINT8 **data, UINTN *len, EFI_GUID owner) -{ - return get_variable_attr(var, data, len, owner, NULL); -} -#endif - #ifndef HAVE_SHIM_LOCK_GUID EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; #endif -- cgit v1.2.3 From 6a95bea8d508f3937e46b1eec2b5def8d5135d78 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 4 Aug 2021 10:42:02 -0400 Subject: tests: Add a 'test-coverage' make target for gcov This adds a make target that builds the tests with gcov so we can identify coverage gaps in the test suite. It also makes a special test-lto invocation, so that a developer can run these tests with the somewhat different optimization results LTO will have. Signed-off-by: Peter Jones --- Makefile | 2 +- include/test.mk | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index 07936e52..3b59fc69 100644 --- a/Makefile +++ b/Makefile @@ -287,7 +287,7 @@ else $(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f endif -test test-clean : +test test-clean test-coverage test-lto : @make -f $(TOPDIR)/include/test.mk \ COMPILER="$(COMPILER)" \ CROSS_COMPILE="$(CROSS_COMPILE)" \ diff --git a/include/test.mk b/include/test.mk index 60a7e6fa..86f1a9d8 100644 --- a/include/test.mk +++ b/include/test.mk @@ -11,6 +11,8 @@ CC = gcc VALGRIND ?= DEBUG_PRINTS ?= 0 OPTIMIZATIONS=-O2 -ggdb +CFLAGS_LTO = +CFLAGS_GCOV = CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -isystem $(TOPDIR)/include/system \ $(EFI_INCLUDES) \ @@ -18,8 +20,9 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -isystem /usr/include \ -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \ $(ARCH_CFLAGS) \ + $(CFLAGS_LTO) \ + $(CFLAGS_GCOV) \ -fshort-wchar \ - -flto \ -fno-builtin \ -rdynamic \ -fno-inline \ @@ -43,12 +46,16 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -DSHIM_UNIT_TEST \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" +export CFLAGS_LTO CFLAGS_GCOV + libefi-test.a : $(MAKE) -C gnu-efi \ COMPILER="$(COMPILER)" \ CC="$(CC)" \ ARCH=$(ARCH_GNUEFI) \ TOPDIR=$(TOPDIR)/gnu-efi \ + CFLAGS_LTO="$(CFLAGS_LTO)" \ + CFLAGS_GCOV="$(CFLAGS_GCOV)" \ -f $(TOPDIR)/gnu-efi/Makefile \ clean lib mv gnu-efi/$(ARCH)/lib/libefi.a $@ @@ -56,6 +63,8 @@ libefi-test.a : COMPILER="$(COMPILER)" \ ARCH=$(ARCH_GNUEFI) \ TOPDIR=$(TOPDIR)/gnu-efi \ + CFLAGS_LTO="$(CFLAGS_LTO)" \ + CFLAGS_GCOV="$(CFLAGS_GCOV)" \ -f $(TOPDIR)/gnu-efi/Makefile \ clean @@ -86,8 +95,16 @@ $(tests) :: test-% : test.c test-%.c $(test-%_FILES) test : $(tests) $(MAKE) -f include/test.mk test-clean +test-lto : CFLAGS_LTO+=-flto +test-lto : $(tests) + $(MAKE) -f include/test.mk test-clean + +test-coverage : CFLAGS_GCOV+=--coverage +test-coverage : $(tests) + test-clean : - @rm -vf test-random.h random.bin libefi-test.a vgcore.* + @rm -vf test-random.h random.bin libefi-test.a + @rm -vf *.gcda *.gcno *.gcov vgcore.* clean : test-clean -- cgit v1.2.3 From 1368d9ac37b985454ff1b9a28f9a46b82b5eba9b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 3 Aug 2021 15:49:47 -0400 Subject: tests: add an efi error decoder Signed-off-by: Peter Jones --- include/test.h | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'include') diff --git a/include/test.h b/include/test.h index 6d90ef18..f6b0e929 100644 --- a/include/test.h +++ b/include/test.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -180,6 +181,123 @@ guidcmp(const EFI_GUID * const guid0, const EFI_GUID * const guid1) #define CompareGuid(a, b) guidcmp(a, b) +static inline char * +efi_strerror(EFI_STATUS status) +{ + static char buf0[1024]; + static char buf1[1024]; + char *out; + static int n; + + out = n++ % 2 ? buf0 : buf1; + if (n > 1) + n -= 2; + SetMem(out, 1024, 0); + + switch (status) { + case EFI_SUCCESS: + strcpy(out, "EFI_SUCCESS"); + break; + case EFI_LOAD_ERROR: + strcpy(out, "EFI_LOAD_ERROR"); + break; + case EFI_INVALID_PARAMETER: + strcpy(out, "EFI_INVALID_PARAMETER"); + break; + case EFI_UNSUPPORTED: + strcpy(out, "EFI_UNSUPPORTED"); + break; + case EFI_BAD_BUFFER_SIZE: + strcpy(out, "EFI_BAD_BUFFER_SIZE"); + break; + case EFI_BUFFER_TOO_SMALL: + strcpy(out, "EFI_BUFFER_TOO_SMALL"); + break; + case EFI_NOT_READY: + strcpy(out, "EFI_NOT_READY"); + break; + case EFI_DEVICE_ERROR: + strcpy(out, "EFI_DEVICE_ERROR"); + break; + case EFI_WRITE_PROTECTED: + strcpy(out, "EFI_WRITE_PROTECTED"); + break; + case EFI_OUT_OF_RESOURCES: + strcpy(out, "EFI_OUT_OF_RESOURCES"); + break; + case EFI_VOLUME_CORRUPTED: + strcpy(out, "EFI_VOLUME_CORRUPTED"); + break; + case EFI_VOLUME_FULL: + strcpy(out, "EFI_VOLUME_FULL"); + break; + case EFI_NO_MEDIA: + strcpy(out, "EFI_NO_MEDIA"); + break; + case EFI_MEDIA_CHANGED: + strcpy(out, "EFI_MEDIA_CHANGED"); + break; + case EFI_NOT_FOUND: + strcpy(out, "EFI_NOT_FOUND"); + break; + case EFI_ACCESS_DENIED: + strcpy(out, "EFI_ACCESS_DENIED"); + break; + case EFI_NO_RESPONSE: + strcpy(out, "EFI_NO_RESPONSE"); + break; + case EFI_NO_MAPPING: + strcpy(out, "EFI_NO_MAPPING"); + break; + case EFI_TIMEOUT: + strcpy(out, "EFI_TIMEOUT"); + break; + case EFI_NOT_STARTED: + strcpy(out, "EFI_NOT_STARTED"); + break; + case EFI_ALREADY_STARTED: + strcpy(out, "EFI_ALREADY_STARTED"); + break; + case EFI_ABORTED: + strcpy(out, "EFI_ABORTED"); + break; + case EFI_ICMP_ERROR: + strcpy(out, "EFI_ICMP_ERROR"); + break; + case EFI_TFTP_ERROR: + strcpy(out, "EFI_TFTP_ERROR"); + break; + case EFI_PROTOCOL_ERROR: + strcpy(out, "EFI_PROTOCOL_ERROR"); + break; + case EFI_INCOMPATIBLE_VERSION: + strcpy(out, "EFI_INCOMPATIBLE_VERSION"); + break; + case EFI_SECURITY_VIOLATION: + strcpy(out, "EFI_SECURITY_VIOLATION"); + break; + case EFI_CRC_ERROR: + strcpy(out, "EFI_CRC_ERROR"); + break; + case EFI_END_OF_MEDIA: + strcpy(out, "EFI_END_OF_MEDIA"); + break; + case EFI_END_OF_FILE: + strcpy(out, "EFI_END_OF_FILE"); + break; + case EFI_INVALID_LANGUAGE: + strcpy(out, "EFI_INVALID_LANGUAGE"); + break; + case EFI_COMPROMISED_DATA: + strcpy(out, "EFI_COMPROMISED_DATA"); + break; + default: + sprintf(out, "0x%lx", status); + break; + } + return out; +} + extern int debug; #ifdef dprint #undef dprint -- cgit v1.2.3 From 5ed27307d4854e67cf999fd7785b48f5cf488a51 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 3 Aug 2021 11:15:29 -0400 Subject: tests: add some slightly better EFIAPI error mocks This adds more mock functions that just return various EFI error codes in the EFIAPI ABI. Signed-off-by: Peter Jones --- include/test.h | 6 +++ test.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 145 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index f6b0e929..fcaa1cdf 100644 --- a/include/test.h +++ b/include/test.h @@ -304,6 +304,12 @@ extern int debug; #define dprint(fmt, ...) {( if (debug) printf("%s:%d:" fmt, __func__, __LINE__, ##__VA_ARGS__); }) #endif +void EFIAPI mock_efi_void(); +EFI_STATUS EFIAPI mock_efi_success(); +EFI_STATUS EFIAPI mock_efi_unsupported(); +EFI_STATUS EFIAPI mock_efi_not_found(); +void init_efi_system_table(void); +void reset_efi_system_table(void); void print_traceback(int skip); #define eassert(cond, fmt, ...) \ diff --git a/test.c b/test.c index 39725a72..8a4a72ff 100644 --- a/test.c +++ b/test.c @@ -4,9 +4,6 @@ * Copyright Peter Jones */ -#ifndef SHIM_UNIT_TEST -#define SHIM_UNIT_TEST -#endif #include "shim.h" #include @@ -87,32 +84,162 @@ mock_efi_free_pool(void *buf) return EFI_SUCCESS; } -EFI_BOOT_SERVICES mock_bs = { - .RaiseTPL = NULL, - .RestoreTPL = NULL, +void EFIAPI +mock_efi_void() +{ + ; +} + +EFI_STATUS EFIAPI +mock_efi_success() +{ + return EFI_SUCCESS; +} + +EFI_STATUS EFIAPI +mock_efi_unsupported() +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS EFIAPI +mock_efi_not_found() +{ + return EFI_NOT_FOUND; +} + +EFI_BOOT_SERVICES mock_bs, mock_default_bs = { + .Hdr = { + .Signature = EFI_BOOT_SERVICES_SIGNATURE, + .Revision = EFI_1_10_BOOT_SERVICES_REVISION, + .HeaderSize = offsetof(EFI_BOOT_SERVICES, SetMem) + + sizeof(mock_bs.SetMem), + }, + + .RaiseTPL = mock_efi_unsupported, + .RestoreTPL = mock_efi_void, .AllocatePages = mock_efi_allocate_pages, .FreePages = mock_efi_free_pages, + .GetMemoryMap = mock_efi_unsupported, .AllocatePool = mock_efi_allocate_pool, .FreePool = mock_efi_free_pool, + + .CreateEvent = mock_efi_unsupported, + .SetTimer = mock_efi_unsupported, + .WaitForEvent = mock_efi_unsupported, + .SignalEvent = mock_efi_unsupported, + .CloseEvent = mock_efi_unsupported, + .CheckEvent = mock_efi_unsupported, + + .InstallProtocolInterface = mock_efi_unsupported, + .ReinstallProtocolInterface = mock_efi_unsupported, + .UninstallProtocolInterface = mock_efi_unsupported, + .HandleProtocol = mock_efi_unsupported, +#if 0 + /* + * EFI 1.10 has a "Reserved" field here that's not in later + * revisions. + * + * I don't think it's in any actual *firmware* either. + */ + .Reserved = NULL, +#endif + .RegisterProtocolNotify = mock_efi_unsupported, + .LocateHandle = mock_efi_not_found, + .LocateDevicePath = mock_efi_unsupported, + .InstallConfigurationTable = mock_efi_unsupported, + + .LoadImage = (void *)mock_efi_unsupported, + .StartImage = mock_efi_unsupported, + .Exit = mock_efi_unsupported, + .UnloadImage = mock_efi_unsupported, + .ExitBootServices = mock_efi_unsupported, + + .GetNextMonotonicCount = mock_efi_unsupported, + .Stall = mock_efi_unsupported, + .SetWatchdogTimer = mock_efi_unsupported, + + .ConnectController = (void *)mock_efi_unsupported, + .DisconnectController = mock_efi_unsupported, + + .OpenProtocol = mock_efi_unsupported, + .CloseProtocol = mock_efi_unsupported, + .OpenProtocolInformation = mock_efi_unsupported, + + .ProtocolsPerHandle = mock_efi_unsupported, + .LocateHandleBuffer = mock_efi_unsupported, + .LocateProtocol = mock_efi_unsupported, + + .InstallMultipleProtocolInterfaces = (void *)mock_efi_unsupported, + .UninstallMultipleProtocolInterfaces = (void *)mock_efi_unsupported, + + .CalculateCrc32 = mock_efi_unsupported, + + .CopyMem = NULL, + .SetMem = NULL, + .CreateEventEx = mock_efi_unsupported, }; -EFI_RUNTIME_SERVICES mock_rt = { - .Hdr = { 0, }, - .GetVariable = NULL, +EFI_RUNTIME_SERVICES mock_rt, mock_default_rt = { + .Hdr = { + .Signature = EFI_RUNTIME_SERVICES_SIGNATURE, + .Revision = EFI_1_10_RUNTIME_SERVICES_REVISION, + .HeaderSize = offsetof(EFI_RUNTIME_SERVICES, ResetSystem) + + sizeof(mock_rt.ResetSystem), + }, + + .GetTime = mock_efi_unsupported, + .SetTime = mock_efi_unsupported, + .GetWakeupTime = mock_efi_unsupported, + .SetWakeupTime = (void *)mock_efi_unsupported, + + .SetVirtualAddressMap = mock_efi_unsupported, + .ConvertPointer = mock_efi_unsupported, + + .GetVariable = mock_efi_unsupported, + .SetVariable = mock_efi_unsupported, + .GetNextVariableName = mock_efi_unsupported, + + .GetNextHighMonotonicCount = mock_efi_unsupported, + .ResetSystem = mock_efi_unsupported, + + .UpdateCapsule = mock_efi_unsupported, + .QueryCapsuleCapabilities = mock_efi_unsupported, + + .QueryVariableInfo = mock_efi_unsupported, }; -EFI_SYSTEM_TABLE mock_st = { - .Hdr = { 0, }, +EFI_SYSTEM_TABLE mock_st, mock_default_st = { + .Hdr = { + .Signature = EFI_SYSTEM_TABLE_SIGNATURE, + .Revision = EFI_1_10_SYSTEM_TABLE_REVISION, + .HeaderSize = sizeof(EFI_SYSTEM_TABLE), + }, .BootServices = &mock_bs, .RuntimeServices = &mock_rt, }; -void CONSTRUCTOR init_efi_system_table(void) +void CONSTRUCTOR +init_efi_system_table(void) +{ + static bool once = true; + if (once) { + once = false; + reset_efi_system_table(); + } +} + +void +reset_efi_system_table(void) { ST = &mock_st; BS = &mock_bs; RT = &mock_rt; + + memcpy(&mock_bs, &mock_default_bs, sizeof(mock_bs)); + memcpy(&mock_rt, &mock_default_rt, sizeof(mock_rt)); + memcpy(&mock_st, &mock_default_st, sizeof(mock_st)); } EFI_STATUS EFIAPI -- cgit v1.2.3 From 2c9eebcf6ddd198c5ba49d784c4536d05023c28b Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 22 Jul 2021 15:45:23 -0400 Subject: tests: add a mock implementation of {Get,Set}Variable and tests for it Some tests will need variables, and so we need a mock implementation of the various calls relating to them. This patch adds implementations for the EFI Runtime Services calls GetVariable(), SetVariable(), GetNextVariableName(), and QueryVariableInfo(). Additionally, it enforces tunable limits on storage for variables, and (with only a little work) the limits can be different for SetVariable() vs what is returned by QueryVariableInfo(). That is, it can lie to you like real systems do. Signed-off-by: Peter Jones --- .gitignore | 1 + include/mock-variables.h | 170 ++++ include/test-data-efivars-0.h | 554 +++++++++++ include/test.h | 124 ++- include/test.mk | 8 +- mock-variables.c | 1022 ++++++++++++++++++++ ..._PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 | Bin 0 -> 132 bytes .../AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 | Bin 0 -> 12 bytes ...ITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e | Bin 0 -> 28 bytes .../Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 122 bytes .../db-d719b2cb-3d3a-4596-a3bc-dad00e67656f | Bin 0 -> 7421 bytes .../dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 6326 bytes .../dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f | Bin 0 -> 3728 bytes ...dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 3728 bytes ..._PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 | Bin 0 -> 132 bytes .../AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 | Bin 0 -> 12 bytes ...ITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e | Bin 0 -> 28 bytes ...MITSESetup-c811fa38-42c8-4579-a9bb-60e94eddfb34 | Bin 0 -> 85 bytes .../AOD_SETUP-5ed15dc0-edef-4161-9151-6014c4cc630c | Bin 0 -> 437 bytes ...AmdAcpiVar-79941ecd-ed36-49d0-8124-e4c31ac75cd4 | Bin 0 -> 14 bytes .../AmdSetup-3a997502-647a-4c82-998e-52ef9486a247 | Bin 0 -> 1557 bytes ...teCountVar-81c76078-bfde-4368-9790-570914c01a65 | Bin 0 -> 8 bytes ...SyncFlagNv-ad3f6761-f0a3-46c8-a4cb-19b70ffdb305 | Bin 0 -> 8 bytes .../AsbkpInfo-cb825795-feb1-4c0b-894f-cc70f8064395 | Bin 0 -> 12 bytes ...ExtFancard-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 5 bytes ...upFeatures-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 18 bytes ...SetupOneof-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 40 bytes ...susNodePsu-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 5 bytes ...nSetupData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 340 bytes ...sRomLayout-7186d975-2dba-4413-81a8-9f1538faef5e | Bin 0 -> 20 bytes ...DataBackup-1111b056-c5e9-40ca-aba3-ec172533d814 | Bin 0 -> 9 bytes ...DetectData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 40 bytes ...osEventLog-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 | Bin 0 -> 884 bytes .../Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 122 bytes .../Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 124 bytes .../Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 120 bytes .../Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 59 bytes .../Boot0004-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 67 bytes .../Boot0005-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 63 bytes .../Boot0006-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 158 bytes ...ootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 6 bytes ...ootFromUSB-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 5 bytes ...ionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 8 bytes .../BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 16 bytes ...OSfailflag-c89dc9c7-5105-472c-a743-b1621e142b41 | Bin 0 -> 6 bytes .../ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 282 bytes .../ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 276 bytes .../ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 40 bytes .../ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 40 bytes ...rentPolicy-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 5 bytes ...tBootOrder-45cf35f6-0d6e-4d04-856a-0370a5b16f53 | Bin 0 -> 10 bytes ...mentModeNv-97e8965f-c761-4f48-b6e4-9ffa9cb2a2d6 | Bin 0 -> 5 bytes ...CoreStatus-29749bad-401b-4f6d-b124-cece8c590c48 | Bin 0 -> 5 bytes .../EnWpData-cbab171f-f356-4009-baaa-6628353a0a29 | Bin 0 -> 68 bytes .../ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 40 bytes ...ayoutOrder-4db88a62-6721-47a0-9082-280b00323594 | Bin 0 -> 14 bytes ...ActiveFlag-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 | Bin 0 -> 5 bytes ...BootOption-b540a530-6978-4da7-91cb-7207d764d262 | Bin 0 -> 146 bytes ...stBootFlag-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 5 bytes ...dSmartInfo-a6f44860-b2e8-4fda-bd45-78368994b6ae | Bin 0 -> 6 bytes .../HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0 | Bin 0 -> 12 bytes ...RecSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 6 bytes .../KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 2651 bytes ...KEKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 3577 bytes ...TPSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes ...erSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes ...vkSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes ...l_SiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes ...kuSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes ...inSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b | Bin 0 -> 8 bytes .../LastBoot-b540a530-6978-4da7-91cb-7207d764d262 | Bin 0 -> 39 bytes ...mTableSize-4b3082a3-80c6-4d7e-9cd0-583917265df1 | Bin 0 -> 6 bytes ...estControl-e20939be-32d4-41be-a150-897f85d49829 | Bin 0 -> 5 bytes ...ontrolLock-bb983ccf-151d-40e1-a07b-4a17be168292 | Bin 0 -> 5 bytes .../MokList-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 924 bytes .../MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 924 bytes .../MokListX-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 80 bytes ...MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 80 bytes ...nicCounter-01368881-c4ad-4b1d-b631-d57a8ec8db6b | Bin 0 -> 8 bytes .../MyFav-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 | Bin 0 -> 82 bytes ...RAM_Verify-15a9dd61-e4f8-4a99-80db-353b13d76490 | Bin 0 -> 6 bytes ...rkStackVar-d1405d16-7afc-4695-bb12-41459d3695a2 | Bin 0 -> 12 bytes .../NvHdd0-e57abcbd-9456-4639-8f65-06aab41d840f | Bin 0 -> 32 bytes .../NvHdd8-e57abcbd-9456-4639-8f65-06aab41d840f | Bin 0 -> 26 bytes ...ndications-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 12 bytes ...sSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 12 bytes ...PCI_COMMON-aca9f304-21e2-4852-9875-7ff4881d67a5 | Bin 0 -> 12 bytes .../PK-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 1094 bytes .../PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 890 bytes ...SataModVar-5e9a565f-cdc0-413b-ad13-1fe8713ffdcd | Bin 0 -> 7 bytes ...atformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 10 bytes ...mLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 60 bytes ...PreVgaInfo-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 8 bytes .../RsdpAddr-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 12 bytes ...SIDSUPPORT-7d3dceee-cbce-4ea7-8709-6e552f1edbde | Bin 0 -> 5 bytes .../SbatLevel-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 22 bytes ...batLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 | Bin 0 -> 22 bytes ...SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 5 bytes ...eBootSetup-7b59104a-c00d-4158-87ff-f04d6396a915 | Bin 0 -> 11 bytes .../Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 468 bytes ...tupLedData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 6 bytes .../SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 5 bytes ...ureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 148 bytes ...PointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 | Bin 0 -> 12 bytes ...atchBuffer-4b3082a3-80c6-4d7e-9cd0-583917265df1 | Bin 0 -> 12 bytes ...PointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 | Bin 0 -> 12 bytes ...tdDefaults-4599d26f-1a11-49b8-b91f-858745cff824 | Bin 0 -> 7179 bytes ...RBIOSFLAGS-7d3dceee-cbce-4ea7-8709-6e552f1edbde | Bin 0 -> 10 bytes .../Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 6 bytes ...ootBridges-fb5703f5-f8a7-f401-18b4-3f108deb2612 | Bin 0 -> 5 bytes ...mServFlags-7d3dceee-cbce-4ea7-8709-6e552f1edbde | Bin 0 -> 12 bytes ...UsbSupport-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 | Bin 0 -> 53 bytes ...TINGS_NAME-c05fba7d-7a92-49e0-bcee-233b14dca803 | Bin 0 -> 5663 bytes ...VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 5 bytes .../WpBufAddr-cba83c4a-a5fc-48a8-b3a6-d33636166544 | Bin 0 -> 8 bytes ...OnceStatus-4b3082a3-80c6-4d7e-9cd0-583917265df1 | Bin 0 -> 68 bytes .../db-d719b2cb-3d3a-4596-a3bc-dad00e67656f | Bin 0 -> 7421 bytes .../dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 6326 bytes .../dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f | Bin 0 -> 3728 bytes ...dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c | Bin 0 -> 3728 bytes test-mock-variables.c | 421 ++++++++ test.c | 8 + 122 files changed, 2299 insertions(+), 9 deletions(-) create mode 100644 include/mock-variables.h create mode 100644 include/test-data-efivars-0.h create mode 100644 mock-variables.c create mode 100644 test-data/efivars-0/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 create mode 100644 test-data/efivars-0/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 create mode 100644 test-data/efivars-0/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e create mode 100644 test-data/efivars-0/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-0/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f create mode 100644 test-data/efivars-0/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-0/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f create mode 100644 test-data/efivars-0/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 create mode 100644 test-data/efivars-1/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 create mode 100644 test-data/efivars-1/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e create mode 100644 test-data/efivars-1/AMITSESetup-c811fa38-42c8-4579-a9bb-60e94eddfb34 create mode 100644 test-data/efivars-1/AOD_SETUP-5ed15dc0-edef-4161-9151-6014c4cc630c create mode 100644 test-data/efivars-1/AmdAcpiVar-79941ecd-ed36-49d0-8124-e4c31ac75cd4 create mode 100644 test-data/efivars-1/AmdSetup-3a997502-647a-4c82-998e-52ef9486a247 create mode 100644 test-data/efivars-1/AmiHardwareSignatureSetupUpdateCountVar-81c76078-bfde-4368-9790-570914c01a65 create mode 100644 test-data/efivars-1/ApSyncFlagNv-ad3f6761-f0a3-46c8-a4cb-19b70ffdb305 create mode 100644 test-data/efivars-1/AsbkpInfo-cb825795-feb1-4c0b-894f-cc70f8064395 create mode 100644 test-data/efivars-1/AsusExtFancard-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/AsusFanSetupFeatures-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/AsusHwmSetupOneof-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/AsusNodePsu-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/AsusQFanSetupData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/AsusRomLayout-7186d975-2dba-4413-81a8-9f1538faef5e create mode 100644 test-data/efivars-1/AsusSetupDataBackup-1111b056-c5e9-40ca-aba3-ec172533d814 create mode 100644 test-data/efivars-1/AutoDetectData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/BiosEventLog-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 create mode 100644 test-data/efivars-1/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0004-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0005-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Boot0006-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/BootFromUSB-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/CMOSfailflag-c89dc9c7-5105-472c-a743-b1621e142b41 create mode 100644 test-data/efivars-1/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/CurrentPolicy-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/DefaultBootOrder-45cf35f6-0d6e-4d04-856a-0370a5b16f53 create mode 100644 test-data/efivars-1/DeploymentModeNv-97e8965f-c761-4f48-b6e4-9ffa9cb2a2d6 create mode 100644 test-data/efivars-1/DownCoreStatus-29749bad-401b-4f6d-b124-cece8c590c48 create mode 100644 test-data/efivars-1/EnWpData-cbab171f-f356-4009-baaa-6628353a0a29 create mode 100644 test-data/efivars-1/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/FPLayoutOrder-4db88a62-6721-47a0-9082-280b00323594 create mode 100644 test-data/efivars-1/FTMActiveFlag-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 create mode 100644 test-data/efivars-1/FastBootOption-b540a530-6978-4da7-91cb-7207d764d262 create mode 100644 test-data/efivars-1/FirstBootFlag-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/HddSmartInfo-a6f44860-b2e8-4fda-bd45-78368994b6ae create mode 100644 test-data/efivars-1/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0 create mode 100644 test-data/efivars-1/HwErrRecSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/KEKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/Kernel_ATPSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/Kernel_DriverSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/Kernel_RvkSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/Kernel_SiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/Kernel_SkuSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/Kernel_WinSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b create mode 100644 test-data/efivars-1/LastBoot-b540a530-6978-4da7-91cb-7207d764d262 create mode 100644 test-data/efivars-1/MaximumTableSize-4b3082a3-80c6-4d7e-9cd0-583917265df1 create mode 100644 test-data/efivars-1/MemoryOverwriteRequestControl-e20939be-32d4-41be-a150-897f85d49829 create mode 100644 test-data/efivars-1/MemoryOverwriteRequestControlLock-bb983ccf-151d-40e1-a07b-4a17be168292 create mode 100644 test-data/efivars-1/MokList-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/MokListX-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/MonotonicCounter-01368881-c4ad-4b1d-b631-d57a8ec8db6b create mode 100644 test-data/efivars-1/MyFav-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 create mode 100644 test-data/efivars-1/NVRAM_Verify-15a9dd61-e4f8-4a99-80db-353b13d76490 create mode 100644 test-data/efivars-1/NetworkStackVar-d1405d16-7afc-4695-bb12-41459d3695a2 create mode 100644 test-data/efivars-1/NvHdd0-e57abcbd-9456-4639-8f65-06aab41d840f create mode 100644 test-data/efivars-1/NvHdd8-e57abcbd-9456-4639-8f65-06aab41d840f create mode 100644 test-data/efivars-1/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/PCI_COMMON-aca9f304-21e2-4852-9875-7ff4881d67a5 create mode 100644 test-data/efivars-1/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/PcieSataModVar-5e9a565f-cdc0-413b-ad13-1fe8713ffdcd create mode 100644 test-data/efivars-1/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/PreVgaInfo-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/RsdpAddr-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/SIDSUPPORT-7d3dceee-cbce-4ea7-8709-6e552f1edbde create mode 100644 test-data/efivars-1/SbatLevel-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 create mode 100644 test-data/efivars-1/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/SecureBootSetup-7b59104a-c00d-4158-87ff-f04d6396a915 create mode 100644 test-data/efivars-1/Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/SetupLedData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/SmbiosEntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 create mode 100644 test-data/efivars-1/SmbiosScratchBuffer-4b3082a3-80c6-4d7e-9cd0-583917265df1 create mode 100644 test-data/efivars-1/SmbiosV3EntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 create mode 100644 test-data/efivars-1/StdDefaults-4599d26f-1a11-49b8-b91f-858745cff824 create mode 100644 test-data/efivars-1/TPMPERBIOSFLAGS-7d3dceee-cbce-4ea7-8709-6e552f1edbde create mode 100644 test-data/efivars-1/Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/TotalNumberOfRootBridges-fb5703f5-f8a7-f401-18b4-3f108deb2612 create mode 100644 test-data/efivars-1/TpmServFlags-7d3dceee-cbce-4ea7-8709-6e552f1edbde create mode 100644 test-data/efivars-1/UsbSupport-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 create mode 100644 test-data/efivars-1/VARSTORE_OCMR_SETTINGS_NAME-c05fba7d-7a92-49e0-bcee-233b14dca803 create mode 100644 test-data/efivars-1/VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/WpBufAddr-cba83c4a-a5fc-48a8-b3a6-d33636166544 create mode 100644 test-data/efivars-1/WriteOnceStatus-4b3082a3-80c6-4d7e-9cd0-583917265df1 create mode 100644 test-data/efivars-1/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f create mode 100644 test-data/efivars-1/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-data/efivars-1/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f create mode 100644 test-data/efivars-1/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c create mode 100644 test-mock-variables.c (limited to 'include') diff --git a/.gitignore b/.gitignore index 727951a3..7fc55bce 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,6 @@ Make.local shim_cert.h /test-* !/test-*.c +!/test-data/ /test-random.h version.c diff --git a/include/mock-variables.h b/include/mock-variables.h new file mode 100644 index 00000000..7ba00847 --- /dev/null +++ b/include/mock-variables.h @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * mock-variables.h - a mock GetVariable/SetVariable/GNVN/etc + * implementation for testing. + * Copyright Peter Jones + */ + +#ifndef SHIM_MOCK_VARIABLES_H_ +#define SHIM_MOCK_VARIABLES_H_ + +#include "test.h" + +EFI_STATUS EFIAPI mock_get_variable(CHAR16 *name, EFI_GUID *guid, UINT32 *attrs, + UINTN *size, VOID *data); +EFI_STATUS EFIAPI mock_get_next_variable_name(UINTN *size, CHAR16 *name, + EFI_GUID *guid); +EFI_STATUS EFIAPI mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, + UINTN size, VOID *data); +EFI_STATUS EFIAPI mock_query_variable_info(UINT32 attrs, + UINT64 *max_var_storage, + UINT64 *remaining_var_storage, + UINT64 *max_var_size); + +struct mock_variable_limits { + UINT32 attrs; + UINT64 *max_var_storage; + UINT64 *remaining_var_storage; + UINT64 *max_var_size; + EFI_STATUS status; + + list_t list; +}; + +typedef enum { + MOCK_SORT_DESCENDING, + MOCK_SORT_PREPEND, + MOCK_SORT_APPEND, + MOCK_SORT_ASCENDING, + MOCK_SORT_MAX_SENTINEL +} mock_sort_policy_t; + +extern mock_sort_policy_t mock_variable_sort_policy; + +extern list_t mock_default_variable_limits; +extern list_t *mock_qvi_limits; +extern list_t *mock_sv_limits; + +struct mock_variable { + CHAR16 *name; + EFI_GUID guid; + void *data; + size_t size; + uint32_t attrs; + + list_t list; +}; + +extern list_t mock_variables; + +static inline void +dump_mock_variables(const char * const file, + const int line, + const char * const func) +{ + list_t *pos = NULL; + printf("%s:%d:%s(): dumping variables\n", file, line, func); + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + + var = list_entry(pos, struct mock_variable, list); + printf("%s:%d:%s(): "GUID_FMT"-%s\n", file, line, func, + GUID_ARGS(var->guid), Str2str(var->name)); + } +} + +static inline void +dump_mock_variables_if_wrong(const char * const file, + const int line, + const char * const func, + EFI_GUID *guid, CHAR16 *first) +{ + UINTN size = 0; + CHAR16 buf[8192] = { 0, }; + EFI_STATUS status; + + size = sizeof(buf); + buf[0] = L'\0'; + status = RT->GetNextVariableName(&size, buf, guid); + if (EFI_ERROR(status)) { + printf("%s:%d:%s() Can't dump variables: %lx\n", + __FILE__, __LINE__, __func__, + (unsigned long)status); + return; + } + buf[size] = L'\0'; + if (StrCmp(buf, first) == 0) + return; + printf("%s:%d:%s():expected \"%s\" but got \"%s\". Variables:\n", + file, line, func, Str2str(first), Str2str(buf)); + dump_mock_variables(file, line, func); +} + +void mock_load_variables(const char *const dirname, const char *filters[], + bool filter_out); +void mock_install_query_variable_info(void); +void mock_uninstall_query_variable_info(void); +void mock_reset_variables(void); +void mock_finalize_vars(void); + +typedef enum { + NONE = 0, + CREATE, + DELETE, + APPEND, + REPLACE, +} mock_variable_op_t; + +typedef EFI_STATUS (mock_set_variable_pre_hook_t)(CHAR16 *name, EFI_GUID *guid, + UINT32 attrs, UINTN size, + VOID *data); +extern mock_set_variable_pre_hook_t *mock_set_variable_pre_hook; + +typedef void (mock_set_variable_post_hook_t)(CHAR16 *name, EFI_GUID *guid, + UINT32 attrs, UINTN size, + VOID *data, EFI_STATUS *status, + mock_variable_op_t op, + const char * const file, + const int line, + const char * const func); +extern mock_set_variable_post_hook_t *mock_set_variable_post_hook; + +typedef EFI_STATUS (mock_get_variable_pre_hook_t)(CHAR16 *name, EFI_GUID *guid, + UINT32 *attrs, UINTN *size, + VOID *data); +extern mock_get_variable_pre_hook_t *mock_get_variable_pre_hook; + +typedef void (mock_get_variable_post_hook_t)(CHAR16 *name, EFI_GUID *guid, + UINT32 *attrs, UINTN *size, + VOID *data, EFI_STATUS *status, + const char * const file, + const int line, + const char * const func); +extern mock_get_variable_post_hook_t *mock_get_variable_post_hook; + +typedef EFI_STATUS (mock_get_next_variable_name_pre_hook_t)(UINTN *size, + CHAR16 *name, + EFI_GUID *guid); +extern mock_get_next_variable_name_pre_hook_t + *mock_get_next_variable_name_pre_hook; + +typedef void (mock_get_next_variable_name_post_hook_t)( + UINTN *size, CHAR16 *name, EFI_GUID *guid, + EFI_STATUS *status, const char * const file, + const int line, const char * const func); +extern mock_get_next_variable_name_post_hook_t + *mock_get_next_variable_name_post_hook; + +typedef EFI_STATUS (mock_query_variable_info_pre_hook_t)( + UINT32 attrs, UINT64 *max_var_storage, + UINT64 *remaining_var_storage, UINT64 *max_var_size); +extern mock_query_variable_info_pre_hook_t *mock_query_variable_info_pre_hook; + +typedef void (mock_query_variable_info_post_hook_t)( + UINT32 attrs, UINT64 *max_var_storage, UINT64 *remaining_var_storage, + UINT64 *max_var_size, EFI_STATUS *status, const char * const file, + const int line, const char * const func); +extern mock_query_variable_info_post_hook_t *mock_query_variable_info_post_hook; + +#endif /* !SHIM_MOCK_VARIABLES_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/test-data-efivars-0.h b/include/test-data-efivars-0.h new file mode 100644 index 00000000..b3e7c8c5 --- /dev/null +++ b/include/test-data-efivars-0.h @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-data-efivars-0.h - test data + * Copyright Peter Jones + */ + +#ifndef TEST_DATA_EFIVARS_0_H_ +#define TEST_DATA_EFIVARS_0_H_ + +static const unsigned char test_data_efivars_0_Boot0000[] = { + 0x01, 0x00, 0x00, 0x00, 0x62, 0x00, 0x46, 0x00, 0x65, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x00, 0x00, 0x04, 0x01, 0x2a, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x58, 0x7f, 0x92, + 0x54, 0xdc, 0xf4, 0x4f, 0x82, 0x1c, 0xd2, 0x9b, 0x59, 0xc4, 0x8a, 0xb1, + 0x02, 0x02, 0x04, 0x04, 0x34, 0x00, 0x5c, 0x00, 0x45, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x5c, 0x00, 0x46, 0x00, 0x45, 0x00, 0x44, 0x00, 0x4f, 0x00, + 0x52, 0x00, 0x41, 0x00, 0x5c, 0x00, 0x53, 0x00, 0x48, 0x00, 0x49, 0x00, + 0x4d, 0x00, 0x58, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x45, 0x00, + 0x46, 0x00, 0x49, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x04, 0x00 +}; + +static const unsigned char test_data_efivars_0_dbDefault[] = { + 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, + 0x5c, 0x2b, 0xf0, 0x72, 0x82, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x03, 0x00, 0x00, 0x91, 0x30, 0x05, 0x3b, 0x9f, 0x6c, 0xcc, 0x04, + 0xb1, 0xac, 0xe2, 0xa5, 0x1e, 0x3b, 0xe5, 0xf5, 0x30, 0x82, 0x03, 0x52, + 0x30, 0x82, 0x02, 0x3a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0xda, + 0x83, 0xb9, 0x90, 0x42, 0x2e, 0xbc, 0x8c, 0x44, 0x1f, 0x8d, 0x8b, 0x03, + 0x9a, 0x65, 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x41, 0x53, 0x55, 0x53, 0x54, + 0x65, 0x4b, 0x20, 0x4d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x61, + 0x72, 0x64, 0x20, 0x53, 0x57, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x31, 0x31, 0x32, 0x32, 0x36, 0x32, 0x33, 0x33, 0x35, 0x30, + 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x31, 0x32, 0x32, 0x36, 0x32, 0x33, + 0x33, 0x35, 0x30, 0x34, 0x5a, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x41, 0x53, 0x55, 0x53, 0x54, 0x65, + 0x4b, 0x20, 0x4d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x61, 0x72, + 0x64, 0x20, 0x53, 0x57, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x8c, 0xf6, 0xa6, 0xeb, 0x77, 0xfc, 0x83, + 0x8a, 0xa4, 0x9f, 0xd5, 0xf8, 0xcf, 0x3f, 0x37, 0xf2, 0x6e, 0x2d, 0x0a, + 0x62, 0xc5, 0xd8, 0x9b, 0x1d, 0x16, 0x0b, 0x22, 0x7f, 0x29, 0x5f, 0x3a, + 0x26, 0xdf, 0x53, 0x97, 0x8c, 0x78, 0x94, 0x19, 0x90, 0x42, 0x73, 0x0f, + 0x85, 0xc2, 0xff, 0xa4, 0x85, 0x7c, 0x81, 0x2e, 0x0b, 0x51, 0xba, 0x56, + 0x23, 0x27, 0x92, 0x3d, 0xa3, 0xf2, 0xdc, 0xe2, 0x77, 0x84, 0x9e, 0x50, + 0xbe, 0x8a, 0xeb, 0x51, 0x34, 0xa4, 0xf8, 0xef, 0x5d, 0xd7, 0x51, 0xfe, + 0x70, 0x42, 0x4c, 0x42, 0x06, 0xef, 0x69, 0x2c, 0xa2, 0xd3, 0x25, 0xe1, + 0x26, 0x57, 0x23, 0x85, 0x6d, 0xd0, 0xa7, 0x7b, 0xc0, 0x45, 0x28, 0x7e, + 0x89, 0xd5, 0xb4, 0x0a, 0xeb, 0xaf, 0x41, 0x79, 0x21, 0xd2, 0xd7, 0x00, + 0xec, 0x48, 0xf9, 0x44, 0xf6, 0x5b, 0xbe, 0xb6, 0x25, 0x24, 0xf0, 0x8e, + 0x2e, 0xb4, 0x52, 0x3e, 0xe1, 0x0e, 0xc1, 0xa4, 0x67, 0xea, 0xfe, 0xe5, + 0x93, 0xcc, 0xb9, 0xc4, 0x36, 0x21, 0xcb, 0x54, 0xfa, 0xaf, 0x9d, 0x9c, + 0x85, 0x78, 0xcc, 0xe5, 0x88, 0xf3, 0x84, 0x0c, 0x67, 0xdb, 0x26, 0x69, + 0x58, 0xca, 0xde, 0x47, 0x34, 0xec, 0xcf, 0x2f, 0xb6, 0x49, 0x59, 0xb5, + 0x56, 0xdb, 0x58, 0x45, 0x7b, 0x21, 0x9d, 0x99, 0x0b, 0x5f, 0xde, 0x57, + 0x16, 0xa6, 0xab, 0xc8, 0x79, 0x3f, 0x9d, 0x76, 0x89, 0xe2, 0x09, 0xf9, + 0x8d, 0xe2, 0x63, 0x37, 0xfc, 0x74, 0xea, 0x73, 0x7e, 0x70, 0xac, 0x15, + 0x16, 0xa5, 0xed, 0x88, 0x60, 0x5f, 0x33, 0xed, 0x94, 0x9e, 0x0a, 0x05, + 0xde, 0xc7, 0x85, 0xc3, 0xc1, 0x7a, 0x54, 0xfb, 0x4e, 0xcb, 0xcb, 0xe8, + 0x5e, 0x44, 0x7c, 0x39, 0xdb, 0x2d, 0xb2, 0xb7, 0x6c, 0xce, 0xca, 0x2f, + 0x63, 0x9d, 0x16, 0x4e, 0xa6, 0xe5, 0xef, 0xd6, 0xcf, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x62, 0x06, 0x03, 0x55, 0x1d, + 0x01, 0x04, 0x5b, 0x30, 0x59, 0x80, 0x10, 0x56, 0xb0, 0x8b, 0x2a, 0xa7, + 0xfe, 0xcc, 0xf1, 0x0c, 0xed, 0x87, 0x62, 0xdc, 0xd5, 0x1d, 0xc3, 0xa1, + 0x33, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x26, 0x41, 0x53, 0x55, 0x53, 0x54, 0x65, 0x4b, 0x20, 0x4d, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x53, 0x57, + 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x82, 0x10, 0xda, 0x83, 0xb9, 0x90, 0x42, 0x2e, + 0xbc, 0x8c, 0x44, 0x1f, 0x8d, 0x8b, 0x03, 0x9a, 0x65, 0xa2, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x02, 0xcf, 0x52, 0x6f, 0x0b, 0x91, + 0xeb, 0xe4, 0x3b, 0xb2, 0x70, 0x0c, 0x07, 0x2d, 0x79, 0x80, 0x01, 0x9e, + 0x4b, 0x4d, 0x92, 0xbb, 0xdc, 0x9e, 0xe5, 0xe5, 0x31, 0x85, 0xe3, 0x9a, + 0x75, 0xed, 0xca, 0xde, 0x8c, 0xee, 0x28, 0x34, 0x01, 0x83, 0x14, 0x47, + 0x9e, 0x3a, 0xd4, 0x43, 0x5b, 0x2c, 0xc4, 0x41, 0xc8, 0x40, 0x7d, 0xb5, + 0x08, 0x76, 0x86, 0x80, 0x2b, 0xa8, 0x00, 0x9f, 0xb7, 0xd3, 0xb1, 0xe6, + 0x60, 0x5c, 0x32, 0xb0, 0xa0, 0x01, 0x0f, 0xba, 0x36, 0x8b, 0xb7, 0xb5, + 0x4e, 0x87, 0xd5, 0xb7, 0x0a, 0x2c, 0xbd, 0xbc, 0x6a, 0x43, 0x3c, 0xee, + 0x76, 0x7c, 0x76, 0x20, 0xed, 0x39, 0x91, 0xa8, 0xbf, 0x70, 0x1e, 0xd6, + 0xa8, 0x1a, 0x3e, 0x81, 0x36, 0x6b, 0x7d, 0x1d, 0x8d, 0xf6, 0xf8, 0xaf, + 0x5b, 0x38, 0x53, 0x6a, 0x04, 0x0d, 0x7e, 0xae, 0x4d, 0xee, 0xab, 0x02, + 0xd4, 0xa4, 0xa2, 0xa9, 0xcf, 0xb6, 0xe3, 0x66, 0xa3, 0xca, 0x4d, 0x5d, + 0xd4, 0x18, 0x61, 0x4d, 0xda, 0x83, 0x28, 0x4e, 0xaa, 0x2a, 0xaf, 0xda, + 0xeb, 0xdf, 0x2a, 0x20, 0xbd, 0x78, 0x80, 0xef, 0xd1, 0xb0, 0xdd, 0x9b, + 0x77, 0xdb, 0xc9, 0x25, 0x39, 0x4b, 0xcf, 0xa2, 0x86, 0x1a, 0xac, 0xcc, + 0x32, 0xe7, 0x87, 0xd4, 0x59, 0xb2, 0x03, 0xc4, 0x69, 0x02, 0x8f, 0x17, + 0xc9, 0xde, 0x52, 0xcb, 0xe7, 0xab, 0xb8, 0x35, 0xc5, 0xf8, 0x33, 0x06, + 0x03, 0x93, 0x52, 0xcf, 0xb3, 0x68, 0xd2, 0xb3, 0x5c, 0x1c, 0xe8, 0x19, + 0xfe, 0x75, 0x26, 0xed, 0xd1, 0x65, 0x72, 0x13, 0x4d, 0x69, 0x34, 0x5a, + 0x9b, 0x0c, 0xb4, 0xe3, 0x56, 0x53, 0x3c, 0xb4, 0x67, 0x27, 0xf8, 0xfa, + 0xd3, 0x20, 0xda, 0x37, 0x58, 0xf6, 0xad, 0xe2, 0x82, 0x59, 0xa2, 0xb8, + 0x22, 0x2f, 0x9e, 0x56, 0xfe, 0xbc, 0x17, 0x49, 0x1d, 0xaf, 0xa1, 0x59, + 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, + 0xf0, 0x72, 0x79, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x03, + 0x00, 0x00, 0x91, 0x30, 0x05, 0x3b, 0x9f, 0x6c, 0xcc, 0x04, 0xb1, 0xac, + 0xe2, 0xa5, 0x1e, 0x3b, 0xe5, 0xf5, 0x30, 0x82, 0x03, 0x49, 0x30, 0x82, + 0x02, 0x31, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0xb8, 0xe5, 0x81, + 0xe4, 0xdf, 0x77, 0xa5, 0xbb, 0x42, 0x82, 0xd5, 0xcc, 0xfc, 0x00, 0xc0, + 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x30, 0x2e, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x23, 0x41, 0x53, 0x55, 0x53, 0x54, 0x65, 0x4b, + 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x20, 0x53, 0x57, + 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x31, 0x32, + 0x32, 0x37, 0x30, 0x30, 0x31, 0x38, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x33, + 0x31, 0x31, 0x32, 0x32, 0x37, 0x30, 0x30, 0x31, 0x38, 0x35, 0x32, 0x5a, + 0x30, 0x2e, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x23, 0x41, 0x53, 0x55, 0x53, 0x54, 0x65, 0x4b, 0x20, 0x4e, 0x6f, 0x74, + 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x20, 0x53, 0x57, 0x20, 0x4b, 0x65, 0x79, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9e, 0x61, 0xfa, + 0x74, 0x2c, 0x2a, 0x88, 0x17, 0xc4, 0xbd, 0x77, 0x19, 0x0d, 0xb3, 0x33, + 0x27, 0x0c, 0x0e, 0x94, 0xec, 0xb0, 0x8b, 0x71, 0xb3, 0x08, 0x77, 0xb7, + 0xd2, 0x08, 0x9d, 0x32, 0x4f, 0x5c, 0xf7, 0x0c, 0xcf, 0xe0, 0x29, 0x53, + 0x56, 0xed, 0x24, 0x91, 0xd8, 0xbd, 0x53, 0x2a, 0x89, 0x89, 0x8c, 0x74, + 0x28, 0xab, 0x16, 0x2d, 0x4f, 0x9b, 0x65, 0xfc, 0x63, 0x7d, 0xed, 0x23, + 0xb6, 0x97, 0x5c, 0x6d, 0x04, 0xe4, 0x15, 0x7f, 0xdc, 0xf8, 0xba, 0x6b, + 0x08, 0xcc, 0xc9, 0x21, 0xe9, 0xb5, 0xde, 0x8e, 0x03, 0x28, 0x12, 0x63, + 0xf0, 0x6a, 0xb6, 0xe5, 0xdf, 0x1d, 0x72, 0x28, 0xcc, 0x64, 0xd6, 0x63, + 0x66, 0x2f, 0x04, 0x52, 0x6a, 0x1d, 0x25, 0x7d, 0xc7, 0xbd, 0xe0, 0x78, + 0xfb, 0x0c, 0xb7, 0x37, 0xe5, 0xae, 0xf7, 0x0d, 0xd6, 0xb5, 0xb4, 0xbf, + 0xf5, 0xf1, 0xc6, 0x82, 0x56, 0x78, 0x5c, 0xa8, 0xf3, 0x53, 0x2e, 0xf5, + 0xec, 0x15, 0x3f, 0x12, 0x62, 0x2f, 0xeb, 0xb6, 0x79, 0x79, 0x86, 0xac, + 0x76, 0xff, 0xb6, 0x66, 0x45, 0xf5, 0x33, 0xda, 0xdd, 0x25, 0xd6, 0xa7, + 0xbf, 0xf8, 0xd9, 0xdb, 0xd3, 0xf1, 0xfa, 0xce, 0x0e, 0x22, 0x30, 0xd7, + 0xd4, 0x80, 0x02, 0xbd, 0xd3, 0x2c, 0x1e, 0xec, 0x46, 0x2e, 0x2f, 0xca, + 0x0f, 0x7a, 0xfa, 0xb9, 0x5c, 0xff, 0x2b, 0x16, 0xc6, 0x6a, 0x6b, 0x8d, + 0x94, 0x64, 0x92, 0x7e, 0xf9, 0x55, 0xee, 0x96, 0x00, 0x4d, 0x04, 0x2e, + 0x4b, 0x15, 0xed, 0xf1, 0x08, 0x49, 0x6a, 0x07, 0x86, 0x69, 0xc8, 0xc5, + 0x64, 0xfa, 0xad, 0x2c, 0x4f, 0x02, 0x50, 0xe4, 0x1f, 0x83, 0xc7, 0x2f, + 0x19, 0x9f, 0xe8, 0xa5, 0x62, 0xd9, 0x51, 0x32, 0x18, 0xb6, 0x83, 0xca, + 0x08, 0x0a, 0xa1, 0xab, 0xa7, 0x65, 0x70, 0x9c, 0x1e, 0x48, 0xc3, 0x0f, + 0x49, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x5f, + 0x06, 0x03, 0x55, 0x1d, 0x01, 0x04, 0x58, 0x30, 0x56, 0x80, 0x10, 0x00, + 0x65, 0x11, 0xe3, 0xca, 0x0f, 0x96, 0xe8, 0x8d, 0x5b, 0x04, 0xa4, 0xe7, + 0xfe, 0xce, 0xaa, 0xa1, 0x30, 0x30, 0x2e, 0x31, 0x2c, 0x30, 0x2a, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x41, 0x53, 0x55, 0x53, 0x54, 0x65, + 0x4b, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x20, 0x53, + 0x57, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x82, 0x10, 0xb8, 0xe5, 0x81, 0xe4, 0xdf, + 0x77, 0xa5, 0xbb, 0x42, 0x82, 0xd5, 0xcc, 0xfc, 0x00, 0xc0, 0x71, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x31, 0x18, 0xf4, 0xee, 0xe3, + 0x72, 0xba, 0xbe, 0x33, 0x44, 0x61, 0x74, 0x19, 0x1f, 0x66, 0xac, 0x5c, + 0xfd, 0x1d, 0x9a, 0x26, 0x75, 0xd0, 0x14, 0xcd, 0x68, 0x38, 0xb3, 0xa8, + 0x3f, 0x4f, 0xb4, 0x4a, 0xe9, 0x1e, 0x21, 0xf2, 0xc9, 0xee, 0x37, 0x96, + 0x26, 0xbe, 0x1d, 0x58, 0x9b, 0xad, 0x21, 0xce, 0x58, 0x79, 0x53, 0xd3, + 0xff, 0x38, 0xef, 0x8f, 0x22, 0xcd, 0x90, 0x0e, 0xc6, 0x32, 0x21, 0x75, + 0x9b, 0x5a, 0xab, 0xaf, 0x08, 0xff, 0x05, 0xcd, 0x2b, 0xf8, 0x8c, 0xe7, + 0x97, 0x47, 0xbb, 0x78, 0xe4, 0x5f, 0x56, 0x47, 0xd2, 0xbc, 0xc8, 0xa5, + 0x95, 0xcb, 0x76, 0x89, 0x5c, 0x65, 0x24, 0x02, 0x18, 0x06, 0x9c, 0x12, + 0x5f, 0xef, 0xe0, 0x5c, 0x19, 0x45, 0x38, 0x96, 0xdf, 0x7a, 0x60, 0x5d, + 0x61, 0xba, 0x4d, 0xc8, 0x7b, 0x6e, 0x8d, 0x8c, 0x6e, 0x1d, 0xa9, 0xe5, + 0x92, 0x35, 0xa2, 0x4f, 0x36, 0xd3, 0x40, 0xad, 0xd7, 0x40, 0x12, 0xab, + 0x6c, 0x48, 0x8d, 0x18, 0x92, 0xe4, 0x00, 0x52, 0x03, 0xdf, 0x14, 0xac, + 0x66, 0x3f, 0x6a, 0xae, 0x42, 0x3a, 0x06, 0x50, 0xaa, 0xa5, 0x0d, 0x40, + 0xa7, 0x7b, 0xeb, 0xfd, 0x41, 0x49, 0xff, 0xeb, 0xa3, 0xb4, 0x50, 0x4f, + 0xf7, 0x54, 0x13, 0x3b, 0x1f, 0x8e, 0xb4, 0x45, 0x04, 0x20, 0x42, 0x74, + 0xfe, 0x78, 0x3d, 0xbe, 0x7c, 0xdb, 0xa7, 0x2a, 0x2a, 0x9d, 0x06, 0x48, + 0xc0, 0x9a, 0x02, 0x23, 0xaf, 0xf2, 0x98, 0x07, 0x95, 0xde, 0x3b, 0x30, + 0x73, 0xec, 0x3e, 0x73, 0x58, 0x8f, 0x07, 0x53, 0x40, 0x96, 0xd8, 0x24, + 0xd9, 0x66, 0x80, 0x7a, 0x75, 0x8d, 0xb7, 0x39, 0x27, 0x10, 0x89, 0x7a, + 0xb4, 0x53, 0xbf, 0x3b, 0xc2, 0xe2, 0x97, 0x93, 0x37, 0x8a, 0x9d, 0x4d, + 0x23, 0x6e, 0xac, 0xeb, 0x0d, 0x53, 0x21, 0x4d, 0x0b, 0x34, 0x13, 0xa1, + 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c, + 0x2b, 0xf0, 0x72, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, + 0x06, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, + 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x30, 0x82, 0x06, 0x10, 0x30, + 0x82, 0x03, 0xf8, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61, 0x08, + 0xd3, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x81, 0x91, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, + 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, + 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x32, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, + 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, + 0x68, 0x69, 0x72, 0x64, 0x20, 0x50, 0x61, 0x72, 0x74, 0x79, 0x20, 0x4d, + 0x61, 0x72, 0x6b, 0x65, 0x74, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x52, + 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x36, 0x32, + 0x37, 0x32, 0x31, 0x32, 0x32, 0x34, 0x35, 0x5a, 0x17, 0x0d, 0x32, 0x36, + 0x30, 0x36, 0x32, 0x37, 0x32, 0x31, 0x33, 0x32, 0x34, 0x35, 0x5a, 0x30, + 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, + 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, + 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x22, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, + 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x55, + 0x45, 0x46, 0x49, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x31, 0x31, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x08, 0x6c, 0x4c, + 0xc7, 0x45, 0x09, 0x6a, 0x4b, 0x0c, 0xa4, 0xc0, 0x87, 0x7f, 0x06, 0x75, + 0x0c, 0x43, 0x01, 0x54, 0x64, 0xe0, 0x16, 0x7f, 0x07, 0xed, 0x92, 0x7d, + 0x0b, 0xb2, 0x73, 0xbf, 0x0c, 0x0a, 0xc6, 0x4a, 0x45, 0x61, 0xa0, 0xc5, + 0x16, 0x2d, 0x96, 0xd3, 0xf5, 0x2b, 0xa0, 0xfb, 0x4d, 0x49, 0x9b, 0x41, + 0x80, 0x90, 0x3c, 0xb9, 0x54, 0xfd, 0xe6, 0xbc, 0xd1, 0x9d, 0xc4, 0xa4, + 0x18, 0x8a, 0x7f, 0x41, 0x8a, 0x5c, 0x59, 0x83, 0x68, 0x32, 0xbb, 0x8c, + 0x47, 0xc9, 0xee, 0x71, 0xbc, 0x21, 0x4f, 0x9a, 0x8a, 0x7c, 0xff, 0x44, + 0x3f, 0x8d, 0x8f, 0x32, 0xb2, 0x26, 0x48, 0xae, 0x75, 0xb5, 0xee, 0xc9, + 0x4c, 0x1e, 0x4a, 0x19, 0x7e, 0xe4, 0x82, 0x9a, 0x1d, 0x78, 0x77, 0x4d, + 0x0c, 0xb0, 0xbd, 0xf6, 0x0f, 0xd3, 0x16, 0xd3, 0xbc, 0xfa, 0x2b, 0xa5, + 0x51, 0x38, 0x5d, 0xf5, 0xfb, 0xba, 0xdb, 0x78, 0x02, 0xdb, 0xff, 0xec, + 0x0a, 0x1b, 0x96, 0xd5, 0x83, 0xb8, 0x19, 0x13, 0xe9, 0xb6, 0xc0, 0x7b, + 0x40, 0x7b, 0xe1, 0x1f, 0x28, 0x27, 0xc9, 0xfa, 0xef, 0x56, 0x5e, 0x1c, + 0xe6, 0x7e, 0x94, 0x7e, 0xc0, 0xf0, 0x44, 0xb2, 0x79, 0x39, 0xe5, 0xda, + 0xb2, 0x62, 0x8b, 0x4d, 0xbf, 0x38, 0x70, 0xe2, 0x68, 0x24, 0x14, 0xc9, + 0x33, 0xa4, 0x08, 0x37, 0xd5, 0x58, 0x69, 0x5e, 0xd3, 0x7c, 0xed, 0xc1, + 0x04, 0x53, 0x08, 0xe7, 0x4e, 0xb0, 0x2a, 0x87, 0x63, 0x08, 0x61, 0x6f, + 0x63, 0x15, 0x59, 0xea, 0xb2, 0x2b, 0x79, 0xd7, 0x0c, 0x61, 0x67, 0x8a, + 0x5b, 0xfd, 0x5e, 0xad, 0x87, 0x7f, 0xba, 0x86, 0x67, 0x4f, 0x71, 0x58, + 0x12, 0x22, 0x04, 0x22, 0x22, 0xce, 0x8b, 0xef, 0x54, 0x71, 0x00, 0xce, + 0x50, 0x35, 0x58, 0x76, 0x95, 0x08, 0xee, 0x6a, 0xb1, 0xa2, 0x01, 0xd5, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x76, 0x30, 0x82, 0x01, + 0x72, 0x30, 0x12, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, + 0x15, 0x01, 0x04, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x23, 0x06, + 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x02, 0x04, 0x16, + 0x04, 0x14, 0xf8, 0xc1, 0x6b, 0xb7, 0x7f, 0x77, 0x53, 0x4a, 0xf3, 0x25, + 0x37, 0x1d, 0x4e, 0xa1, 0x26, 0x7b, 0x0f, 0x20, 0x70, 0x80, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x13, 0xad, 0xbf, + 0x43, 0x09, 0xbd, 0x82, 0x70, 0x9c, 0x8c, 0xd5, 0x4f, 0x31, 0x6e, 0xd5, + 0x22, 0x98, 0x8a, 0x1b, 0xd4, 0x30, 0x19, 0x06, 0x09, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x53, + 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, + 0x01, 0xff, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x45, 0x66, 0x52, 0x43, 0xe1, 0x7e, 0x58, 0x11, 0xbf, + 0xd6, 0x4e, 0x9e, 0x23, 0x55, 0x08, 0x3b, 0x3a, 0x22, 0x6a, 0xa8, 0x30, + 0x5c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x55, 0x30, 0x53, 0x30, 0x51, + 0xa0, 0x4f, 0xa0, 0x4d, 0x86, 0x4b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, + 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, + 0x72, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x2f, + 0x4d, 0x69, 0x63, 0x43, 0x6f, 0x72, 0x54, 0x68, 0x69, 0x50, 0x61, 0x72, + 0x4d, 0x61, 0x72, 0x52, 0x6f, 0x6f, 0x5f, 0x32, 0x30, 0x31, 0x30, 0x2d, + 0x31, 0x30, 0x2d, 0x30, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x60, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x54, 0x30, + 0x52, 0x30, 0x50, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x02, 0x86, 0x44, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, 0x65, 0x72, 0x74, + 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x43, 0x6f, 0x72, 0x54, 0x68, 0x69, 0x50, + 0x61, 0x72, 0x4d, 0x61, 0x72, 0x52, 0x6f, 0x6f, 0x5f, 0x32, 0x30, 0x31, + 0x30, 0x2d, 0x31, 0x30, 0x2d, 0x30, 0x35, 0x2e, 0x63, 0x72, 0x74, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x35, 0x08, 0x42, 0xff, 0x30, + 0xcc, 0xce, 0xf7, 0x76, 0x0c, 0xad, 0x10, 0x68, 0x58, 0x35, 0x29, 0x46, + 0x32, 0x76, 0x27, 0x7c, 0xef, 0x12, 0x41, 0x27, 0x42, 0x1b, 0x4a, 0xaa, + 0x6d, 0x81, 0x38, 0x48, 0x59, 0x13, 0x55, 0xf3, 0xe9, 0x58, 0x34, 0xa6, + 0x16, 0x0b, 0x82, 0xaa, 0x5d, 0xad, 0x82, 0xda, 0x80, 0x83, 0x41, 0x06, + 0x8f, 0xb4, 0x1d, 0xf2, 0x03, 0xb9, 0xf3, 0x1a, 0x5d, 0x1b, 0xf1, 0x50, + 0x90, 0xf9, 0xb3, 0x55, 0x84, 0x42, 0x28, 0x1c, 0x20, 0xbd, 0xb2, 0xae, + 0x51, 0x14, 0xc5, 0xc0, 0xac, 0x97, 0x95, 0x21, 0x1c, 0x90, 0xdb, 0x0f, + 0xfc, 0x77, 0x9e, 0x95, 0x73, 0x91, 0x88, 0xca, 0xbd, 0xbd, 0x52, 0xb9, + 0x05, 0x50, 0x0d, 0xdf, 0x57, 0x9e, 0xa0, 0x61, 0xed, 0x0d, 0xe5, 0x6d, + 0x25, 0xd9, 0x40, 0x0f, 0x17, 0x40, 0xc8, 0xce, 0xa3, 0x4a, 0xc2, 0x4d, + 0xaf, 0x9a, 0x12, 0x1d, 0x08, 0x54, 0x8f, 0xbd, 0xc7, 0xbc, 0xb9, 0x2b, + 0x3d, 0x49, 0x2b, 0x1f, 0x32, 0xfc, 0x6a, 0x21, 0x69, 0x4f, 0x9b, 0xc8, + 0x7e, 0x42, 0x34, 0xfc, 0x36, 0x06, 0x17, 0x8b, 0x8f, 0x20, 0x40, 0xc0, + 0xb3, 0x9a, 0x25, 0x75, 0x27, 0xcd, 0xc9, 0x03, 0xa3, 0xf6, 0x5d, 0xd1, + 0xe7, 0x36, 0x54, 0x7a, 0xb9, 0x50, 0xb5, 0xd3, 0x12, 0xd1, 0x07, 0xbf, + 0xbb, 0x74, 0xdf, 0xdc, 0x1e, 0x8f, 0x80, 0xd5, 0xed, 0x18, 0xf4, 0x2f, + 0x14, 0x16, 0x6b, 0x2f, 0xde, 0x66, 0x8c, 0xb0, 0x23, 0xe5, 0xc7, 0x84, + 0xd8, 0xed, 0xea, 0xc1, 0x33, 0x82, 0xad, 0x56, 0x4b, 0x18, 0x2d, 0xf1, + 0x68, 0x95, 0x07, 0xcd, 0xcf, 0xf0, 0x72, 0xf0, 0xae, 0xbb, 0xdd, 0x86, + 0x85, 0x98, 0x2c, 0x21, 0x4c, 0x33, 0x2b, 0xf0, 0x0f, 0x4a, 0xf0, 0x68, + 0x87, 0xb5, 0x92, 0x55, 0x32, 0x75, 0xa1, 0x6a, 0x82, 0x6a, 0x3c, 0xa3, + 0x25, 0x11, 0xa4, 0xed, 0xad, 0xd7, 0x04, 0xae, 0xcb, 0xd8, 0x40, 0x59, + 0xa0, 0x84, 0xd1, 0x95, 0x4c, 0x62, 0x91, 0x22, 0x1a, 0x74, 0x1d, 0x8c, + 0x3d, 0x47, 0x0e, 0x44, 0xa6, 0xe4, 0xb0, 0x9b, 0x34, 0x35, 0xb1, 0xfa, + 0xb6, 0x53, 0xa8, 0x2c, 0x81, 0xec, 0xa4, 0x05, 0x71, 0xc8, 0x9d, 0xb8, + 0xba, 0xe8, 0x1b, 0x44, 0x66, 0xe4, 0x47, 0x54, 0x0e, 0x8e, 0x56, 0x7f, + 0xb3, 0x9f, 0x16, 0x98, 0xb2, 0x86, 0xd0, 0x68, 0x3e, 0x90, 0x23, 0xb5, + 0x2f, 0x5e, 0x8f, 0x50, 0x85, 0x8d, 0xc6, 0x8d, 0x82, 0x5f, 0x41, 0xa1, + 0xf4, 0x2e, 0x0d, 0xe0, 0x99, 0xd2, 0x6c, 0x75, 0xe4, 0xb6, 0x69, 0xb5, + 0x21, 0x86, 0xfa, 0x07, 0xd1, 0xf6, 0xe2, 0x4d, 0xd1, 0xda, 0xad, 0x2c, + 0x77, 0x53, 0x1e, 0x25, 0x32, 0x37, 0xc7, 0x6c, 0x52, 0x72, 0x95, 0x86, + 0xb0, 0xf1, 0x35, 0x61, 0x6a, 0x19, 0xf5, 0xb2, 0x3b, 0x81, 0x50, 0x56, + 0xa6, 0x32, 0x2d, 0xfe, 0xa2, 0x89, 0xf9, 0x42, 0x86, 0x27, 0x18, 0x55, + 0xa1, 0x82, 0xca, 0x5a, 0x9b, 0xf8, 0x30, 0x98, 0x54, 0x14, 0xa6, 0x47, + 0x96, 0x25, 0x2f, 0xc8, 0x26, 0xe4, 0x41, 0x94, 0x1a, 0x5c, 0x02, 0x3f, + 0xe5, 0x96, 0xe3, 0x85, 0x5b, 0x3c, 0x3e, 0x3f, 0xbb, 0x47, 0x16, 0x72, + 0x55, 0xe2, 0x25, 0x22, 0xb1, 0xd9, 0x7b, 0xe7, 0x03, 0x06, 0x2a, 0xa3, + 0xf7, 0x1e, 0x90, 0x46, 0xc3, 0x00, 0x0d, 0xd6, 0x19, 0x89, 0xe3, 0x0e, + 0x35, 0x27, 0x62, 0x03, 0x71, 0x15, 0xa6, 0xef, 0xd0, 0x27, 0xa0, 0xa0, + 0x59, 0x37, 0x60, 0xf8, 0x38, 0x94, 0xb8, 0xe0, 0x78, 0x70, 0xf8, 0xba, + 0x4c, 0x86, 0x87, 0x94, 0xf6, 0xe0, 0xae, 0x02, 0x45, 0xee, 0x65, 0xc2, + 0xb6, 0xa3, 0x7e, 0x69, 0x16, 0x75, 0x07, 0x92, 0x9b, 0xf5, 0xa6, 0xbc, + 0x59, 0x83, 0x58, 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, + 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72, 0x07, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xeb, 0x05, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77, 0x59, + 0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x30, + 0x82, 0x05, 0xd7, 0x30, 0x82, 0x03, 0xbf, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x0a, 0x61, 0x07, 0x76, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x81, 0x88, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, + 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, + 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, + 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x29, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, + 0x66, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x32, 0x30, 0x31, 0x30, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x31, 0x31, 0x30, 0x31, 0x39, 0x31, 0x38, 0x34, 0x31, + 0x34, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x30, 0x31, 0x39, 0x31, + 0x38, 0x35, 0x31, 0x34, 0x32, 0x5a, 0x30, 0x81, 0x84, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, + 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, + 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, + 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, + 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x2e, 0x30, + 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x73, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x50, 0x43, 0x41, 0x20, 0x32, 0x30, 0x31, 0x31, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdd, 0x0c, 0xbb, 0xa2, 0xe4, 0x2e, + 0x09, 0xe3, 0xe7, 0xc5, 0xf7, 0x96, 0x69, 0xbc, 0x00, 0x21, 0xbd, 0x69, + 0x33, 0x33, 0xef, 0xad, 0x04, 0xcb, 0x54, 0x80, 0xee, 0x06, 0x83, 0xbb, + 0xc5, 0x20, 0x84, 0xd9, 0xf7, 0xd2, 0x8b, 0xf3, 0x38, 0xb0, 0xab, 0xa4, + 0xad, 0x2d, 0x7c, 0x62, 0x79, 0x05, 0xff, 0xe3, 0x4a, 0x3f, 0x04, 0x35, + 0x20, 0x70, 0xe3, 0xc4, 0xe7, 0x6b, 0xe0, 0x9c, 0xc0, 0x36, 0x75, 0xe9, + 0x8a, 0x31, 0xdd, 0x8d, 0x70, 0xe5, 0xdc, 0x37, 0xb5, 0x74, 0x46, 0x96, + 0x28, 0x5b, 0x87, 0x60, 0x23, 0x2c, 0xbf, 0xdc, 0x47, 0xa5, 0x67, 0xf7, + 0x51, 0x27, 0x9e, 0x72, 0xeb, 0x07, 0xa6, 0xc9, 0xb9, 0x1e, 0x3b, 0x53, + 0x35, 0x7c, 0xe5, 0xd3, 0xec, 0x27, 0xb9, 0x87, 0x1c, 0xfe, 0xb9, 0xc9, + 0x23, 0x09, 0x6f, 0xa8, 0x46, 0x91, 0xc1, 0x6e, 0x96, 0x3c, 0x41, 0xd3, + 0xcb, 0xa3, 0x3f, 0x5d, 0x02, 0x6a, 0x4d, 0xec, 0x69, 0x1f, 0x25, 0x28, + 0x5c, 0x36, 0xff, 0xfd, 0x43, 0x15, 0x0a, 0x94, 0xe0, 0x19, 0xb4, 0xcf, + 0xdf, 0xc2, 0x12, 0xe2, 0xc2, 0x5b, 0x27, 0xee, 0x27, 0x78, 0x30, 0x8b, + 0x5b, 0x2a, 0x09, 0x6b, 0x22, 0x89, 0x53, 0x60, 0x16, 0x2c, 0xc0, 0x68, + 0x1d, 0x53, 0xba, 0xec, 0x49, 0xf3, 0x9d, 0x61, 0x8c, 0x85, 0x68, 0x09, + 0x73, 0x44, 0x5d, 0x7d, 0xa2, 0x54, 0x2b, 0xdd, 0x79, 0xf7, 0x15, 0xcf, + 0x35, 0x5d, 0x6c, 0x1c, 0x2b, 0x5c, 0xce, 0xbc, 0x9c, 0x23, 0x8b, 0x6f, + 0x6e, 0xb5, 0x26, 0xd9, 0x36, 0x13, 0xc3, 0x4f, 0xd6, 0x27, 0xae, 0xb9, + 0x32, 0x3b, 0x41, 0x92, 0x2c, 0xe1, 0xc7, 0xcd, 0x77, 0xe8, 0xaa, 0x54, + 0x4e, 0xf7, 0x5c, 0x0b, 0x04, 0x87, 0x65, 0xb4, 0x43, 0x18, 0xa8, 0xb2, + 0xe0, 0x6d, 0x19, 0x77, 0xec, 0x5a, 0x24, 0xfa, 0x48, 0x03, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x43, 0x30, 0x82, 0x01, 0x3f, 0x30, + 0x10, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, + 0x04, 0x03, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xa9, 0x29, 0x02, 0x39, 0x8e, 0x16, 0xc4, 0x97, + 0x78, 0xcd, 0x90, 0xf9, 0x9e, 0x4f, 0x9a, 0xe1, 0x7c, 0x55, 0xaf, 0x53, + 0x30, 0x19, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, + 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, + 0x43, 0x00, 0x41, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd5, 0xf6, + 0x56, 0xcb, 0x8f, 0xe8, 0xa2, 0x5c, 0x62, 0x68, 0xd1, 0x3d, 0x94, 0x90, + 0x5b, 0xd7, 0xce, 0x9a, 0x18, 0xc4, 0x30, 0x56, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x4f, 0x30, 0x4d, 0x30, 0x4b, 0xa0, 0x49, 0xa0, 0x47, 0x86, + 0x45, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x70, 0x72, + 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x52, 0x6f, + 0x6f, 0x43, 0x65, 0x72, 0x41, 0x75, 0x74, 0x5f, 0x32, 0x30, 0x31, 0x30, + 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x5a, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x4e, + 0x30, 0x4c, 0x30, 0x4a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, 0x65, 0x72, + 0x74, 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x52, 0x6f, 0x6f, 0x43, 0x65, 0x72, + 0x41, 0x75, 0x74, 0x5f, 0x32, 0x30, 0x31, 0x30, 0x2d, 0x30, 0x36, 0x2d, + 0x32, 0x33, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, + 0x01, 0x00, 0x14, 0xfc, 0x7c, 0x71, 0x51, 0xa5, 0x79, 0xc2, 0x6e, 0xb2, + 0xef, 0x39, 0x3e, 0xbc, 0x3c, 0x52, 0x0f, 0x6e, 0x2b, 0x3f, 0x10, 0x13, + 0x73, 0xfe, 0xa8, 0x68, 0xd0, 0x48, 0xa6, 0x34, 0x4d, 0x8a, 0x96, 0x05, + 0x26, 0xee, 0x31, 0x46, 0x90, 0x61, 0x79, 0xd6, 0xff, 0x38, 0x2e, 0x45, + 0x6b, 0xf4, 0xc0, 0xe5, 0x28, 0xb8, 0xda, 0x1d, 0x8f, 0x8a, 0xdb, 0x09, + 0xd7, 0x1a, 0xc7, 0x4c, 0x0a, 0x36, 0x66, 0x6a, 0x8c, 0xec, 0x1b, 0xd7, + 0x04, 0x90, 0xa8, 0x18, 0x17, 0xa4, 0x9b, 0xb9, 0xe2, 0x40, 0x32, 0x36, + 0x76, 0xc4, 0xc1, 0x5a, 0xc6, 0xbf, 0xe4, 0x04, 0xc0, 0xea, 0x16, 0xd3, + 0xac, 0xc3, 0x68, 0xef, 0x62, 0xac, 0xdd, 0x54, 0x6c, 0x50, 0x30, 0x58, + 0xa6, 0xeb, 0x7c, 0xfe, 0x94, 0xa7, 0x4e, 0x8e, 0xf4, 0xec, 0x7c, 0x86, + 0x73, 0x57, 0xc2, 0x52, 0x21, 0x73, 0x34, 0x5a, 0xf3, 0xa3, 0x8a, 0x56, + 0xc8, 0x04, 0xda, 0x07, 0x09, 0xed, 0xf8, 0x8b, 0xe3, 0xce, 0xf4, 0x7e, + 0x8e, 0xae, 0xf0, 0xf6, 0x0b, 0x8a, 0x08, 0xfb, 0x3f, 0xc9, 0x1d, 0x72, + 0x7f, 0x53, 0xb8, 0xeb, 0xbe, 0x63, 0xe0, 0xe3, 0x3d, 0x31, 0x65, 0xb0, + 0x81, 0xe5, 0xf2, 0xac, 0xcd, 0x16, 0xa4, 0x9f, 0x3d, 0xa8, 0xb1, 0x9b, + 0xc2, 0x42, 0xd0, 0x90, 0x84, 0x5f, 0x54, 0x1d, 0xff, 0x89, 0xea, 0xba, + 0x1d, 0x47, 0x90, 0x6f, 0xb0, 0x73, 0x4e, 0x41, 0x9f, 0x40, 0x9f, 0x5f, + 0xe5, 0xa1, 0x2a, 0xb2, 0x11, 0x91, 0x73, 0x8a, 0x21, 0x28, 0xf0, 0xce, + 0xde, 0x73, 0x39, 0x5f, 0x3e, 0xab, 0x5c, 0x60, 0xec, 0xdf, 0x03, 0x10, + 0xa8, 0xd3, 0x09, 0xe9, 0xf4, 0xf6, 0x96, 0x85, 0xb6, 0x7f, 0x51, 0x88, + 0x66, 0x47, 0x19, 0x8d, 0xa2, 0xb0, 0x12, 0x3d, 0x81, 0x2a, 0x68, 0x05, + 0x77, 0xbb, 0x91, 0x4c, 0x62, 0x7b, 0xb6, 0xc1, 0x07, 0xc7, 0xba, 0x7a, + 0x87, 0x34, 0x03, 0x0e, 0x4b, 0x62, 0x7a, 0x99, 0xe9, 0xca, 0xfc, 0xce, + 0x4a, 0x37, 0xc9, 0x2d, 0xa4, 0x57, 0x7c, 0x1c, 0xfe, 0x3d, 0xdc, 0xb8, + 0x0f, 0x5a, 0xfa, 0xd6, 0xc4, 0xb3, 0x02, 0x85, 0x02, 0x3a, 0xea, 0xb3, + 0xd9, 0x6e, 0xe4, 0x69, 0x21, 0x37, 0xde, 0x81, 0xd1, 0xf6, 0x75, 0x19, + 0x05, 0x67, 0xd3, 0x93, 0x57, 0x5e, 0x29, 0x1b, 0x39, 0xc8, 0xee, 0x2d, + 0xe1, 0xcd, 0xe4, 0x45, 0x73, 0x5b, 0xd0, 0xd2, 0xce, 0x7a, 0xab, 0x16, + 0x19, 0x82, 0x46, 0x58, 0xd0, 0x5e, 0x9d, 0x81, 0xb3, 0x67, 0xaf, 0x6c, + 0x35, 0xf2, 0xbc, 0xe5, 0x3f, 0x24, 0xe2, 0x35, 0xa2, 0x0a, 0x75, 0x06, + 0xf6, 0x18, 0x56, 0x99, 0xd4, 0x78, 0x2c, 0xd1, 0x05, 0x1b, 0xeb, 0xd0, + 0x88, 0x01, 0x9d, 0xaa, 0x10, 0xf1, 0x05, 0xdf, 0xba, 0x7e, 0x2c, 0x63, + 0xb7, 0x06, 0x9b, 0x23, 0x21, 0xc4, 0xf9, 0x78, 0x6c, 0xe2, 0x58, 0x17, + 0x06, 0x36, 0x2b, 0x91, 0x12, 0x03, 0xcc, 0xa4, 0xd9, 0xf2, 0x2d, 0xba, + 0xf9, 0x94, 0x9d, 0x40, 0xed, 0x18, 0x45, 0xf1, 0xce, 0x8a, 0x5c, 0x6b, + 0x3e, 0xab, 0x03, 0xd3, 0x70, 0x18, 0x2a, 0x0a, 0x6a, 0xe0, 0x5f, 0x47, + 0xd1, 0xd5, 0x63, 0x0a, 0x32, 0xf2, 0xaf, 0xd7, 0x36, 0x1f, 0x2a, 0x70, + 0x5a, 0xe5, 0x42, 0x59, 0x08, 0x71, 0x4b, 0x57, 0xba, 0x7e, 0x83, 0x81, + 0xf0, 0x21, 0x3c, 0xf4, 0x1c, 0xc1, 0xc5, 0xb9, 0x90, 0x93, 0x0e, 0x88, + 0x45, 0x93, 0x86, 0xe9, 0xb1, 0x20, 0x99, 0xbe, 0x98, 0xcb, 0xc5, 0x95, + 0xa4, 0x5d, 0x62, 0xd6, 0xa0, 0x63, 0x08, 0x20, 0xbd, 0x75, 0x10, 0x77, + 0x7d, 0x3d, 0xf3, 0x45, 0xb9, 0x9f, 0x97, 0x9f, 0xcb, 0x57, 0x80, 0x6f, + 0x33, 0xa9, 0x04, 0xcf, 0x77, 0xa4, 0x62, 0x1c, 0x59, 0x7e, 0xa1, 0x59, + 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, + 0xf0, 0x72, 0x64, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x04, + 0x00, 0x00, 0xe4, 0x0a, 0xc4, 0x6d, 0xe8, 0x2e, 0x4c, 0x9c, 0xa3, 0x14, + 0x0f, 0xc7, 0xb2, 0x00, 0x87, 0x10, 0x30, 0x82, 0x04, 0x34, 0x30, 0x82, + 0x03, 0x1c, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xb9, 0x41, + 0x24, 0xa0, 0x18, 0x2c, 0x92, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x84, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, + 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0b, + 0x49, 0x73, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x4d, 0x61, 0x6e, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x07, 0x44, 0x6f, + 0x75, 0x67, 0x6c, 0x61, 0x73, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x0e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x2b, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x4d, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x32, 0x31, 0x31, + 0x31, 0x32, 0x35, 0x31, 0x5a, 0x17, 0x0d, 0x34, 0x32, 0x30, 0x34, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x32, 0x35, 0x31, 0x5a, 0x30, 0x81, 0x84, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, + 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0b, 0x49, + 0x73, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x4d, 0x61, 0x6e, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x07, 0x44, 0x6f, 0x75, + 0x67, 0x6c, 0x61, 0x73, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x0e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x2b, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x4d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x5b, 0x3a, 0x16, + 0x74, 0xee, 0x21, 0x5d, 0xae, 0x61, 0xed, 0x9d, 0x56, 0xac, 0xbd, 0xde, + 0xde, 0x72, 0xf3, 0xdd, 0x7e, 0x2d, 0x4c, 0x62, 0x0f, 0xac, 0xc0, 0x6d, + 0x48, 0x08, 0x11, 0xcf, 0x8d, 0x8b, 0xfb, 0x61, 0x1f, 0x27, 0xcc, 0x11, + 0x6e, 0xd9, 0x55, 0x3d, 0x39, 0x54, 0xeb, 0x40, 0x3b, 0xb1, 0xbb, 0xe2, + 0x85, 0x34, 0x79, 0xca, 0xf7, 0x7b, 0xbf, 0xba, 0x7a, 0xc8, 0x10, 0x2d, + 0x19, 0x7d, 0xad, 0x59, 0xcf, 0xa6, 0xd4, 0xe9, 0x4e, 0x0f, 0xda, 0xae, + 0x52, 0xea, 0x4c, 0x9e, 0x90, 0xce, 0xc6, 0x99, 0x0d, 0x4e, 0x67, 0x65, + 0x78, 0x5d, 0xf9, 0xd1, 0xd5, 0x38, 0x4a, 0x4a, 0x7a, 0x8f, 0x93, 0x9c, + 0x7f, 0x1a, 0xa3, 0x85, 0xdb, 0xce, 0xfa, 0x8b, 0xf7, 0xc2, 0xa2, 0x21, + 0x2d, 0x9b, 0x54, 0x41, 0x35, 0x10, 0x57, 0x13, 0x8d, 0x6c, 0xbc, 0x29, + 0x06, 0x50, 0x4a, 0x7e, 0xea, 0x99, 0xa9, 0x68, 0xa7, 0x3b, 0xc7, 0x07, + 0x1b, 0x32, 0x9e, 0xa0, 0x19, 0x87, 0x0e, 0x79, 0xbb, 0x68, 0x99, 0x2d, + 0x7e, 0x93, 0x52, 0xe5, 0xf6, 0xeb, 0xc9, 0x9b, 0xf9, 0x2b, 0xed, 0xb8, + 0x68, 0x49, 0xbc, 0xd9, 0x95, 0x50, 0x40, 0x5b, 0xc5, 0xb2, 0x71, 0xaa, + 0xeb, 0x5c, 0x57, 0xde, 0x71, 0xf9, 0x40, 0x0a, 0xdd, 0x5b, 0xac, 0x1e, + 0x84, 0x2d, 0x50, 0x1a, 0x52, 0xd6, 0xe1, 0xf3, 0x6b, 0x6e, 0x90, 0x64, + 0x4f, 0x5b, 0xb4, 0xeb, 0x20, 0xe4, 0x61, 0x10, 0xda, 0x5a, 0xf0, 0xea, + 0xe4, 0x42, 0xd7, 0x01, 0xc4, 0xfe, 0x21, 0x1f, 0xd9, 0xb9, 0xc0, 0x54, + 0x95, 0x42, 0x81, 0x52, 0x72, 0x1f, 0x49, 0x64, 0x7a, 0xc8, 0x6c, 0x24, + 0xf1, 0x08, 0x70, 0x0b, 0x4d, 0xa5, 0xa0, 0x32, 0xd1, 0xa0, 0x1c, 0x57, + 0xa8, 0x4d, 0xe3, 0xaf, 0xa5, 0x8e, 0x05, 0x05, 0x3e, 0x10, 0x43, 0xa1, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa6, 0x30, 0x81, 0xa3, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xad, 0x91, + 0x99, 0x0b, 0xc2, 0x2a, 0xb1, 0xf5, 0x17, 0x04, 0x8c, 0x23, 0xb6, 0x65, + 0x5a, 0x26, 0x8e, 0x34, 0x5a, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0x91, 0x99, 0x0b, 0xc2, + 0x2a, 0xb1, 0xf5, 0x17, 0x04, 0x8c, 0x23, 0xb6, 0x65, 0x5a, 0x26, 0x8e, + 0x34, 0x5a, 0x63, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x43, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3c, 0x30, 0x3a, 0x30, 0x38, 0xa0, 0x36, + 0xa0, 0x34, 0x86, 0x32, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, + 0x62, 0x6f, 0x6f, 0x74, 0x2d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, + 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x3f, 0x7d, 0xf6, 0x76, 0xa5, 0xb3, 0x83, 0xb4, 0x2b, 0x7a, + 0xd0, 0x6d, 0x52, 0x1a, 0x03, 0x83, 0xc4, 0x12, 0xa7, 0x50, 0x9c, 0x47, + 0x92, 0xcc, 0xc0, 0x94, 0x77, 0x82, 0xd2, 0xae, 0x57, 0xb3, 0x99, 0x04, + 0xf5, 0x32, 0x3a, 0xc6, 0x55, 0x1d, 0x07, 0xdb, 0x12, 0xa9, 0x56, 0xfa, + 0xd8, 0xd4, 0x76, 0x20, 0xeb, 0xe4, 0xc3, 0x51, 0xdb, 0x9a, 0x5c, 0x9c, + 0x92, 0x3f, 0x18, 0x73, 0xda, 0x94, 0x6a, 0xa1, 0x99, 0x38, 0x8c, 0xa4, + 0x88, 0x6d, 0xc1, 0xfc, 0x39, 0x71, 0xd0, 0x74, 0x76, 0x16, 0x03, 0x3e, + 0x56, 0x23, 0x35, 0xd5, 0x55, 0x47, 0x5b, 0x1a, 0x1d, 0x41, 0xc2, 0xd3, + 0x12, 0x4c, 0xdc, 0xff, 0xae, 0x0a, 0x92, 0x9c, 0x62, 0x0a, 0x17, 0x01, + 0x9c, 0x73, 0xe0, 0x5e, 0xb1, 0xfd, 0xbc, 0xd6, 0xb5, 0x19, 0x11, 0x7a, + 0x7e, 0xcd, 0x3e, 0x03, 0x7e, 0x66, 0xdb, 0x5b, 0xa8, 0xc9, 0x39, 0x48, + 0x51, 0xff, 0x53, 0xe1, 0x9c, 0x31, 0x53, 0x91, 0x1b, 0x3b, 0x10, 0x75, + 0x03, 0x17, 0xba, 0xe6, 0x81, 0x02, 0x80, 0x94, 0x70, 0x4c, 0x46, 0xb7, + 0x94, 0xb0, 0x3d, 0x15, 0xcd, 0x1f, 0x8e, 0x02, 0xe0, 0x68, 0x02, 0x8f, + 0xfb, 0xf9, 0x47, 0x1d, 0x7d, 0xa2, 0x01, 0xc6, 0x07, 0x51, 0xc4, 0x9a, + 0xcc, 0xed, 0xdd, 0xcf, 0xa3, 0x5d, 0xed, 0x92, 0xbb, 0xbe, 0xd1, 0xfd, + 0xe6, 0xec, 0x1f, 0x33, 0x51, 0x73, 0x04, 0xbe, 0x3c, 0x72, 0xb0, 0x7d, + 0x08, 0xf8, 0x01, 0xff, 0x98, 0x7d, 0xcb, 0x9c, 0xe0, 0x69, 0x39, 0x77, + 0x25, 0x47, 0x71, 0x88, 0xb1, 0x8d, 0x27, 0xa5, 0x2e, 0xa8, 0xf7, 0x3f, + 0x5f, 0x80, 0x69, 0x97, 0x3e, 0xa9, 0xf4, 0x99, 0x14, 0xdb, 0xce, 0x03, + 0x0e, 0x0b, 0x66, 0xc4, 0x1c, 0x6d, 0xbd, 0xb8, 0x27, 0x77, 0xc1, 0x42, + 0x94, 0xbd, 0xfc, 0x6a, 0x0a, 0xbc, 0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50, + 0x92, 0x40, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf5, 0x8f, 0xbd, 0xf7, 0x1b, 0xe8, 0xc3, 0x7c, 0xbb, 0xd6, + 0x94, 0x4e, 0x47, 0x2c, 0x45, 0x0b, 0x10, 0x43, 0x81, 0x7b, 0x97, 0x29, + 0x14, 0x48, 0x7c, 0x22, 0x10, 0x33, 0xf3, 0x07, 0x9e, 0x43, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x97, 0x01, 0x57, 0xde, 0x52, 0xcd, 0xae, 0x14, 0xcf, + 0x17, 0xee, 0x36, 0x98, 0x81, 0xd6, 0x24, 0x5b, 0x3a, 0x6a, 0xb6, 0x35, + 0x2e, 0xab, 0xae, 0xe5, 0x88, 0xa0, 0x58, 0x4b, 0x03, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf1, 0x6b, 0x5f, 0xc3, 0x61, 0x18, 0x3f, 0x58, 0x71, 0x20, + 0xe6, 0x02, 0xc0, 0xd6, 0x57, 0x73, 0xaf, 0xdf, 0xe7, 0x86, 0x12, 0x41, + 0x84, 0xfa, 0x70, 0x80, 0x52, 0x58, 0xd7, 0x6d, 0x59, 0x4c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x02, 0x1f, 0x15, 0xe3, 0xa6, 0x7b, 0x75, 0xac, 0xe8, + 0x84, 0x99, 0x9b, 0xed, 0xff, 0xe3, 0x42, 0x13, 0x79, 0x2a, 0x61, 0x1e, + 0x40, 0xe5, 0x62, 0xe8, 0x7e, 0x6b, 0x9a, 0x0c, 0xb2, 0x82, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xa5, 0xd1, 0x09, 0xb2, 0xaf, 0xa3, 0xfa, 0x90, 0x87, 0x8f, + 0x70, 0x38, 0x2b, 0x23, 0x88, 0xfc, 0xd2, 0xfe, 0xae, 0xae, 0x8a, 0x51, + 0xb8, 0x0a, 0xdd, 0x04, 0x8e, 0x9f, 0x87, 0x6b, 0x2a, 0x4e +}; + +#endif /* !TEST_DATA_EFIVARS_0_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/test.h b/include/test.h index fcaa1cdf..5261dbd9 100644 --- a/include/test.h +++ b/include/test.h @@ -43,21 +43,27 @@ #include -#define ZeroMem(buf, sz) memset(buf, 0, sz) -#define SetMem(buf, sz, value) memset(buf, value, sz) -#define CopyMem(dest, src, len) memcpy(dest, src, len) -#define CompareMem(dest, src, len) memcmp(dest, src, len) +#include + +#include +#include + +#include #include -#define AllocateZeroPool(x) calloc(1, (x)) -#define AllocatePool(x) malloc(x) -#define FreePool(x) free(x) -#define ReallocatePool(old, oldsz, newsz) realloc(old, newsz) +#include "list.h" +INTN StrCmp(IN CONST CHAR16 *s1, + IN CONST CHAR16 *s2); INTN StrnCmp(IN CONST CHAR16 *s1, IN CONST CHAR16 *s2, IN UINTN len); +VOID StrCpy(IN CHAR16 *Dest, + IN CONST CHAR16 *Src); +VOID StrnCpy(IN CHAR16 *Dest, + IN CONST CHAR16 *Src, + IN UINTN Len); CHAR16 *StrDuplicate(IN CONST CHAR16 *Src); UINTN StrLen(IN CONST CHAR16 *s1); UINTN StrSize(IN CONST CHAR16 *s1); @@ -298,6 +304,108 @@ efi_strerror(EFI_STATUS status) return out; } +static inline char * +Str2str(CHAR16 *in) +{ + static char buf0[1024]; + static char buf1[1024]; + char *out; + static int n; + + out = n++ % 2 ? buf0 : buf1; + if (n > 1) + n -= 2; + SetMem(out, 1024, 0); + for (UINTN i = 0; i < 1023 && in[i]; i++) + out[i] = in[i]; + return out; +} + +static inline char * +format_var_attrs(UINT32 attr) +{ + static char buf0[1024]; + static char buf1[1024]; + static int n; + int pos = 0; + bool found = false; + char *buf, *bufp; + + buf = n++ % 2 ? buf0 : buf1; + if (n > 1) + n -= 2; + SetMem(buf, sizeof(buf0), 0); + bufp = &buf[0]; + for (UINT32 i = 0; i < 8; i++) { + switch((1ul << i) & attr) { + case EFI_VARIABLE_NON_VOLATILE: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "NV"); + attr &= ~EFI_VARIABLE_NON_VOLATILE; + found = true; + break; + case EFI_VARIABLE_BOOTSERVICE_ACCESS: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "BS"); + attr &= ~EFI_VARIABLE_BOOTSERVICE_ACCESS; + found = true; + break; + case EFI_VARIABLE_RUNTIME_ACCESS: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "RT"); + attr &= ~EFI_VARIABLE_RUNTIME_ACCESS; + found = true; + break; + case EFI_VARIABLE_HARDWARE_ERROR_RECORD: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "HER"); + attr &= ~EFI_VARIABLE_HARDWARE_ERROR_RECORD; + found = true; + break; + case EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "AUTH"); + attr &= ~EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; + found = true; + break; + case EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "TIMEAUTH"); + attr &= ~EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + found = true; + break; + case EFI_VARIABLE_APPEND_WRITE: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "APPEND"); + attr &= ~EFI_VARIABLE_APPEND_WRITE; + found = true; + break; + case EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS: + if (found) + bufp = stpcpy(bufp, "|"); + bufp = stpcpy(bufp, "ENHANCED_AUTH"); + attr &= ~EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS; + found = true; + break; + default: + break; + } + } + if (attr) { + if (found) + bufp = stpcpy(bufp, "|"); + snprintf(bufp, bufp - buf - 1, "0x%x", attr); + } + return &buf[0]; +} + extern int debug; #ifdef dprint #undef dprint diff --git a/include/test.mk b/include/test.mk index 86f1a9d8..d362253b 100644 --- a/include/test.mk +++ b/include/test.mk @@ -79,6 +79,12 @@ $(patsubst %.c,%.o,$(wildcard test-*.c)) : | test-random.h test-load-options_FILES = lib/guid.c test-load-options : CFLAGS+=-DHAVE_SHIM_LOCK_GUID +test-mock-variables_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c mock-variables.c +test-mock-variables: CFLAGS+=-DHAVE_SHIM_LOCK_GUID + +test-mok-mirror_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c +test-mok-mirror: CFLAGS+=-DHAVE_START_IMAGE -DHAVE_SHIM_LOCK_GUID + test-sbat_FILES = csv.c lib/variables.c lib/guid.c test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID @@ -89,7 +95,7 @@ tests := $(patsubst %.c,%,$(wildcard test-*.c)) $(tests) :: test-% : | libefi-test.a $(tests) :: test-% : test.c test-%.c $(test-%_FILES) - $(CC) $(CFLAGS) -o $@ $^ $(wildcard $*.c) $(test-$*_FILES) libefi-test.a -lefivar + $(CC) $(CFLAGS) -o $@ $(sort $^ $(wildcard $*.c) $(test-$*_FILES)) libefi-test.a -lefivar $(VALGRIND) ./$@ test : $(tests) diff --git a/mock-variables.c b/mock-variables.c new file mode 100644 index 00000000..d8342d59 --- /dev/null +++ b/mock-variables.c @@ -0,0 +1,1022 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * mock-variables.c - a mock GetVariable/SetVariable/GNVN/etc + * implementation for testing. + * Copyright Peter Jones + */ +#include "shim.h" +#include "mock-variables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-function" + +list_t mock_default_variable_limits; +list_t *mock_qvi_limits = &mock_default_variable_limits; +list_t *mock_sv_limits = &mock_default_variable_limits; + +list_t mock_variables = LIST_HEAD_INIT(mock_variables); + +mock_sort_policy_t mock_variable_sort_policy = MOCK_SORT_APPEND; + +static const size_t guidstr_size = sizeof("8be4df61-93ca-11d2-aa0d-00e098032b8c"); + +static int +variable_limits_cmp(const struct mock_variable_limits * const v0, + const struct mock_variable_limits * const v1) +{ + UINT32 mask = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + + return (v0->attrs & mask) - (v1->attrs & mask); +} + +static INT64 +variable_cmp(const struct mock_variable * const v0, + const struct mock_variable * const v1) +{ + INT64 ret; + if (v0 == NULL || v1 == NULL) + return (uintptr_t)v0 - (uintptr_t)v1; + + ret = CompareGuid(&v0->guid, &v1->guid); + ret <<= 8ul; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): "GUID_FMT" %s "GUID_FMT" (0x%011"PRIx64" %"PRId64")\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(v0->guid), + ret < 0 ? "<" : (ret > 0 ? ">" : "="), + GUID_ARGS(v1->guid), + (UINT64)ret & 0x1fffffffffful, + ret); +#endif + if (ret != 0) { + return ret; + } + + ret = StrCmp(v0->name, v1->name); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): \"%s\" %s \"%s\" (0x%02hhx (%d)\n", + __FILE__, __LINE__-1, __func__, + Str2str(v0->name), + ret < 0 ? "<" : (ret > 0 ? ">" : "=="), + Str2str(v1->name), + ret, ret); +#endif + return ret; +} + +static char * +list2var(list_t *pos) +{ + static char buf0[1024]; + static char buf1[1024]; + char *out; + static int n; + struct mock_variable *var; + + out = n++ % 2 ? buf0 : buf1; + if (n > 1) + n -= 2; + SetMem(out, 1024, 0); + if (pos == &mock_variables) { + strcpy(out, "list tail"); + return out; + } + var = list_entry(pos, struct mock_variable, list); + snprintf(out, 1023, GUID_FMT"-%s", + GUID_ARGS(var->guid), + Str2str(var->name)); + return out; +} + +EFI_STATUS EFIAPI +mock_get_variable(CHAR16 *name, EFI_GUID *guid, UINT32 *attrs, UINTN *size, + VOID *data) +{ + list_t *pos = NULL; + struct mock_variable goal = { + .name = name, + .guid = *guid, + }; + struct mock_variable *result = NULL; + EFI_STATUS status; + + if (name == NULL || guid == NULL || size == NULL) { + status = EFI_INVALID_PARAMETER; + return status; + } + + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + + var = list_entry(pos, struct mock_variable, list); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(goal.guid), Str2str(goal.name), + GUID_ARGS(var->guid), Str2str(var->name)); +#endif + if (variable_cmp(&goal, var) == 0) { + if (attrs != NULL) + *attrs = var->attrs; + if (var->size > *size) { + *size = var->size; + status = EFI_BUFFER_TOO_SMALL; + return status; + } + if (data == NULL) { + status = EFI_INVALID_PARAMETER; + return status; + } + *size = var->size; + memcpy(data, var->data, var->size); + status = EFI_SUCCESS; + return status; + } + } + + status = EFI_NOT_FOUND; + return status; +} + +static EFI_STATUS +mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid, + struct mock_variable *result) +{ + EFI_STATUS status; + + if (*size < StrSize(result->name)) { + *size = StrSize(result->name); + status = EFI_BUFFER_TOO_SMALL; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning %lx\n", + __FILE__, __LINE__-1, __func__, status); +#endif + return status; + } + + *size = StrLen(result->name) + 1; + StrCpy(name, result->name); + memcpy(guid, &result->guid, sizeof(EFI_GUID)); + + status = EFI_SUCCESS; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): returning %lx\n", + __FILE__, __LINE__-1, __func__, status); +#endif + return status; +} + +EFI_STATUS EFIAPI +mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) +{ + list_t *pos = NULL; + struct mock_variable goal = { + .name = name, + .guid = *guid, + }; + struct mock_variable *result = NULL; + bool found = false; + EFI_STATUS status; + + if (size == NULL || name == NULL || guid == NULL) { + status = EFI_INVALID_PARAMETER; + return status; + } + + for (size_t i = 0; i < *size; i++) { + if (name[i] == 0) { + found = true; + break; + } + } + + if (found == false) { + status = EFI_INVALID_PARAMETER; + return status; + } + + found = false; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():searching for "GUID_FMT"%s%s\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(*guid), + name[0] == 0 ? "" : "-", + name[0] == 0 ? "" : Str2str(name)); +#endif + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + + var = list_entry(pos, struct mock_variable, list); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): candidate var:%p &var->guid:%p &var->list:%p\n", + __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); +#endif + if (name[0] == 0) { + if (CompareGuid(&var->guid, guid) == 0) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): found\n", + __FILE__, __LINE__-1, __func__); +#endif + result = var; + found = true; + break; + } + } else { + if (found) { + if (CompareGuid(&var->guid, guid) == 0) { + result = var; + break; + } + continue; + } + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(goal.guid), Str2str(goal.name), + GUID_ARGS(var->guid), Str2str(var->name)); +#endif + if (variable_cmp(&goal, var) == 0) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): found\n", + __FILE__, __LINE__-1, __func__); +#endif + found = true; + } + } + } +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + if (result) { + printf("%s:%d:%s(): found:%d result:%p &result->guid:%p &result->list:%p\n" + __FILE__, __LINE__-1, __func__, found, result, + &result->guid, &result->list); + printf("%s:%d:%s(): "GUID_FMT"-%s\n", + __FILE__, __LINE__-1, __func__, GUID_ARGS(result->guid), + Str2str(result->name)); + } else { + printf("%s:%d:%s(): not found\n", + __FILE__, __LINE__-1, __func__); + } +#endif + + if (!found) { + if (name[0] == 0) + status = EFI_NOT_FOUND; + else + status = EFI_INVALID_PARAMETER; + return status; + } + + if (!result) { + status = EFI_NOT_FOUND; + return status; + } + + return mock_gnvn_set_result(size, name, guid, result); +} + +static void +free_var(struct mock_variable *var) +{ + if (!var) + return; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): var:%p &var->guid:%p ", + __FILE__, __LINE__-1, __func__, + var, var ? &var->guid : NULL); + if (var) + printf(GUID_FMT"-%s", GUID_ARGS(var->guid), + var->name ? Str2str(var->name) : ""); + printf("\n"); +#endif + list_del(&var->list); + if (var->size && var->data) + free(var->data); + SetMem(var, sizeof(*var), 0); + free(var); +} + +static bool +mock_sv_attrs_match(UINT32 old, UINT32 new) +{ + UINT32 mask = ~EFI_VARIABLE_APPEND_WRITE; + + return (old & mask) == (new & mask); +} + +static EFI_STATUS +mock_sv_adjust_usage_data(UINT32 attrs, size_t size, ssize_t change) +{ + const UINT32 bs = EFI_VARIABLE_BOOTSERVICE_ACCESS; + const UINT32 bs_nv = bs | EFI_VARIABLE_NON_VOLATILE; + const UINT32 bs_rt = bs | EFI_VARIABLE_RUNTIME_ACCESS; + const UINT32 bs_rt_nv = bs_nv | bs_rt; + struct mock_variable_limits goal = { + .attrs = attrs & bs_rt_nv, + }; + struct mock_variable_limits *qvi_limits = NULL; + struct mock_variable_limits *sv_limits = NULL; + list_t var, *pos = NULL; + UINT64 remaining; + + list_for_each(pos, mock_qvi_limits) { + struct mock_variable_limits *candidate; + + candidate = list_entry(pos, struct mock_variable_limits, list); + if (variable_limits_cmp(&goal, candidate) == 0) { + qvi_limits = candidate; + break; + } + } + + list_for_each(pos, mock_sv_limits) { + struct mock_variable_limits *candidate; + + candidate = list_entry(pos, struct mock_variable_limits, list); + if (variable_limits_cmp(&goal, candidate) == 0) { + sv_limits = candidate; + break; + } + } + if (!sv_limits) { + return EFI_UNSUPPORTED; + } + + if (sv_limits->status != EFI_SUCCESS) + return sv_limits->status; + + if (*sv_limits->max_var_size < size) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():*sv_limits->max_var_size:%zu size:%zu\n", + __FILE__, __LINE__, __func__, + *sv_limits->max_var_size, size); +#endif + return EFI_OUT_OF_RESOURCES; + } + + if (change > 0 && (UINT64)change > *sv_limits->remaining_var_storage) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():*sv_limits->remaining_var_storage:%zu change:%zd\n", + __FILE__, __LINE__, __func__, + *sv_limits->remaining_var_storage, change); +#endif + return EFI_OUT_OF_RESOURCES; + } + + *sv_limits->remaining_var_storage += change; + + if (qvi_limits) { + /* + * If the adjustment here is wrong, we don't want to not do + * the set variable, we also don't want to not account + * for it, and of course we can't have any integer UB. So + * just limit it safely and move on, even though that may + * result in wrong checks against QueryVariableInfo() later. + * + * As if there are correct checks against QueryVariableInfo()... + */ + if (qvi_limits->remaining_var_storage == sv_limits->remaining_var_storage) + ; + else if (change < 0 && (UINT64)-change > *qvi_limits->remaining_var_storage) + *qvi_limits->remaining_var_storage = 0; + else if (change > 0 && UINT64_MAX - *qvi_limits->remaining_var_storage < (UINT64)change) + *qvi_limits->remaining_var_storage = UINT64_MAX; + else + *qvi_limits->remaining_var_storage += change; + } + return EFI_SUCCESS; +} + +static EFI_STATUS +mock_delete_variable(struct mock_variable *var) +{ + EFI_STATUS status; + + status = mock_sv_adjust_usage_data(var->attrs, 0, - var->size); + if (EFI_ERROR(status)) { + printf("%s:%d:%s(): status:0x%lx\n", + __FILE__, __LINE__ - 1, __func__, status); + return status; + } + + status = EFI_SUCCESS; + free_var(var); + return status; +} + +static EFI_STATUS +mock_replace_variable(struct mock_variable *var, VOID *data, UINTN size) +{ + EFI_STATUS status; + VOID *new; + + status = mock_sv_adjust_usage_data(var->attrs, size, + - var->size + size); + if (EFI_ERROR(status)) { + return status; + } + + new = calloc(1, size); + if (!new) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():calloc(1, %zu) failed\n", + __FILE__, __LINE__, __func__, + size); +#endif + status = EFI_OUT_OF_RESOURCES; + return status; + } + memcpy(new, data, size); + free(var->data); + var->data = new; + var->size = size; + + status = EFI_SUCCESS; + return status; +} + +static EFI_STATUS +mock_sv_extend(struct mock_variable *var, VOID *data, UINTN size) +{ + EFI_STATUS status; + uint8_t *new; + + if (size == 0) { + status = EFI_SUCCESS; + return status; + } + + status = mock_sv_adjust_usage_data(var->attrs, var->size + size, size); + if (EFI_ERROR(status)) { + return status; + } + + new = realloc(var->data, var->size + size); + if (!new) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():realloc(%zu) failed\n", + __FILE__, __LINE__, __func__, + var->size + size); +#endif + status = EFI_OUT_OF_RESOURCES; + return status; + } + + memcpy(&new[var->size], data, size); + var->data = (void *)new; + var->size += size; + + status = EFI_SUCCESS; + return status; +} + +void +mock_print_var_list(list_t *head) +{ + list_t *pos = NULL; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():variables so far:\n", __FILE__, __LINE__, __func__); +#endif + list_for_each(pos, head) { + struct mock_variable *var = NULL; + + var = list_entry(pos, struct mock_variable, list); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): "GUID_FMT"-%s (%lu bytes)\n", + __FILE__, __LINE__ - 1, __func__, + GUID_ARGS(var->guid), Str2str(var->name), var->size); +#endif + } +} + +static EFI_STATUS +mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, + VOID *data, struct mock_variable **out) +{ + EFI_STATUS status; + struct mock_variable *var; + uint8_t *buf; + + if (size == 0) { + status = EFI_INVALID_PARAMETER; + return status; + } + + status = EFI_OUT_OF_RESOURCES; + buf = calloc(1, sizeof(struct mock_variable) + StrSize(name)); + if (!buf) { +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): calloc(1, %zu) failed\n", + __FILE__, __LINE__, __func__, + sizeof(struct mock_variable) + StrSize(name)); +#endif + goto err; + } + var = (struct mock_variable *)buf; + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): var:%p &var->guid:%p &var->list:%p\n", + __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); +#endif + + var->data = malloc(size); + if (!var->data) + goto err_free; + + var->name = (CHAR16 *)&buf[sizeof(*var)]; + StrCpy(var->name, name); + memcpy(&var->guid, guid, sizeof(EFI_GUID)); + memcpy(var->data, data, size); + var->size = size; + var->attrs = attrs; + INIT_LIST_HEAD(&var->list); + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): var: "GUID_FMT"-%s\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(var->guid), Str2str(var->name)); +#endif + + *out = var; + status = EFI_SUCCESS; +err_free: + if (EFI_ERROR(status)) + free_var(var); +err: + return status; +} + +EFI_STATUS EFIAPI +mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, + VOID *data) +{ + list_t *pos = NULL, *tmp = NULL, *var_list = NULL; + struct mock_variable goal = { + .name = name, + .guid = *guid, + }; + struct mock_variable *var = NULL; + bool found = false; + bool add_tail = true; + EFI_STATUS status; + long cmp = -1; + + if (!name || name[0] == 0 || !guid) { + status = EFI_INVALID_PARAMETER; + return status; + } + + if ((attrs & EFI_VARIABLE_RUNTIME_ACCESS) && + !(attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS)) { + status = EFI_INVALID_PARAMETER; + return status; + } + +#if 0 + /* + * We don't ever operate after ExitBootServices(), so I'm not + * checking for the missing EFI_VARIABLE_RUNTIME_ACCESS case + */ + if (has_exited_boot_services() && !(attrs & EFI_VARIABLE_RUNTIME_ACCESS)) { + status = EFI_INVALID_PARAMETER; + return status; + } +#endif + +#if 0 + /* + * For now, we're ignoring that we don't support these. + */ + if (attrs & (EFI_VARIABLE_HARDWARE_ERROR_RECORD | + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS)) { + status = EFI_UNSUPPORTED; + return status; + } +#endif + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():Setting "GUID_FMT"-%s\n", + __FILE__, __LINE__ - 1, __func__, + GUID_ARGS(*guid), Str2str(name)); +#endif + switch (mock_variable_sort_policy) { + case MOCK_SORT_PREPEND: + var_list = &mock_variables; + add_tail = false; + break; + case MOCK_SORT_APPEND: + var_list = &mock_variables; + add_tail = true; + break; + case MOCK_SORT_DESCENDING: + add_tail = true; + break; + case MOCK_SORT_ASCENDING: + add_tail = true; + break; + default: + break; + } + + pos = &mock_variables; + list_for_each_safe(pos, tmp, &mock_variables) { + found = false; + var = list_entry(pos, struct mock_variable, list); +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(goal.guid), Str2str(goal.name), + GUID_ARGS(var->guid), Str2str(var->name)); +#endif + cmp = variable_cmp(&goal, var); + cmp = cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); + + switch (mock_variable_sort_policy) { + case MOCK_SORT_DESCENDING: + if (cmp >= 0) { + var_list = pos; + found = true; + } + break; + case MOCK_SORT_ASCENDING: + if (cmp <= 0) { + var_list = pos; + found = true; + } + break; + default: + if (cmp == 0) { + var_list = pos; + found = true; + } + break; + } + if (found) + break; + } +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + printf("%s:%d:%s():var_list:%p &mock_variables:%p cmp:%ld\n", + __FILE__, __LINE__ - 1, __func__, + var_list, &mock_variables, cmp); +#endif + if (cmp != 0 || (cmp == 0 && var_list == &mock_variables)) { + size_t totalsz = size + StrSize(name); +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + printf("%s:%d:%s():var:%p attrs:0x%lx\n", + __FILE__, __LINE__ - 1, __func__, var, attrs); +#endif + status = mock_new_variable(name, guid, attrs, size, data, &var); + if (EFI_ERROR(status)) { + return status; + } + mock_sv_adjust_usage_data(attrs, size, totalsz); + if (EFI_ERROR(status)) { + mock_sv_adjust_usage_data(attrs, 0, -totalsz); + return status; + } + +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + printf("%s:%d:%s(): Adding "GUID_FMT"-%s %s %s\n", + __FILE__, __LINE__ - 1, __func__, + GUID_ARGS(var->guid), Str2str(var->name), + add_tail ? "after" : "before", + list2var(pos)); +#endif + if (add_tail) + list_add_tail(&var->list, pos); + else + list_add(&var->list, pos); + return status; + } + + var = list_entry(var_list, struct mock_variable, list); +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + printf("%s:%d:%s():var:%p attrs:%s cmp:%ld size:%ld\n", + __FILE__, __LINE__ - 1, __func__, + var, format_var_attrs(var->attrs), cmp, size); +#endif + if (!mock_sv_attrs_match(var->attrs, attrs) && !(attrs == 0 && size == 0)) { + status = EFI_INVALID_PARAMETER; + return status; + } + + if (attrs & EFI_VARIABLE_APPEND_WRITE) + return mock_sv_extend(var, data, size); + + if (size == 0) { + UINT32 mask = EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS + | EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS; + /* + * We can't process deletes on these correctly unless we + * parse the header. + */ + if (attrs & mask) { + return EFI_INVALID_PARAMETER; + } + + return mock_delete_variable(var); + } + + return mock_replace_variable(var, data, size); +} + +EFI_STATUS EFIAPI +mock_query_variable_info(UINT32 attrs, UINT64 *max_var_storage, + UINT64 *remaining_var_storage, UINT64 *max_var_size) +{ + list_t mvl, *pos = NULL; + struct mock_variable_limits goal = { + .attrs = attrs, + }; + struct mock_variable_limits *limits = NULL; + EFI_STATUS status; + + if (max_var_storage == NULL || + remaining_var_storage == NULL || + max_var_size == NULL) { + status = EFI_INVALID_PARAMETER; + return status; + } + + list_for_each(pos, mock_qvi_limits) { + limits = list_entry(pos, struct mock_variable_limits, list); + if (variable_limits_cmp(&goal, limits) == 0) { + *max_var_storage = *limits->max_var_storage; + *remaining_var_storage = *limits->remaining_var_storage; + *max_var_size = *limits->max_var_size; + status = EFI_SUCCESS; + return status; + } + } + + status = EFI_UNSUPPORTED; + return status; +} + +static UINT64 default_max_var_storage; +static UINT64 default_remaining_var_storage; +static UINT64 default_max_var_size; + +static struct mock_variable_limits default_limits[] = { + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS, + .max_var_storage = &default_max_var_storage, + .remaining_var_storage = &default_remaining_var_storage, + .max_var_size = &default_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .max_var_storage = &default_max_var_storage, + .remaining_var_storage = &default_remaining_var_storage, + .max_var_size = &default_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &default_max_var_storage, + .remaining_var_storage = &default_remaining_var_storage, + .max_var_size = &default_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .max_var_storage = &default_max_var_storage, + .remaining_var_storage = &default_remaining_var_storage, + .max_var_size = &default_max_var_size, + .status = EFI_SUCCESS, + }, + {.attrs = 0, } +}; + +void +mock_set_default_usage_limits(void) +{ + default_max_var_storage = 65536; + default_remaining_var_storage = 65536; + default_max_var_size = 32768; + + INIT_LIST_HEAD(&mock_default_variable_limits); + for (size_t i = 0; default_limits[i].attrs != 0; i++) { + INIT_LIST_HEAD(&default_limits[i].list); + list_add_tail(&default_limits[i].list, + &mock_default_variable_limits); + } +} + +void +mock_load_one_variable(int dfd, const char * const dirname, char * const name) +{ + int fd; + FILE *f; + int rc; + struct stat statbuf; + size_t guidlen, namelen; + efi_guid_t guid; + size_t sz; + ssize_t offset = 0; + EFI_STATUS status; + UINT32 attrs; + + rc = fstatat(dfd, name, &statbuf, 0); + if (rc < 0) + err(2, "Could not stat \"%s/%s\"", dirname, name); + + if (!(S_ISREG(statbuf.st_mode))) + return; + + if (statbuf.st_size < 5) + errx(2, "Test data variable \"%s/%s\" is too small (%ld bytes)", + dirname, name, statbuf.st_size); + +#if 0 + mock_print_var_list(&mock_variables); +#endif + + uint8_t buf[statbuf.st_size]; + + fd = openat(dfd, name, O_RDONLY); + if (fd < 0) + err(2, "Could not open \"%s/%s\"", dirname, name); + + f = fdopen(fd, "r"); + if (!f) + err(2, "Could not open \"%s/%s\"", dirname, name); + + while (offset != statbuf.st_size) { + sz = fread(buf + offset, 1, statbuf.st_size - offset, f); + if (sz == 0) { + if (ferror(f)) + err(2, "Could not read from \"%s/%s\"", + dirname, name); + if (feof(f)) + errx(2, "Unexpected end of file reading \"%s/%s\"", + dirname, name); + } + + offset += sz; + } + + guidlen = strlen("8be4df61-93ca-11d2-aa0d-00e098032b8c"); + namelen = strlen(name) - guidlen; + + if (namelen < 2) + errx(2, "namelen for \"%s\" is %zu!?!", name, namelen); + + CHAR16 namebuf[namelen]; + + name[namelen-1] = 0; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("loading %s-%s\n", &name[namelen], name); +#endif + for (size_t i = 0; i < namelen; i++) + namebuf[i] = name[i]; + + rc = efi_str_to_guid(&name[namelen], &guid); + if (rc < 0) + err(2, "Could not parse \"%s\" as EFI GUID", &name[namelen]); + + memcpy(&attrs, (UINT32 *)buf, sizeof(UINT32)); + + status = RT->SetVariable(namebuf, (EFI_GUID *)&guid, attrs, + statbuf.st_size - sizeof(attrs), + &buf[sizeof(attrs)]); + if (EFI_ERROR(status)) + errx(2, "%s:%d:%s(): Could not set variable: 0x%llx", + __FILE__, __LINE__ - 1, __func__, + (unsigned long long)status); + + fclose(f); +} + +void +mock_load_variables(const char *const dirname, const char *filters[], + bool filter_out) +{ + int dfd; + DIR *d; + struct dirent *entry; + + d = opendir(dirname); + if (!d) + err(1, "Could not open directory \"%s\"", dirname); + + dfd = dirfd(d); + if (dfd < 0) + err(1, "Could not get directory file descriptor for \"%s\"", + dirname); + + while ((entry = readdir(d)) != NULL) { + size_t len = strlen(entry->d_name); + bool found = false; + if (filters && len > guidstr_size + 1) { + char spacebuf[len]; + + len -= guidstr_size; + SetMem(spacebuf, sizeof(spacebuf)-1, ' '); + spacebuf[len] = '\0'; + for (size_t i = 0; filters[i]; i++) { + if (strlen(filters[i]) > len) + continue; + if (!strncmp(entry->d_name, filters[i], len)) { + found = true; + break; + } + } + } + if ((found == false && filter_out == true) || + (found == true && filter_out == false)) { + mock_load_one_variable(dfd, dirname, entry->d_name); + } + } + + closedir(d); +#if 0 + mock_print_var_list(&mock_variables); +#endif +} + +static bool qvi_installed = false; + +void +mock_install_query_variable_info(void) +{ + qvi_installed = true; + RT->Hdr.Revision = 2ul << 16ul; + RT->QueryVariableInfo = mock_query_variable_info; +} + +void +mock_uninstall_query_variable_info(void) +{ + qvi_installed = false; + RT->Hdr.Revision = EFI_1_10_SYSTEM_TABLE_REVISION; + RT->QueryVariableInfo = mock_efi_unsupported; +} + +void CONSTRUCTOR +mock_reset_variables(void) +{ + list_t *pos = NULL, *tmp = NULL; + static bool once = true; + + init_efi_system_table(); + + if (once) { + INIT_LIST_HEAD(&mock_variables); + once = false; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():mock_variables = {%p,%p};\n", + __FILE__, __LINE__-1, __func__, + mock_variables.next, + mock_variables.prev); + printf("%s:%d:%s():list_empty(&mock_variables):%d\n", + __FILE__, __LINE__-1, __func__, list_empty(&mock_variables)); + printf("%s:%d:%s():list_size(&mock_variables):%d\n", + __FILE__, __LINE__-1, __func__, list_size(&mock_variables)); +#endif + } + + list_for_each_safe(pos, tmp, &mock_variables) { + struct mock_variable *var = NULL; + var = list_entry(pos, struct mock_variable, list); + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():var:"GUID_FMT"-%s\n", + __FILE__, __LINE__-1, __func__, + GUID_ARGS(var->guid), Str2str(var->name)); +#endif + mock_delete_variable(var); + } + INIT_LIST_HEAD(&mock_variables); + mock_set_default_usage_limits(); + + RT->GetVariable = mock_get_variable; + RT->GetNextVariableName = mock_get_next_variable_name; + RT->SetVariable = mock_set_variable; + if (qvi_installed) + mock_install_query_variable_info(); + else + mock_uninstall_query_variable_info(); +} + +void DESTRUCTOR +mock_finalize_vars(void) +{ + mock_reset_variables(); +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/test-data/efivars-0/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 b/test-data/efivars-0/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 new file mode 100644 index 00000000..3e375f03 Binary files /dev/null and b/test-data/efivars-0/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 differ diff --git a/test-data/efivars-0/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 b/test-data/efivars-0/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 new file mode 100644 index 00000000..ad58e851 Binary files /dev/null and b/test-data/efivars-0/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 differ diff --git a/test-data/efivars-0/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e b/test-data/efivars-0/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e new file mode 100644 index 00000000..480c8c8d Binary files /dev/null and b/test-data/efivars-0/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e differ diff --git a/test-data/efivars-0/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-0/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..1bebe830 Binary files /dev/null and b/test-data/efivars-0/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-0/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f b/test-data/efivars-0/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f new file mode 100644 index 00000000..794e2236 Binary files /dev/null and b/test-data/efivars-0/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f differ diff --git a/test-data/efivars-0/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-0/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..caa92886 Binary files /dev/null and b/test-data/efivars-0/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-0/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f b/test-data/efivars-0/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f new file mode 100644 index 00000000..7fe70233 Binary files /dev/null and b/test-data/efivars-0/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f differ diff --git a/test-data/efivars-0/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-0/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..b747b47c Binary files /dev/null and b/test-data/efivars-0/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 b/test-data/efivars-1/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 new file mode 100644 index 00000000..3e375f03 Binary files /dev/null and b/test-data/efivars-1/AMD_PBS_SETUP-a339d746-f678-49b3-9fc7-54ce0f9df226 differ diff --git a/test-data/efivars-1/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 b/test-data/efivars-1/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 new file mode 100644 index 00000000..ad58e851 Binary files /dev/null and b/test-data/efivars-1/AMD_RAID-fe26a894-d199-47d4-8afa-070e3d54ba86 differ diff --git a/test-data/efivars-1/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e b/test-data/efivars-1/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e new file mode 100644 index 00000000..480c8c8d Binary files /dev/null and b/test-data/efivars-1/AMITCGPPIVAR-a8a2093b-fefa-43c1-8e62-ce526847265e differ diff --git a/test-data/efivars-1/AMITSESetup-c811fa38-42c8-4579-a9bb-60e94eddfb34 b/test-data/efivars-1/AMITSESetup-c811fa38-42c8-4579-a9bb-60e94eddfb34 new file mode 100644 index 00000000..ccf2dc2d Binary files /dev/null and b/test-data/efivars-1/AMITSESetup-c811fa38-42c8-4579-a9bb-60e94eddfb34 differ diff --git a/test-data/efivars-1/AOD_SETUP-5ed15dc0-edef-4161-9151-6014c4cc630c b/test-data/efivars-1/AOD_SETUP-5ed15dc0-edef-4161-9151-6014c4cc630c new file mode 100644 index 00000000..77b1eede Binary files /dev/null and b/test-data/efivars-1/AOD_SETUP-5ed15dc0-edef-4161-9151-6014c4cc630c differ diff --git a/test-data/efivars-1/AmdAcpiVar-79941ecd-ed36-49d0-8124-e4c31ac75cd4 b/test-data/efivars-1/AmdAcpiVar-79941ecd-ed36-49d0-8124-e4c31ac75cd4 new file mode 100644 index 00000000..269345bd Binary files /dev/null and b/test-data/efivars-1/AmdAcpiVar-79941ecd-ed36-49d0-8124-e4c31ac75cd4 differ diff --git a/test-data/efivars-1/AmdSetup-3a997502-647a-4c82-998e-52ef9486a247 b/test-data/efivars-1/AmdSetup-3a997502-647a-4c82-998e-52ef9486a247 new file mode 100644 index 00000000..ea257e23 Binary files /dev/null and b/test-data/efivars-1/AmdSetup-3a997502-647a-4c82-998e-52ef9486a247 differ diff --git a/test-data/efivars-1/AmiHardwareSignatureSetupUpdateCountVar-81c76078-bfde-4368-9790-570914c01a65 b/test-data/efivars-1/AmiHardwareSignatureSetupUpdateCountVar-81c76078-bfde-4368-9790-570914c01a65 new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/AmiHardwareSignatureSetupUpdateCountVar-81c76078-bfde-4368-9790-570914c01a65 differ diff --git a/test-data/efivars-1/ApSyncFlagNv-ad3f6761-f0a3-46c8-a4cb-19b70ffdb305 b/test-data/efivars-1/ApSyncFlagNv-ad3f6761-f0a3-46c8-a4cb-19b70ffdb305 new file mode 100644 index 00000000..c100358e Binary files /dev/null and b/test-data/efivars-1/ApSyncFlagNv-ad3f6761-f0a3-46c8-a4cb-19b70ffdb305 differ diff --git a/test-data/efivars-1/AsbkpInfo-cb825795-feb1-4c0b-894f-cc70f8064395 b/test-data/efivars-1/AsbkpInfo-cb825795-feb1-4c0b-894f-cc70f8064395 new file mode 100644 index 00000000..e673fc1e Binary files /dev/null and b/test-data/efivars-1/AsbkpInfo-cb825795-feb1-4c0b-894f-cc70f8064395 differ diff --git a/test-data/efivars-1/AsusExtFancard-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AsusExtFancard-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/AsusExtFancard-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/AsusFanSetupFeatures-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AsusFanSetupFeatures-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..1f4faec7 Binary files /dev/null and b/test-data/efivars-1/AsusFanSetupFeatures-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/AsusHwmSetupOneof-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AsusHwmSetupOneof-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..0f3fe32f Binary files /dev/null and b/test-data/efivars-1/AsusHwmSetupOneof-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/AsusNodePsu-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AsusNodePsu-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/AsusNodePsu-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/AsusQFanSetupData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AsusQFanSetupData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..974cea30 Binary files /dev/null and b/test-data/efivars-1/AsusQFanSetupData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/AsusRomLayout-7186d975-2dba-4413-81a8-9f1538faef5e b/test-data/efivars-1/AsusRomLayout-7186d975-2dba-4413-81a8-9f1538faef5e new file mode 100644 index 00000000..efdb73d0 Binary files /dev/null and b/test-data/efivars-1/AsusRomLayout-7186d975-2dba-4413-81a8-9f1538faef5e differ diff --git a/test-data/efivars-1/AsusSetupDataBackup-1111b056-c5e9-40ca-aba3-ec172533d814 b/test-data/efivars-1/AsusSetupDataBackup-1111b056-c5e9-40ca-aba3-ec172533d814 new file mode 100644 index 00000000..418e63b8 Binary files /dev/null and b/test-data/efivars-1/AsusSetupDataBackup-1111b056-c5e9-40ca-aba3-ec172533d814 differ diff --git a/test-data/efivars-1/AutoDetectData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/AutoDetectData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..730e919f Binary files /dev/null and b/test-data/efivars-1/AutoDetectData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/BiosEventLog-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 b/test-data/efivars-1/BiosEventLog-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 new file mode 100644 index 00000000..4ef95072 Binary files /dev/null and b/test-data/efivars-1/BiosEventLog-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 differ diff --git a/test-data/efivars-1/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..1bebe830 Binary files /dev/null and b/test-data/efivars-1/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..fdbf58aa Binary files /dev/null and b/test-data/efivars-1/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..395525be Binary files /dev/null and b/test-data/efivars-1/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..d4a6326b Binary files /dev/null and b/test-data/efivars-1/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0004-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0004-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..9a151ed7 Binary files /dev/null and b/test-data/efivars-1/Boot0004-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0005-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0005-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..aa8ba6f1 Binary files /dev/null and b/test-data/efivars-1/Boot0005-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Boot0006-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Boot0006-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..cb32ae74 Binary files /dev/null and b/test-data/efivars-1/Boot0006-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..4be6b177 Binary files /dev/null and b/test-data/efivars-1/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/BootFromUSB-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/BootFromUSB-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/BootFromUSB-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..f06270d6 Binary files /dev/null and b/test-data/efivars-1/BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..97119095 Binary files /dev/null and b/test-data/efivars-1/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/CMOSfailflag-c89dc9c7-5105-472c-a743-b1621e142b41 b/test-data/efivars-1/CMOSfailflag-c89dc9c7-5105-472c-a743-b1621e142b41 new file mode 100644 index 00000000..7cfdbd29 Binary files /dev/null and b/test-data/efivars-1/CMOSfailflag-c89dc9c7-5105-472c-a743-b1621e142b41 differ diff --git a/test-data/efivars-1/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..c6875549 Binary files /dev/null and b/test-data/efivars-1/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..1530945f Binary files /dev/null and b/test-data/efivars-1/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..1f8a9503 Binary files /dev/null and b/test-data/efivars-1/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..2dcf180c Binary files /dev/null and b/test-data/efivars-1/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/CurrentPolicy-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/CurrentPolicy-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..88c1ba07 Binary files /dev/null and b/test-data/efivars-1/CurrentPolicy-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/DefaultBootOrder-45cf35f6-0d6e-4d04-856a-0370a5b16f53 b/test-data/efivars-1/DefaultBootOrder-45cf35f6-0d6e-4d04-856a-0370a5b16f53 new file mode 100644 index 00000000..e8844a33 Binary files /dev/null and b/test-data/efivars-1/DefaultBootOrder-45cf35f6-0d6e-4d04-856a-0370a5b16f53 differ diff --git a/test-data/efivars-1/DeploymentModeNv-97e8965f-c761-4f48-b6e4-9ffa9cb2a2d6 b/test-data/efivars-1/DeploymentModeNv-97e8965f-c761-4f48-b6e4-9ffa9cb2a2d6 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/DeploymentModeNv-97e8965f-c761-4f48-b6e4-9ffa9cb2a2d6 differ diff --git a/test-data/efivars-1/DownCoreStatus-29749bad-401b-4f6d-b124-cece8c590c48 b/test-data/efivars-1/DownCoreStatus-29749bad-401b-4f6d-b124-cece8c590c48 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/DownCoreStatus-29749bad-401b-4f6d-b124-cece8c590c48 differ diff --git a/test-data/efivars-1/EnWpData-cbab171f-f356-4009-baaa-6628353a0a29 b/test-data/efivars-1/EnWpData-cbab171f-f356-4009-baaa-6628353a0a29 new file mode 100644 index 00000000..6f0c2f14 Binary files /dev/null and b/test-data/efivars-1/EnWpData-cbab171f-f356-4009-baaa-6628353a0a29 differ diff --git a/test-data/efivars-1/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..1f8a9503 Binary files /dev/null and b/test-data/efivars-1/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/FPLayoutOrder-4db88a62-6721-47a0-9082-280b00323594 b/test-data/efivars-1/FPLayoutOrder-4db88a62-6721-47a0-9082-280b00323594 new file mode 100644 index 00000000..6d032958 Binary files /dev/null and b/test-data/efivars-1/FPLayoutOrder-4db88a62-6721-47a0-9082-280b00323594 differ diff --git a/test-data/efivars-1/FTMActiveFlag-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 b/test-data/efivars-1/FTMActiveFlag-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/FTMActiveFlag-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 differ diff --git a/test-data/efivars-1/FastBootOption-b540a530-6978-4da7-91cb-7207d764d262 b/test-data/efivars-1/FastBootOption-b540a530-6978-4da7-91cb-7207d764d262 new file mode 100644 index 00000000..502ad840 Binary files /dev/null and b/test-data/efivars-1/FastBootOption-b540a530-6978-4da7-91cb-7207d764d262 differ diff --git a/test-data/efivars-1/FirstBootFlag-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/FirstBootFlag-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..a652cc86 Binary files /dev/null and b/test-data/efivars-1/FirstBootFlag-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/HddSmartInfo-a6f44860-b2e8-4fda-bd45-78368994b6ae b/test-data/efivars-1/HddSmartInfo-a6f44860-b2e8-4fda-bd45-78368994b6ae new file mode 100644 index 00000000..7cfdbd29 Binary files /dev/null and b/test-data/efivars-1/HddSmartInfo-a6f44860-b2e8-4fda-bd45-78368994b6ae differ diff --git a/test-data/efivars-1/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0 b/test-data/efivars-1/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0 new file mode 100644 index 00000000..89e10eab Binary files /dev/null and b/test-data/efivars-1/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0 differ diff --git a/test-data/efivars-1/HwErrRecSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/HwErrRecSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..60f8feb8 Binary files /dev/null and b/test-data/efivars-1/HwErrRecSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..062abae1 Binary files /dev/null and b/test-data/efivars-1/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/KEKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/KEKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..2ebd5c58 Binary files /dev/null and b/test-data/efivars-1/KEKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/Kernel_ATPSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_ATPSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_ATPSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/Kernel_DriverSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_DriverSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_DriverSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/Kernel_RvkSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_RvkSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_RvkSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/Kernel_SiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_SiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_SiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/Kernel_SkuSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_SkuSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_SkuSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/Kernel_WinSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b b/test-data/efivars-1/Kernel_WinSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b new file mode 100644 index 00000000..78bf6592 Binary files /dev/null and b/test-data/efivars-1/Kernel_WinSiStatus-77fa9abd-0359-4d32-bd60-28f4e78f784b differ diff --git a/test-data/efivars-1/LastBoot-b540a530-6978-4da7-91cb-7207d764d262 b/test-data/efivars-1/LastBoot-b540a530-6978-4da7-91cb-7207d764d262 new file mode 100644 index 00000000..ef1bad0a Binary files /dev/null and b/test-data/efivars-1/LastBoot-b540a530-6978-4da7-91cb-7207d764d262 differ diff --git a/test-data/efivars-1/MaximumTableSize-4b3082a3-80c6-4d7e-9cd0-583917265df1 b/test-data/efivars-1/MaximumTableSize-4b3082a3-80c6-4d7e-9cd0-583917265df1 new file mode 100644 index 00000000..b4637d81 Binary files /dev/null and b/test-data/efivars-1/MaximumTableSize-4b3082a3-80c6-4d7e-9cd0-583917265df1 differ diff --git a/test-data/efivars-1/MemoryOverwriteRequestControl-e20939be-32d4-41be-a150-897f85d49829 b/test-data/efivars-1/MemoryOverwriteRequestControl-e20939be-32d4-41be-a150-897f85d49829 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/MemoryOverwriteRequestControl-e20939be-32d4-41be-a150-897f85d49829 differ diff --git a/test-data/efivars-1/MemoryOverwriteRequestControlLock-bb983ccf-151d-40e1-a07b-4a17be168292 b/test-data/efivars-1/MemoryOverwriteRequestControlLock-bb983ccf-151d-40e1-a07b-4a17be168292 new file mode 100644 index 00000000..f37b1b76 Binary files /dev/null and b/test-data/efivars-1/MemoryOverwriteRequestControlLock-bb983ccf-151d-40e1-a07b-4a17be168292 differ diff --git a/test-data/efivars-1/MokList-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/MokList-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..7567e40c Binary files /dev/null and b/test-data/efivars-1/MokList-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..a4024770 Binary files /dev/null and b/test-data/efivars-1/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/MokListX-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/MokListX-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..d6de49f1 Binary files /dev/null and b/test-data/efivars-1/MokListX-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..6c058bfc Binary files /dev/null and b/test-data/efivars-1/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/MonotonicCounter-01368881-c4ad-4b1d-b631-d57a8ec8db6b b/test-data/efivars-1/MonotonicCounter-01368881-c4ad-4b1d-b631-d57a8ec8db6b new file mode 100644 index 00000000..92182fea Binary files /dev/null and b/test-data/efivars-1/MonotonicCounter-01368881-c4ad-4b1d-b631-d57a8ec8db6b differ diff --git a/test-data/efivars-1/MyFav-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 b/test-data/efivars-1/MyFav-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 new file mode 100644 index 00000000..4ecf019d Binary files /dev/null and b/test-data/efivars-1/MyFav-4034591c-48ea-4cdc-864f-e7cb61cfd0f2 differ diff --git a/test-data/efivars-1/NVRAM_Verify-15a9dd61-e4f8-4a99-80db-353b13d76490 b/test-data/efivars-1/NVRAM_Verify-15a9dd61-e4f8-4a99-80db-353b13d76490 new file mode 100644 index 00000000..e055de8b Binary files /dev/null and b/test-data/efivars-1/NVRAM_Verify-15a9dd61-e4f8-4a99-80db-353b13d76490 differ diff --git a/test-data/efivars-1/NetworkStackVar-d1405d16-7afc-4695-bb12-41459d3695a2 b/test-data/efivars-1/NetworkStackVar-d1405d16-7afc-4695-bb12-41459d3695a2 new file mode 100644 index 00000000..ab120d7d Binary files /dev/null and b/test-data/efivars-1/NetworkStackVar-d1405d16-7afc-4695-bb12-41459d3695a2 differ diff --git a/test-data/efivars-1/NvHdd0-e57abcbd-9456-4639-8f65-06aab41d840f b/test-data/efivars-1/NvHdd0-e57abcbd-9456-4639-8f65-06aab41d840f new file mode 100644 index 00000000..230f3f60 Binary files /dev/null and b/test-data/efivars-1/NvHdd0-e57abcbd-9456-4639-8f65-06aab41d840f differ diff --git a/test-data/efivars-1/NvHdd8-e57abcbd-9456-4639-8f65-06aab41d840f b/test-data/efivars-1/NvHdd8-e57abcbd-9456-4639-8f65-06aab41d840f new file mode 100644 index 00000000..137dd654 Binary files /dev/null and b/test-data/efivars-1/NvHdd8-e57abcbd-9456-4639-8f65-06aab41d840f differ diff --git a/test-data/efivars-1/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..ad58e851 Binary files /dev/null and b/test-data/efivars-1/OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..65902d34 Binary files /dev/null and b/test-data/efivars-1/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/PCI_COMMON-aca9f304-21e2-4852-9875-7ff4881d67a5 b/test-data/efivars-1/PCI_COMMON-aca9f304-21e2-4852-9875-7ff4881d67a5 new file mode 100644 index 00000000..1484033a Binary files /dev/null and b/test-data/efivars-1/PCI_COMMON-aca9f304-21e2-4852-9875-7ff4881d67a5 differ diff --git a/test-data/efivars-1/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..2a784c4c Binary files /dev/null and b/test-data/efivars-1/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..38d00e83 Binary files /dev/null and b/test-data/efivars-1/PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/PcieSataModVar-5e9a565f-cdc0-413b-ad13-1fe8713ffdcd b/test-data/efivars-1/PcieSataModVar-5e9a565f-cdc0-413b-ad13-1fe8713ffdcd new file mode 100644 index 00000000..7b89b915 Binary files /dev/null and b/test-data/efivars-1/PcieSataModVar-5e9a565f-cdc0-413b-ad13-1fe8713ffdcd differ diff --git a/test-data/efivars-1/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..456f5e7d Binary files /dev/null and b/test-data/efivars-1/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..aacb9a6f Binary files /dev/null and b/test-data/efivars-1/PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/PreVgaInfo-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/PreVgaInfo-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..e85f1be7 Binary files /dev/null and b/test-data/efivars-1/PreVgaInfo-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/RsdpAddr-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/RsdpAddr-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..b6949db9 Binary files /dev/null and b/test-data/efivars-1/RsdpAddr-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/SIDSUPPORT-7d3dceee-cbce-4ea7-8709-6e552f1edbde b/test-data/efivars-1/SIDSUPPORT-7d3dceee-cbce-4ea7-8709-6e552f1edbde new file mode 100644 index 00000000..a652cc86 Binary files /dev/null and b/test-data/efivars-1/SIDSUPPORT-7d3dceee-cbce-4ea7-8709-6e552f1edbde differ diff --git a/test-data/efivars-1/SbatLevel-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/SbatLevel-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..8a889d31 Binary files /dev/null and b/test-data/efivars-1/SbatLevel-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 b/test-data/efivars-1/SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 new file mode 100644 index 00000000..29c9a9b7 Binary files /dev/null and b/test-data/efivars-1/SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 differ diff --git a/test-data/efivars-1/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..687e5611 Binary files /dev/null and b/test-data/efivars-1/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/SecureBootSetup-7b59104a-c00d-4158-87ff-f04d6396a915 b/test-data/efivars-1/SecureBootSetup-7b59104a-c00d-4158-87ff-f04d6396a915 new file mode 100644 index 00000000..65869b98 Binary files /dev/null and b/test-data/efivars-1/SecureBootSetup-7b59104a-c00d-4158-87ff-f04d6396a915 differ diff --git a/test-data/efivars-1/Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..8c8e7c37 Binary files /dev/null and b/test-data/efivars-1/Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/SetupLedData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/SetupLedData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..4c6a2858 Binary files /dev/null and b/test-data/efivars-1/SetupLedData-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..39e28582 Binary files /dev/null and b/test-data/efivars-1/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..c71572cf Binary files /dev/null and b/test-data/efivars-1/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/SmbiosEntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 b/test-data/efivars-1/SmbiosEntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 new file mode 100644 index 00000000..58fad41d Binary files /dev/null and b/test-data/efivars-1/SmbiosEntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 differ diff --git a/test-data/efivars-1/SmbiosScratchBuffer-4b3082a3-80c6-4d7e-9cd0-583917265df1 b/test-data/efivars-1/SmbiosScratchBuffer-4b3082a3-80c6-4d7e-9cd0-583917265df1 new file mode 100644 index 00000000..7b65c3a6 Binary files /dev/null and b/test-data/efivars-1/SmbiosScratchBuffer-4b3082a3-80c6-4d7e-9cd0-583917265df1 differ diff --git a/test-data/efivars-1/SmbiosV3EntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 b/test-data/efivars-1/SmbiosV3EntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 new file mode 100644 index 00000000..43056920 Binary files /dev/null and b/test-data/efivars-1/SmbiosV3EntryPointTable-4b3082a3-80c6-4d7e-9cd0-583917265df1 differ diff --git a/test-data/efivars-1/StdDefaults-4599d26f-1a11-49b8-b91f-858745cff824 b/test-data/efivars-1/StdDefaults-4599d26f-1a11-49b8-b91f-858745cff824 new file mode 100644 index 00000000..da3d726a Binary files /dev/null and b/test-data/efivars-1/StdDefaults-4599d26f-1a11-49b8-b91f-858745cff824 differ diff --git a/test-data/efivars-1/TPMPERBIOSFLAGS-7d3dceee-cbce-4ea7-8709-6e552f1edbde b/test-data/efivars-1/TPMPERBIOSFLAGS-7d3dceee-cbce-4ea7-8709-6e552f1edbde new file mode 100644 index 00000000..60e62642 Binary files /dev/null and b/test-data/efivars-1/TPMPERBIOSFLAGS-7d3dceee-cbce-4ea7-8709-6e552f1edbde differ diff --git a/test-data/efivars-1/Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..60f8feb8 Binary files /dev/null and b/test-data/efivars-1/Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/TotalNumberOfRootBridges-fb5703f5-f8a7-f401-18b4-3f108deb2612 b/test-data/efivars-1/TotalNumberOfRootBridges-fb5703f5-f8a7-f401-18b4-3f108deb2612 new file mode 100644 index 00000000..ba508349 Binary files /dev/null and b/test-data/efivars-1/TotalNumberOfRootBridges-fb5703f5-f8a7-f401-18b4-3f108deb2612 differ diff --git a/test-data/efivars-1/TpmServFlags-7d3dceee-cbce-4ea7-8709-6e552f1edbde b/test-data/efivars-1/TpmServFlags-7d3dceee-cbce-4ea7-8709-6e552f1edbde new file mode 100644 index 00000000..c40690de Binary files /dev/null and b/test-data/efivars-1/TpmServFlags-7d3dceee-cbce-4ea7-8709-6e552f1edbde differ diff --git a/test-data/efivars-1/UsbSupport-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 b/test-data/efivars-1/UsbSupport-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 new file mode 100644 index 00000000..8b3fa282 Binary files /dev/null and b/test-data/efivars-1/UsbSupport-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 differ diff --git a/test-data/efivars-1/VARSTORE_OCMR_SETTINGS_NAME-c05fba7d-7a92-49e0-bcee-233b14dca803 b/test-data/efivars-1/VARSTORE_OCMR_SETTINGS_NAME-c05fba7d-7a92-49e0-bcee-233b14dca803 new file mode 100644 index 00000000..bec6d587 Binary files /dev/null and b/test-data/efivars-1/VARSTORE_OCMR_SETTINGS_NAME-c05fba7d-7a92-49e0-bcee-233b14dca803 differ diff --git a/test-data/efivars-1/VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..39e28582 Binary files /dev/null and b/test-data/efivars-1/VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/WpBufAddr-cba83c4a-a5fc-48a8-b3a6-d33636166544 b/test-data/efivars-1/WpBufAddr-cba83c4a-a5fc-48a8-b3a6-d33636166544 new file mode 100644 index 00000000..c2632ec0 Binary files /dev/null and b/test-data/efivars-1/WpBufAddr-cba83c4a-a5fc-48a8-b3a6-d33636166544 differ diff --git a/test-data/efivars-1/WriteOnceStatus-4b3082a3-80c6-4d7e-9cd0-583917265df1 b/test-data/efivars-1/WriteOnceStatus-4b3082a3-80c6-4d7e-9cd0-583917265df1 new file mode 100644 index 00000000..65648ca3 Binary files /dev/null and b/test-data/efivars-1/WriteOnceStatus-4b3082a3-80c6-4d7e-9cd0-583917265df1 differ diff --git a/test-data/efivars-1/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f b/test-data/efivars-1/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f new file mode 100644 index 00000000..794e2236 Binary files /dev/null and b/test-data/efivars-1/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f differ diff --git a/test-data/efivars-1/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..caa92886 Binary files /dev/null and b/test-data/efivars-1/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-data/efivars-1/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f b/test-data/efivars-1/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f new file mode 100644 index 00000000..7fe70233 Binary files /dev/null and b/test-data/efivars-1/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f differ diff --git a/test-data/efivars-1/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c b/test-data/efivars-1/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 00000000..b747b47c Binary files /dev/null and b/test-data/efivars-1/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c differ diff --git a/test-mock-variables.c b/test-mock-variables.c new file mode 100644 index 00000000..5ea5a981 --- /dev/null +++ b/test-mock-variables.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-mock-variables.c - test our mock variable implementation (irony) + * Copyright Peter Jones + */ + +#include "shim.h" +#include "mock-variables.h" + +#include +#include +#include +#include + +#include "test-data-efivars-0.h" + +#pragma GCC diagnostic ignored "-Wunused-label" + +void mock_print_guidname(EFI_GUID *guid, CHAR16 *name); +void mock_print_var_list(list_t *head); + +static int +test_filter_out_helper(size_t nvars, const CHAR16 *varnames[nvars], + bool filter_out, UINTN expected_count) +{ + const char *mok_rt_vars[n_mok_state_variables]; + EFI_STATUS status; + EFI_GUID guid = SHIM_LOCK_GUID; + CHAR16 name[1024] = L""; + UINTN sz; + char asciiname[1024]; + bool found = false; + int ret = 0; + UINTN count = 0; + + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; + } + + sz = sizeof(name); + status = RT->GetNextVariableName(&sz, name, &guid); + assert_equal_return(status, EFI_NOT_FOUND, -1, "got %lx, expected %lx"); + + mock_load_variables("test-data/efivars-1", mok_rt_vars, filter_out); + + while (true) { + int rc = 0; + + sz = sizeof(name); + status = RT->GetNextVariableName(&sz, name, &guid); + if (status == EFI_NOT_FOUND) + break; + if (EFI_ERROR(status)) + return -1; + + count += 1; + SetMem(asciiname, sizeof(asciiname), 0); + for (UINTN i = 0; i < sizeof(asciiname); i++) + asciiname[i] = name[i]; + for (UINTN i = 0; varnames[i] != NULL; i++) { + if (sz == 0 || StrLen(varnames[i]) != sz-1) + continue; + if (StrCmp(name, varnames[i]) == 0) { + found = true; + if (filter_out) { + rc = assert_false_as_expr(found, -1, + "found=%u for undesired variable \"%s\"\n", + asciiname); + break; + } + } + } + if (!filter_out) + rc = assert_true_as_expr(found, -1, + "found=%u for undesired variable \"%s\"\n", + asciiname); + if (ret >= 0 && rc < 0) + ret = rc; + } + + mock_reset_variables(); + assert_equal_return(count, expected_count, -1, "%lu != %lu\n"); + assert_true_return(list_empty(&mock_variables), -1, "%lu != %lu\n"); + + return ret; +} + +static int +test_filter_out_true(void) +{ + const CHAR16 *varnames[] = { + L"MokListRT", + L"MokListXRT", + L"SbatLevelRT", + NULL + }; + size_t nvars = sizeof(varnames) / sizeof(varnames[0]); + + return test_filter_out_helper(nvars, varnames, true, 3); +} + +static int +test_filter_out_false(void) +{ + const CHAR16 *varnames[] = { + L"MokListRT", + L"MokListXRT", + L"SbatLevelRT", + NULL + }; + size_t nvars = sizeof(varnames) / sizeof(varnames[0]); + + return test_filter_out_helper(nvars, varnames, false, 3); +} + +static int +test_gnvn_buf_size_0(void) +{ + UINTN size = 0; + CHAR16 buf[6] = { 0, }; + EFI_STATUS status; + EFI_GUID empty_guid = { 0, }; + int ret = -1; + + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "0x%lx != 0x%lx\n"); + + size = 1; + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + assert_equal_return(status, EFI_NOT_FOUND, -1, "0x%lx != 0x%lx\n"); + + status = RT->SetVariable(L"test", &GV_GUID, + EFI_VARIABLE_BOOTSERVICE_ACCESS, 5, "test"); + assert_equal_return(status, EFI_SUCCESS, -1, "0x%lx != 0x%lx\n"); + + size = 1; + status = RT->GetNextVariableName(&size, &buf[0], &empty_guid); + assert_equal_goto(status, EFI_NOT_FOUND, err, "0x%lx != 0x%lx\n"); + + size = 1; + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + assert_equal_goto(status, EFI_BUFFER_TOO_SMALL, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, StrSize(L"test"), err, "%zu != %zu\n"); + + size = StrSize(L"test"); + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + + status = RT->SetVariable(L"testa", &GV_GUID, + EFI_VARIABLE_BOOTSERVICE_ACCESS, 5, "test"); + assert_equal_return(status, EFI_SUCCESS, -1, "0x%lx != 0x%lx\n"); + + buf[0] = 0; + size = 1; + status = RT->GetNextVariableName(&size, &buf[0], &empty_guid); + assert_equal_goto(status, EFI_NOT_FOUND, err, "0x%lx != 0x%lx\n"); + + size = StrSize(L"test"); + StrCpy(buf, L"test"); + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + switch (mock_variable_sort_policy) { + case MOCK_SORT_DESCENDING: + case MOCK_SORT_PREPEND: + assert_equal_goto(status, EFI_NOT_FOUND, err, "0x%lx != 0x%lx\n"); + break; + case MOCK_SORT_APPEND: + case MOCK_SORT_ASCENDING: + assert_equal_goto(status, EFI_BUFFER_TOO_SMALL, err, "0x%lx != 0x%lx\n"); + break; + default: + break; + } + + size = StrSize(L"testa"); + StrCpy(buf, L"test"); + status = RT->GetNextVariableName(&size, &buf[0], &GV_GUID); + switch (mock_variable_sort_policy) { + case MOCK_SORT_DESCENDING: + case MOCK_SORT_PREPEND: + assert_equal_goto(status, EFI_NOT_FOUND, err, "0x%lx != 0x%lx\n"); + break; + case MOCK_SORT_APPEND: + case MOCK_SORT_ASCENDING: + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + break; + default: + break; + } + + ret = 0; +err: + mock_reset_variables(); + return ret; +} + +static int +test_gnvn_helper(char *testvars) +{ + UINTN size = 0; + CHAR16 buf[8192] = { 0, }; + EFI_STATUS status; + EFI_GUID empty_guid = { 0, }; + int ret = -1; + const char *mok_rt_vars[n_mok_state_variables]; + + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; + } + + mock_load_variables(testvars, mok_rt_vars, true); + + size = sizeof(buf); + buf[0] = L'\0'; + status = RT->GetNextVariableName(&size, buf, &GV_GUID); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + dump_mock_variables(__FILE__, __LINE__, __func__); +#endif + switch (mock_variable_sort_policy) { + case MOCK_SORT_DESCENDING: + dump_mock_variables_if_wrong(__FILE__, __LINE__, __func__, + &GV_GUID, L"dbxDefault"); + assert_zero_goto(StrCmp(buf, L"dbxDefault"), err, "0x%lx != 0x%lx\n"); + break; + case MOCK_SORT_ASCENDING: + dump_mock_variables_if_wrong(__FILE__, __LINE__, __func__, + &GV_GUID, L"Boot0000"); + assert_zero_goto(StrCmp(buf, L"Boot0000"), err, "0x%lx != 0x%lx buf:\"%s\"\n", + 0, Str2str(buf)); + break; + default: + break; + } + + size = sizeof(buf); + buf[0] = 0; + status = RT->GetNextVariableName(&size, buf, &EFI_SECURE_BOOT_DB_GUID); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + switch (mock_variable_sort_policy) { + case MOCK_SORT_DESCENDING: + assert_zero_goto(StrCmp(buf, L"dbx"), err, "0x%lx != 0x%lx\n"); + break; + case MOCK_SORT_ASCENDING: + assert_zero_goto(StrCmp(buf, L"db"), err, "0x%lx != 0x%lx\n"); + break; + default: + break; + } + + ret = 0; +err: + if (ret) + mock_print_var_list(&mock_variables); + mock_reset_variables(); + return ret; +} + +static int +test_gnvn_0(void) +{ + return test_gnvn_helper("test-data/efivars-0"); +} + +static int +test_gnvn_1(void) +{ + return test_gnvn_helper("test-data/efivars-1"); +} + +static int +test_get_variable_0(void) +{ + UINTN size = 0; + uint8_t buf[8192] = { 0, }; + EFI_STATUS status; + EFI_GUID empty_guid = { 0, }; + UINT32 attrs = 0; + int ret = -1; + int cmp; + const char *mok_rt_vars[n_mok_state_variables]; + + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; + } + + mock_load_variables("test-data/efivars-1", mok_rt_vars, true); + + size = 0; + status = RT->GetVariable(L"Boot0000", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_BUFFER_TOO_SMALL, err, "0x%lx != 0x%lx\n"); + + size = sizeof(buf); + status = RT->GetVariable(L"Boot0000", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, sizeof(test_data_efivars_0_Boot0000), err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, test_data_efivars_0_Boot0000, size), err, "%zu != %zu\n"); + + ret = 0; +err: + if (ret) + mock_print_var_list(&mock_variables); + mock_reset_variables(); + return ret; +} + +static int +test_set_variable_0(void) +{ + UINTN size = 0; + uint8_t buf[8192] = { 0, }; + EFI_STATUS status; + EFI_GUID empty_guid = { 0, }; + UINT32 attrs = 0; + int ret = -1; + UINT32 bs_rt_nv = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE; + + size = 4; + strcpy(buf, "foo"); + status = RT->SetVariable(L"tmp", &GV_GUID, bs_rt_nv, size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + + size = sizeof(buf); + SetMem(buf, sizeof(buf), 0); + status = RT->GetVariable(L"tmp", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, 4, err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, "foo", 4), err, "0x%lx != 0x%lx\n"); + + size = 5; + strcpy(buf, "bang"); + status = RT->SetVariable(L"tmp", &GV_GUID, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, buf); + size = sizeof(buf); + SetMem(buf, sizeof(buf), 0); + status = RT->GetVariable(L"tmp", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, 5, err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, "bang", 5), err, "%d != %d\n"); + + size = 5; + strcpy(buf, "foop"); + status = RT->SetVariable(L"tmp", &GV_GUID, bs_rt_nv, size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + + size = sizeof(buf); + SetMem(buf, sizeof(buf), 0); + status = RT->GetVariable(L"tmp", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, 5, err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, "foop", 5), err, "%d != %d\n"); + + size = 0; + strcpy(buf, ""); + status = RT->SetVariable(L"tmp", &GV_GUID, bs_rt_nv | EFI_VARIABLE_APPEND_WRITE, size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + + size = sizeof(buf); + SetMem(buf, sizeof(buf), 0); + status = RT->GetVariable(L"tmp", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, 5, err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, "foop", 5), err, "%d != %d\n"); + + size = 5; + strcpy(buf, "poof"); + status = RT->SetVariable(L"tmp", &GV_GUID, bs_rt_nv | EFI_VARIABLE_APPEND_WRITE, size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + + size = sizeof(buf); + SetMem(buf, sizeof(buf), 0); + status = RT->GetVariable(L"tmp", &GV_GUID, &attrs, &size, buf); + assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); + assert_equal_goto(size, 10, err, "%zu != %zu\n"); + assert_zero_goto(memcmp(buf, "foop\0poof", 10), err, "%d != %d\n"); + ret = 0; +err: + if (ret) + mock_print_var_list(&mock_variables); + mock_reset_variables(); + return ret; +} + +int +main(void) +{ + int status = 0; + setbuf(stdout, NULL); + + const char *policies[] = { + "MOCK_SORT_DESCENDING", + "MOCK_SORT_PREPEND", + "MOCK_SORT_APPEND", + "MOCK_SORT_ASCENDING", + "MOCK_SORT_MAX_SENTINEL" + }; + + test(test_filter_out_true); + test(test_filter_out_false); + + for (int i = 0; i < MOCK_SORT_MAX_SENTINEL; i++) { + mock_variable_sort_policy = i; + printf("%s: setting variable sort policy to %s\n", + program_invocation_short_name, policies[i]); + + test(test_gnvn_buf_size_0); + test(test_gnvn_0); + test(test_gnvn_1); + } + + test(test_get_variable_0); + test(test_set_variable_0); + return status; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/test.c b/test.c index 8a4a72ff..46cab533 100644 --- a/test.c +++ b/test.c @@ -259,4 +259,12 @@ console_print(const CHAR16 *fmt, ...) return 0; } +#ifndef HAVE_START_IMAGE +EFI_STATUS +start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) +{ + return EFI_UNSUPPORTED; +} +#endif + // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 54bc72cf0abd306d96782cdfa6db7c71ff72e056 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 3 Aug 2021 13:11:14 -0400 Subject: tests: model different behaviors for deleting variables Signed-off-by: Peter Jones --- include/mock-variables.h | 5 +++++ mock-variables.c | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index 7ba00847..fc276ce7 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -41,6 +41,11 @@ typedef enum { extern mock_sort_policy_t mock_variable_sort_policy; +#define MOCK_VAR_DELETE_ATTR_ALLOW_ZERO 0x01 +#define MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH 0x02 + +extern UINT32 mock_variable_delete_attr_policy; + extern list_t mock_default_variable_limits; extern list_t *mock_qvi_limits; extern list_t *mock_sv_limits; diff --git a/mock-variables.c b/mock-variables.c index d8342d59..e02febec 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -27,6 +27,8 @@ list_t mock_variables = LIST_HEAD_INIT(mock_variables); mock_sort_policy_t mock_variable_sort_policy = MOCK_SORT_APPEND; +UINT32 mock_variable_delete_attr_policy; + static const size_t guidstr_size = sizeof("8be4df61-93ca-11d2-aa0d-00e098032b8c"); static int @@ -708,9 +710,23 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, __FILE__, __LINE__ - 1, __func__, var, format_var_attrs(var->attrs), cmp, size); #endif - if (!mock_sv_attrs_match(var->attrs, attrs) && !(attrs == 0 && size == 0)) { + if (!mock_sv_attrs_match(var->attrs, attrs)) { status = EFI_INVALID_PARAMETER; - return status; + if (size == 0 && !(attrs & EFI_VARIABLE_APPEND_WRITE)) { + if ((mock_variable_delete_attr_policy & MOCK_VAR_DELETE_ATTR_ALLOW_ZERO) + && attrs == 0) { + status = EFI_SUCCESS; + } else if (mock_variable_delete_attr_policy & MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH) { + status = EFI_SUCCESS; + } + } + if (EFI_ERROR(status)) { + printf("%s:%d:%s(): var->attrs:%s attrs:%s\n", + __FILE__, __LINE__ - 1, __func__, + format_var_attrs(var->attrs), + format_var_attrs(attrs)); + return status; + } } if (attrs & EFI_VARIABLE_APPEND_WRITE) @@ -1004,6 +1020,8 @@ mock_reset_variables(void) INIT_LIST_HEAD(&mock_variables); mock_set_default_usage_limits(); + mock_variable_delete_attr_policy = MOCK_VAR_DELETE_ATTR_ALLOW_ZERO; + RT->GetVariable = mock_get_variable; RT->GetNextVariableName = mock_get_next_variable_name; RT->SetVariable = mock_set_variable; -- cgit v1.2.3 From 63a5ae1f7c9383f43e4431316eb0c77bcb079b98 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 26 Jul 2021 17:29:15 -0400 Subject: tests: Add config table support This adds a simple implementation of ST->ConfigurationTable, ST->NumberOfTableEntries, and BS->InstallConfigurationTable to our test harness. Currently it is limited at 1024 entries, but that should be well more than enough for any tests we've currently considered. Signed-off-by: Peter Jones --- include/mock-variables.h | 9 +- mock-variables.c | 171 ++++++++++++++++++- test-mock-variables.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 599 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index fc276ce7..759fd1f0 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -21,6 +21,8 @@ EFI_STATUS EFIAPI mock_query_variable_info(UINT32 attrs, UINT64 *remaining_var_storage, UINT64 *max_var_size); +EFI_STATUS EFIAPI mock_install_configuration_table(EFI_GUID *guid, VOID *table); + struct mock_variable_limits { UINT32 attrs; UINT64 *max_var_storage; @@ -40,6 +42,7 @@ typedef enum { } mock_sort_policy_t; extern mock_sort_policy_t mock_variable_sort_policy; +extern mock_sort_policy_t mock_config_table_sort_policy; #define MOCK_VAR_DELETE_ATTR_ALLOW_ZERO 0x01 #define MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH 0x02 @@ -110,7 +113,8 @@ void mock_load_variables(const char *const dirname, const char *filters[], void mock_install_query_variable_info(void); void mock_uninstall_query_variable_info(void); void mock_reset_variables(void); -void mock_finalize_vars(void); +void mock_reset_config_table(void); +void mock_finalize_vars_and_configs(void); typedef enum { NONE = 0, @@ -171,5 +175,8 @@ typedef void (mock_query_variable_info_post_hook_t)( const int line, const char * const func); extern mock_query_variable_info_post_hook_t *mock_query_variable_info_post_hook; +#define MOCK_CONFIG_TABLE_ENTRIES 1024 +extern EFI_CONFIGURATION_TABLE mock_config_table[MOCK_CONFIG_TABLE_ENTRIES]; + #endif /* !SHIM_MOCK_VARIABLES_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/mock-variables.c b/mock-variables.c index 6a9a9dea..e9bce544 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -26,6 +26,7 @@ list_t *mock_sv_limits = &mock_default_variable_limits; list_t mock_variables = LIST_HEAD_INIT(mock_variables); mock_sort_policy_t mock_variable_sort_policy = MOCK_SORT_APPEND; +mock_sort_policy_t mock_config_table_sort_policy = MOCK_SORT_APPEND; UINT32 mock_variable_delete_attr_policy; @@ -1165,6 +1166,140 @@ mock_uninstall_query_variable_info(void) RT->QueryVariableInfo = mock_efi_unsupported; } +EFI_CONFIGURATION_TABLE mock_config_table[MOCK_CONFIG_TABLE_ENTRIES] = { + { + .VendorGuid = { 0, }, + .VendorTable = NULL + }, +}; + +int +mock_config_table_cmp(const void *p0, const void *p1) +{ + EFI_CONFIGURATION_TABLE *entry0, *entry1; + long cmp; + + if (!p0 || !p1) { + cmp = (int)((intptr_t)p0 - (intptr_t)p1); + } else { + entry0 = (EFI_CONFIGURATION_TABLE *)p0; + entry1 = (EFI_CONFIGURATION_TABLE *)p1; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("comparing %p to %p\n", p0, p1); +#endif + cmp = CompareGuid(&entry0->VendorGuid, &entry1->VendorGuid); + } + + if (mock_config_table_sort_policy == MOCK_SORT_DESCENDING) { + cmp = -cmp; + } + + return cmp; +} + +EFI_STATUS EFIAPI +mock_install_configuration_table(EFI_GUID *guid, VOID *table) +{ + bool found = false; + EFI_CONFIGURATION_TABLE *entry; + int idx = 0; + size_t sz; + + if (!guid) + return EFI_INVALID_PARAMETER; + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *entry = &ST->ConfigurationTable[i]; + + if (CompareGuid(guid, &entry->VendorGuid) == 0) { + found = true; + if (table) { + // replace it + entry->VendorTable = table; + } else { + // delete it + ST->NumberOfTableEntries -= 1; + sz = ST->NumberOfTableEntries - i; + sz *= sizeof(*entry); + memmove(&entry[0], &entry[1], sz); + } + return EFI_SUCCESS; + } + } + if (!found && table == NULL) + return EFI_NOT_FOUND; + if (ST->NumberOfTableEntries == MOCK_CONFIG_TABLE_ENTRIES - 1) { + /* + * If necessary, we could allocate another table and copy + * the data, but I'm lazy and we probably don't need to. + */ + return EFI_OUT_OF_RESOURCES; + } + + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + case MOCK_SORT_ASCENDING: + case MOCK_SORT_APPEND: + idx = ST->NumberOfTableEntries; + break; + case MOCK_SORT_PREPEND: + sz = ST->NumberOfTableEntries ? ST->NumberOfTableEntries : 0; + sz *= sizeof(ST->ConfigurationTable[0]); + memmove(&ST->ConfigurationTable[1], &ST->ConfigurationTable[0], sz); + idx = 0; + break; + default: + break; + } + + entry = &ST->ConfigurationTable[idx]; + memcpy(&entry->VendorGuid, guid, sizeof(EFI_GUID)); + entry->VendorTable = table; +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): installing entry %p={%p,%p} as entry %d\n", + __FILE__, __LINE__, __func__, + entry, &entry->VendorGuid, entry->VendorTable, idx); +#endif + ST->NumberOfTableEntries += 1; + +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s():ST->ConfigurationTable:%p\n" + "\t[%d]:%p\n" + "\t[%d]:%p\n", + __FILE__, __LINE__, __func__, ST->ConfigurationTable, + 0, &ST->ConfigurationTable[0], + 1, &ST->ConfigurationTable[1]); +#endif + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + case MOCK_SORT_ASCENDING: +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): entries before sorting:\n", __FILE__, __LINE__, __func__); + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + printf("\t[%d] = %p = {", i, &ST->ConfigurationTable[i]); + printf(".VendorGuid=" GUID_FMT, GUID_ARGS(ST->ConfigurationTable[i].VendorGuid)); + printf(".VendorTable=%p}\n", ST->ConfigurationTable[i].VendorTable); + } +#endif + qsort(&ST->ConfigurationTable[0], ST->NumberOfTableEntries, + sizeof(ST->ConfigurationTable[0]), + mock_config_table_cmp); + break; + default: + break; + } +#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) + printf("%s:%d:%s(): entries:\n", __FILE__, __LINE__, __func__); + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + printf("\t[%d] = %p = {", i, &ST->ConfigurationTable[i]); + printf(".VendorGuid=" GUID_FMT, GUID_ARGS(ST->ConfigurationTable[i].VendorGuid)); + printf(".VendorTable=%p}\n", ST->ConfigurationTable[i].VendorTable); + } +#endif + + return EFI_SUCCESS; +} + void CONSTRUCTOR mock_reset_variables(void) { @@ -1222,10 +1357,44 @@ mock_reset_variables(void) mock_uninstall_query_variable_info(); } +void CONSTRUCTOR +mock_reset_config_table(void) +{ + init_efi_system_table(); + + /* + * Note that BS->InstallConfigurationTable() is *not* defined as + * freeing these. If a test case installs non-malloc()ed tables, + * it needs to call BS->InstallConfigurationTable(guid, NULL) to + * clear them. + */ + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *entry = &ST->ConfigurationTable[i]; + + if (entry->VendorTable) + free(entry->VendorTable); + } + + SetMem(ST->ConfigurationTable, + ST->NumberOfTableEntries * sizeof(EFI_CONFIGURATION_TABLE), + 0); + + ST->NumberOfTableEntries = 0; + + if (ST->ConfigurationTable != mock_config_table) { + free(ST->ConfigurationTable); + ST->ConfigurationTable = mock_config_table; + SetMem(mock_config_table, sizeof(mock_config_table), 0); + } + + BS->InstallConfigurationTable = mock_install_configuration_table; +} + void DESTRUCTOR -mock_finalize_vars(void) +mock_finalize_vars_and_configs(void) { mock_reset_variables(); + mock_reset_config_table(); } // vim:fenc=utf-8:tw=75:noet diff --git a/test-mock-variables.c b/test-mock-variables.c index 5ea5a981..c7e42b05 100644 --- a/test-mock-variables.c +++ b/test-mock-variables.c @@ -8,6 +8,7 @@ #include "mock-variables.h" #include +#include #include #include #include @@ -16,6 +17,8 @@ #pragma GCC diagnostic ignored "-Wunused-label" +static const size_t guidstr_size = sizeof("8be4df61-93ca-11d2-aa0d-00e098032b8c"); + void mock_print_guidname(EFI_GUID *guid, CHAR16 *name); void mock_print_var_list(list_t *head); @@ -386,6 +389,421 @@ err: return ret; } +static void +dump_config_table_if_wrong(const char * const func, int line, ...) +{ + va_list alist, blist; + bool okay = true; + size_t n = 0, m = 0; + + va_start(alist, line); + va_copy(blist, alist); + + int idx = va_arg(alist, int); + EFI_GUID *guid = va_arg(alist, EFI_GUID *); + while (idx >= 0 && guid != NULL) { + EFI_CONFIGURATION_TABLE *entry; + if (idx < 0) + goto nexta; + + n += 1; + if (idx >= (int)ST->NumberOfTableEntries) { + okay = false; + goto nexta; + } + + entry = &ST->ConfigurationTable[idx]; + if (CompareGuid(guid, &entry->VendorGuid) != 0) + okay = false; + +nexta: + idx = va_arg(alist, int); + guid = va_arg(alist, EFI_GUID *); + } + va_end(alist); + + if (okay) + return; + + printf("%s:%d:%s(): %d entries:\n", __FILE__, line, func, ST->NumberOfTableEntries); + idx = va_arg(blist, int); + guid = va_arg(blist, EFI_GUID *); + while (idx >= 0 && guid != NULL) { + EFI_CONFIGURATION_TABLE *entry; + + if (idx >= (int)ST->NumberOfTableEntries) { + printf("\t[%d]: invalid index for " GUID_FMT "\n", + idx, GUID_ARGS(*guid)); + goto nexta; + } + + if (idx < 0) { + printf("\t[%d]: " GUID_FMT "\n", idx, GUID_ARGS(*guid)); + } else { + entry = &ST->ConfigurationTable[idx]; + printf("\t[%d]: %p ", idx, entry); + printf("{.VendorGuid:" GUID_FMT ",", GUID_ARGS(entry->VendorGuid)); + printf("&.VendorTable:%p}\n", entry->VendorTable); + if (CompareGuid(guid, &entry->VendorGuid) != 0) + printf("\t\t\t expected:" GUID_FMT "\n", GUID_ARGS(*guid)); + } +next: + idx = va_arg(blist, int); + guid = va_arg(blist, EFI_GUID *); + } + va_end(blist); + + if ((int)ST->NumberOfTableEntries - n == 0) + return; + + printf("%d extra table entries:\n", ST->NumberOfTableEntries - n); + for (m = n; m < ST->NumberOfTableEntries; m++) { + EFI_CONFIGURATION_TABLE *entry; + + entry = &ST->ConfigurationTable[m]; + + printf("\t[%d]: %p ", m, entry); + printf("{.VendorGuid:" GUID_FMT ",", GUID_ARGS(entry->VendorGuid)); + printf("&.VendorTable:%p}\n", entry->VendorTable); + } +} + +static int +test_install_config_table_0(void) +{ + int ret = -1; + EFI_STATUS status; + + /* + * These three guids are chosen on purpose: they start with "a", + * "b", and "c", respective to their variable names, so you can + * identify them when dumped. + */ + EFI_GUID aguid = SECURITY_PROTOCOL_GUID; + char astr[guidstr_size]; + void *astrp = &astr[0]; + int aidx = -1; + + EFI_GUID bguid = EFI_HTTP_BINDING_GUID; + char bstr[guidstr_size]; + void *bstrp = &bstr[0]; + int bidx = -1; + + EFI_GUID cguid = MOK_VARIABLE_STORE; + char cstr[guidstr_size]; + void *cstrp = &cstr[0]; + int cidx = -1; + + EFI_GUID lip = LOADED_IMAGE_PROTOCOL; + + EFI_GUID guids[3]; + + char tmpstr[guidstr_size]; + + sprintf(astrp, GUID_FMT, GUID_ARGS(aguid)); + sprintf(bstrp, GUID_FMT, GUID_ARGS(bguid)); + sprintf(cstrp, GUID_FMT, GUID_ARGS(cguid)); + + assert_equal_return(ST->NumberOfTableEntries, 0, -1, "%lu != %lu\n"); + + /* + * test installing one + */ + status = BS->InstallConfigurationTable(&bguid, bstrp); + assert_equal_return(status, EFI_SUCCESS, -1, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 1, err, "%lu != %lu\n"); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[0].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[0].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[0].VendorTable, + bstrp, err, "%p != %p\n"); + + /* + * test re-installing the same one + */ + status = BS->InstallConfigurationTable(&bguid, bstrp); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 1, err, "%lu != %lu\n"); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[0].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[0].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[0].VendorTable, + bstrp, err, "%p != %p\n"); + + /* + * Test installing a second one + */ + status = BS->InstallConfigurationTable(&aguid, astrp); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 2, err, "%lu != %lu\n"); + + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + aidx = 1; + bidx = 0; + break; + case MOCK_SORT_PREPEND: + aidx = 0; + bidx = 1; + break; + case MOCK_SORT_APPEND: + aidx = 1; + bidx = 0; + break; + case MOCK_SORT_ASCENDING: + aidx = 0; + bidx = 1; + break; + default: + break; + } + + dump_config_table_if_wrong(__func__, __LINE__, + aidx, &aguid, + bidx, &bguid, + cidx, &cguid, + -1, NULL); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[aidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[aidx].VendorGuid, &aguid), + err, "%d != 0 (%s != %s)\n", tmpstr, astr); + assert_equal_goto(ST->ConfigurationTable[aidx].VendorTable, astrp, + err, "%p != %p\n"); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[bidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[bidx].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[bidx].VendorTable, bstrp, + err, "%p != %p\n"); + + /* + * Test installing a third one + */ + status = BS->InstallConfigurationTable(&cguid, cstrp); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 3, err, "%lu != %lu\n"); + + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + aidx = 2; + bidx = 1; + cidx = 0; + break; + case MOCK_SORT_PREPEND: + aidx = 1; + bidx = 2; + cidx = 0; + break; + case MOCK_SORT_APPEND: + aidx = 1; + bidx = 0; + cidx = 2; + break; + case MOCK_SORT_ASCENDING: + aidx = 0; + bidx = 1; + cidx = 2; + break; + default: + break; + } + + dump_config_table_if_wrong(__func__, __LINE__, + aidx, &aguid, + bidx, &bguid, + cidx, &cguid, + -1, NULL); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[aidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[aidx].VendorGuid, &aguid), + err, "%d != 0 (%s != %s)\n", tmpstr, astr); + assert_equal_goto(ST->ConfigurationTable[aidx].VendorTable, astrp, + err, "%p != %p\n"); + memcpy(&guids[aidx], &aguid, sizeof(EFI_GUID)); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[bidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[bidx].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[bidx].VendorTable, bstrp, + err, "%p != %p\n"); + memcpy(&guids[bidx], &bguid, sizeof(EFI_GUID)); + + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[cidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[cidx].VendorGuid, &cguid), + err, "%d != 0 (%s != %s)\n", tmpstr, cstr); + assert_equal_goto(ST->ConfigurationTable[cidx].VendorTable, cstrp, + err, "%p != %p\n"); + memcpy(&guids[cidx], &cguid, sizeof(EFI_GUID)); + + /* + * Test removing NULL guid + */ + status = BS->InstallConfigurationTable(NULL, NULL); + assert_equal_goto(status, EFI_INVALID_PARAMETER, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 3, err, "%lu != %lu\n"); + + /* + * Test removing a guid that's not present + */ + status = BS->InstallConfigurationTable(&lip, NULL); + assert_equal_goto(status, EFI_NOT_FOUND, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 3, err, "%lu != %lu\n"); + + /* + * Test removing the middle one + */ + status = BS->InstallConfigurationTable(&guids[1], NULL); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 2, err, "%lu != %lu\n"); + + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + aidx = 1; + bidx = -1; + cidx = 0; + break; + case MOCK_SORT_PREPEND: + aidx = -1; + bidx = 1; + cidx = 0; + break; + case MOCK_SORT_APPEND: + aidx = -1; + bidx = 0; + cidx = 1; + break; + case MOCK_SORT_ASCENDING: + aidx = 0; + bidx = -1; + cidx = 1; + break; + default: + break; + } + + dump_config_table_if_wrong(__func__, __LINE__, + aidx, &aguid, + bidx, &bguid, + cidx, &cguid, + -1, NULL); + + if (aidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[aidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[aidx].VendorGuid, &aguid), + err, "%d != 0 (%s != %s)\n", tmpstr, astr); + assert_equal_goto(ST->ConfigurationTable[aidx].VendorTable, astrp, + err, "%p != %p\n"); + memcpy(&guids[aidx], &aguid, sizeof(EFI_GUID)); + } + + if (bidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[bidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[bidx].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[bidx].VendorTable, bstrp, + err, "%p != %p\n"); + memcpy(&guids[bidx], &bguid, sizeof(EFI_GUID)); + } + + if (cidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[cidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[cidx].VendorGuid, &cguid), + err, "%d != 0 (%s != %s)\n", tmpstr, cstr); + assert_equal_goto(ST->ConfigurationTable[cidx].VendorTable, cstrp, + err, "%p != %p\n"); + memcpy(&guids[cidx], &cguid, sizeof(EFI_GUID)); + } + + /* + * Test removing the lowest one + */ + status = BS->InstallConfigurationTable(&guids[0], NULL); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 1, err, "%lu != %lu\n"); + + switch (mock_config_table_sort_policy) { + case MOCK_SORT_DESCENDING: + aidx = 0; + bidx = -1; + cidx = -1; + break; + case MOCK_SORT_PREPEND: + aidx = -1; + bidx = 0; + cidx = -1; + break; + case MOCK_SORT_APPEND: + aidx = -1; + bidx = -1; + cidx = 0; + break; + case MOCK_SORT_ASCENDING: + aidx = -1; + bidx = -1; + cidx = 0; + break; + default: + break; + } + + dump_config_table_if_wrong(__func__, __LINE__, + aidx, &aguid, + bidx, &bguid, + cidx, &cguid, + -1, NULL); + + if (aidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[aidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[aidx].VendorGuid, &aguid), + err, "%d != 0 (%s != %s)\n", tmpstr, astr); + assert_equal_goto(ST->ConfigurationTable[aidx].VendorTable, astrp, + err, "%p != %p\n"); + memcpy(&guids[aidx], &aguid, sizeof(EFI_GUID)); + } + + if (bidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[bidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[bidx].VendorGuid, &bguid), + err, "%d != 0 (%s != %s)\n", tmpstr, bstr); + assert_equal_goto(ST->ConfigurationTable[bidx].VendorTable, bstrp, + err, "%p != %p\n"); + memcpy(&guids[bidx], &bguid, sizeof(EFI_GUID)); + } + + if (cidx >= 0) { + sprintf(tmpstr, GUID_FMT, GUID_ARGS(ST->ConfigurationTable[cidx].VendorGuid)); + assert_zero_goto(CompareGuid(&ST->ConfigurationTable[cidx].VendorGuid, &cguid), + err, "%d != 0 (%s != %s)\n", tmpstr, cstr); + assert_equal_goto(ST->ConfigurationTable[cidx].VendorTable, cstrp, + err, "%p != %p\n"); + memcpy(&guids[cidx], &cguid, sizeof(EFI_GUID)); + } + + /* + * Test removing the last one + */ + status = BS->InstallConfigurationTable(&guids[0], NULL); + assert_equal_goto(status, EFI_SUCCESS, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 0, err, "%lu != %lu\n"); + + /* + * Test removing it again + */ + status = BS->InstallConfigurationTable(&guids[0], NULL); + assert_equal_goto(status, EFI_NOT_FOUND, err, "%lx != %lx\n"); + assert_equal_goto(ST->NumberOfTableEntries, 0, err, "%lu != %lu\n"); + + ret = 0; +err: + while (ST->NumberOfTableEntries) + BS->InstallConfigurationTable(&ST->ConfigurationTable[0].VendorGuid, NULL); + mock_reset_config_table(); + + return ret; +} + int main(void) { @@ -405,12 +823,15 @@ main(void) for (int i = 0; i < MOCK_SORT_MAX_SENTINEL; i++) { mock_variable_sort_policy = i; + mock_config_table_sort_policy = i; printf("%s: setting variable sort policy to %s\n", program_invocation_short_name, policies[i]); test(test_gnvn_buf_size_0); test(test_gnvn_0); test(test_gnvn_1); + + test(test_install_config_table_0); } test(test_get_variable_0); -- cgit v1.2.3 From 397f820b8c091ca5e3023b6dbd2f26fb256a19f0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 23 Jul 2021 14:36:23 -0400 Subject: tests: Add a unit test for mok mirroring Test that our mok mirroring doesn't ever try to delete any variable that it has previously created, and that it properly mirrors at least MokList, MokListX, and SbatLevel, at least when variables actually work. These tests will fail (rather a lot) without 7f64fd6da9458b73c4. Currently valgrind shows a memory leak in this code which is not introduced in this patch series. Since all of our memory is freed on Exit() or when kernel does ExitBootServices(), this doesn't have any significant repercussions. Signed-off-by: Peter Jones --- include/mock-variables.h | 15 ++ include/test-data-efivars-1.h | 106 ++++++++++++ include/test.mk | 2 +- test-mok-mirror.c | 380 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 include/test-data-efivars-1.h create mode 100644 test-mok-mirror.c (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index 759fd1f0..3f282a68 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -124,6 +124,21 @@ typedef enum { REPLACE, } mock_variable_op_t; +static inline const char * +format_var_op(mock_variable_op_t op) +{ + static const char *var_op_names[] = { + "NONE", + "CREATE", + "DELETE", + "APPEND", + "REPLACE", + NULL + }; + + return var_op_names[op]; +} + typedef EFI_STATUS (mock_set_variable_pre_hook_t)(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, VOID *data); diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h new file mode 100644 index 00000000..55090ede --- /dev/null +++ b/include/test-data-efivars-1.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-data-efivars-1.h - test data + * Copyright Peter Jones + */ + +#ifndef TEST_DATA_EFIVARS_1_H_ +#define TEST_DATA_EFIVARS_1_H_ + +static const unsigned char test_data_efivars_1_MokListRT[] = { + 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, + 0x5c, 0x2b, 0xf0, 0x72, 0x98, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x03, 0x00, 0x00, 0x50, 0xab, 0x5d, 0x60, 0x46, 0xe0, 0x00, 0x43, + 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23, 0x30, 0x82, 0x03, 0x68, + 0x30, 0x82, 0x02, 0x50, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x00, + 0x99, 0x76, 0xf2, 0xf4, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x20, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x46, 0x65, 0x64, 0x6f, + 0x72, 0x61, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31, + 0x32, 0x30, 0x37, 0x31, 0x36, 0x32, 0x35, 0x35, 0x34, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x31, 0x32, 0x30, 0x35, 0x31, 0x36, 0x32, 0x35, 0x35, 0x34, + 0x5a, 0x30, 0x20, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x15, 0x46, 0x65, 0x64, 0x6f, 0x72, 0x61, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xae, 0xf5, 0xf7, 0x52, + 0x81, 0xa9, 0x5c, 0x3e, 0x2b, 0xf7, 0x1d, 0x55, 0xf4, 0x5a, 0x68, 0x84, + 0x2d, 0xbc, 0x8b, 0x76, 0x96, 0x85, 0x0d, 0x27, 0xb8, 0x18, 0xa5, 0xcd, + 0xc1, 0x83, 0xb2, 0x8c, 0x27, 0x5d, 0x23, 0x0a, 0xd1, 0x12, 0x0a, 0x75, + 0x98, 0xa2, 0xe6, 0x5d, 0x01, 0x8a, 0xf4, 0xd9, 0x9f, 0xfc, 0x70, 0xbc, + 0xc3, 0xc4, 0x17, 0x7b, 0x02, 0xb5, 0x13, 0xc4, 0x51, 0x92, 0xe0, 0xc0, + 0x05, 0x74, 0xb9, 0x2e, 0x3d, 0x24, 0x78, 0xa0, 0x79, 0x73, 0x94, 0xc0, + 0xc2, 0x2b, 0xb2, 0x82, 0xa7, 0xf4, 0xab, 0x67, 0x4a, 0x22, 0xf3, 0x64, + 0xcd, 0xc3, 0xf9, 0x0c, 0x26, 0x01, 0xbf, 0x1b, 0xd5, 0x3d, 0x39, 0xbf, + 0xc9, 0xfa, 0xfb, 0x5e, 0x52, 0xb9, 0xa4, 0x48, 0xfb, 0x13, 0xbf, 0x87, + 0x29, 0x0a, 0x64, 0xef, 0x21, 0x7b, 0xbc, 0x1e, 0x16, 0x7b, 0x88, 0x4f, + 0xf1, 0x40, 0x2b, 0xd9, 0x22, 0x15, 0x47, 0x4e, 0x84, 0xf6, 0x24, 0x1c, + 0x4d, 0x53, 0x16, 0x5a, 0xb1, 0x29, 0xbb, 0x5e, 0x7d, 0x7f, 0xc0, 0xd4, + 0xe2, 0xd5, 0x79, 0xaf, 0x59, 0x73, 0x02, 0xdc, 0xb7, 0x48, 0xbf, 0xae, + 0x2b, 0x70, 0xc1, 0xfa, 0x74, 0x7f, 0x79, 0xf5, 0xee, 0x23, 0xd0, 0x03, + 0x05, 0xb1, 0x79, 0x18, 0x4f, 0xfd, 0x4f, 0x2f, 0xe2, 0x63, 0x19, 0x4d, + 0x77, 0xba, 0xc1, 0x2c, 0x8b, 0xb3, 0xd9, 0x05, 0x2e, 0xd9, 0xd8, 0xb6, + 0x51, 0x13, 0xbf, 0xce, 0x36, 0x67, 0x97, 0xe4, 0xad, 0x58, 0x56, 0x07, + 0xab, 0xd0, 0x8c, 0x66, 0x12, 0x49, 0xdc, 0x91, 0x68, 0xb4, 0xc8, 0xea, + 0xdd, 0x9c, 0xc0, 0x81, 0xc6, 0x91, 0x5b, 0xdb, 0x12, 0x78, 0xdb, 0xff, + 0xc1, 0xaf, 0x08, 0x16, 0xfc, 0x70, 0x13, 0x97, 0x5b, 0x57, 0xad, 0x6b, + 0x44, 0x98, 0x7e, 0x1f, 0xec, 0xed, 0x46, 0x66, 0x95, 0x0f, 0x05, 0x55, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa8, 0x30, 0x81, 0xa5, 0x30, + 0x4e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x42, 0x30, 0x40, 0x30, 0x3e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x02, 0x86, 0x32, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x66, 0x65, 0x64, 0x6f, 0x72, 0x61, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x77, 0x69, 0x6b, 0x69, 0x2f, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2f, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x42, 0x6f, 0x6f, 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xe3, 0x25, 0x99, + 0xc2, 0xd6, 0x1d, 0xb1, 0xbf, 0x58, 0x07, 0x33, 0x5d, 0x7b, 0x20, 0xe4, + 0xcd, 0x96, 0x3b, 0x42, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x03, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xfd, 0xe3, 0x25, 0x99, 0xc2, 0xd6, 0x1d, 0xb1, 0xbf, 0x58, 0x07, 0x33, + 0x5d, 0x7b, 0x20, 0xe4, 0xcd, 0x96, 0x3b, 0x42, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x37, 0x77, 0xf0, 0x3a, 0x41, 0xa2, 0x1c, 0x9f, + 0x71, 0x3b, 0xd6, 0x9b, 0x95, 0xb5, 0x15, 0xdf, 0x4a, 0xb6, 0xf4, 0xd1, + 0x51, 0xba, 0x0d, 0x04, 0xda, 0x9c, 0xb2, 0x23, 0xf0, 0xf3, 0x34, 0x59, + 0x8d, 0xb8, 0xd4, 0x9a, 0x75, 0x74, 0x65, 0x80, 0x17, 0x61, 0x3a, 0xc1, + 0x96, 0x7f, 0xa7, 0xc1, 0x2b, 0xd3, 0x1a, 0xd6, 0x60, 0x3c, 0x71, 0x3a, + 0xa4, 0xc4, 0xe3, 0x39, 0x03, 0x02, 0x15, 0x12, 0x08, 0x1f, 0x4e, 0xcd, + 0x97, 0x50, 0xf8, 0xff, 0x50, 0xcc, 0xb6, 0x3e, 0x03, 0x7d, 0x7a, 0xe7, + 0x82, 0x7a, 0xc2, 0x67, 0xbe, 0xc9, 0x0e, 0x11, 0x0f, 0x16, 0x2e, 0x1e, + 0xa9, 0xf2, 0x6e, 0xfe, 0x04, 0xbd, 0xea, 0x9e, 0xf4, 0xa9, 0xb3, 0xd9, + 0xd4, 0x61, 0x57, 0x08, 0x87, 0xc4, 0x98, 0xd8, 0xa2, 0x99, 0x64, 0xde, + 0x15, 0x54, 0x8d, 0x57, 0x79, 0x14, 0x1f, 0xfa, 0x0d, 0x4d, 0x6b, 0xcd, + 0x98, 0x35, 0xf5, 0x0c, 0x06, 0xbd, 0xf3, 0x31, 0xd6, 0xfe, 0x05, 0x1f, + 0x60, 0x90, 0xb6, 0x1e, 0x10, 0xf7, 0x24, 0xe0, 0x3c, 0xf6, 0x33, 0x50, + 0xcd, 0x44, 0xc2, 0x71, 0x18, 0x51, 0xbd, 0x18, 0x31, 0x81, 0x1e, 0x32, + 0xe1, 0xe6, 0x9f, 0xf9, 0x9c, 0x02, 0x53, 0xb4, 0xe5, 0x6a, 0x41, 0xd6, + 0x65, 0xb4, 0x2e, 0xf1, 0xcf, 0xb3, 0xb8, 0x82, 0xb0, 0xa3, 0x96, 0xe2, + 0x24, 0xd8, 0x83, 0xae, 0x06, 0x5b, 0xb3, 0x24, 0x74, 0x4d, 0xd1, 0xa4, + 0x0a, 0x1d, 0x0a, 0x32, 0x1b, 0x75, 0xa2, 0x96, 0xd1, 0x0e, 0x3e, 0xe1, + 0x30, 0xc3, 0x18, 0xe8, 0xcb, 0x53, 0xc4, 0x0b, 0x00, 0xad, 0x7e, 0xad, + 0xc8, 0x49, 0x41, 0xef, 0x97, 0x69, 0xbd, 0x13, 0x5f, 0xef, 0xef, 0x3c, + 0xda, 0x60, 0x05, 0xd8, 0x92, 0xfc, 0xda, 0x6a, 0xea, 0x48, 0x3f, 0x0e, + 0x3e, 0x73, 0x77, 0xfd, 0xa6, 0x89, 0xe9, 0x3f +}; + +static const unsigned char test_data_efivars_1_MokListXRT[] = { + 0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50, 0x92, 0x40, 0xac, 0xa9, 0x41, + 0xf9, 0x36, 0x93, 0x43, 0x28, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x50, 0xab, 0x5d, 0x60, 0x46, + 0xe0, 0x00, 0x43, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char test_data_efivars_1_SbatLevelRT[] = { + 0x73, 0x62, 0x61, 0x74, 0x2c, 0x31, 0x2c, 0x32, 0x30, + 0x32, 0x31, 0x30, 0x33, 0x30, 0x32, 0x31, 0x38, 0x0a +}; + +#endif /* !TEST_DATA_EFIVARS_1_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/include/test.mk b/include/test.mk index d362253b..343053d3 100644 --- a/include/test.mk +++ b/include/test.mk @@ -82,7 +82,7 @@ test-load-options : CFLAGS+=-DHAVE_SHIM_LOCK_GUID test-mock-variables_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c mock-variables.c test-mock-variables: CFLAGS+=-DHAVE_SHIM_LOCK_GUID -test-mok-mirror_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c +test-mok-mirror_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c mock-variables.c test-mok-mirror: CFLAGS+=-DHAVE_START_IMAGE -DHAVE_SHIM_LOCK_GUID test-sbat_FILES = csv.c lib/variables.c lib/guid.c diff --git a/test-mok-mirror.c b/test-mok-mirror.c new file mode 100644 index 00000000..d7829843 --- /dev/null +++ b/test-mok-mirror.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-mok-mirror.c - try to test our mok mirroring code + * Copyright Peter Jones + */ + +#include "shim.h" +#include "mock-variables.h" +#include "test-data-efivars-1.h" + +#include + +#pragma GCC diagnostic ignored "-Wunused-parameter" + +EFI_STATUS +start_image(EFI_HANDLE image_handle UNUSED, CHAR16 *mm) +{ + printf("Attempted to launch %s\n", Str2str(mm)); + return EFI_SUCCESS; +} + +#define N_TEST_VAR_OPS 40 +struct test_var { + EFI_GUID guid; + CHAR16 *name; + UINT32 attrs; + UINTN n_ops; + bool must_be_present; + bool must_be_absent; + mock_variable_op_t ops[N_TEST_VAR_OPS]; + EFI_STATUS results[N_TEST_VAR_OPS]; +}; + +static struct test_var *test_vars; + +struct mock_mok_variable_config_entry { + CHAR8 name[256]; + UINT64 data_size; + const unsigned char *data; +}; + +static void +setvar_post(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, + UINTN size, VOID *data, EFI_STATUS *status, + mock_variable_op_t op, const char * const file, + const int line, const char * const func) +{ + if (!test_vars) + return; + + for (UINTN i = 0; test_vars[i].name != NULL; i++) { + struct test_var *tv = &test_vars[i]; + + if (CompareGuid(&tv->guid, guid) != 0 || + StrCmp(tv->name, name) != 0) + continue; + tv->ops[tv->n_ops] = op; + tv->results[tv->n_ops] = *status; + tv->n_ops += 1; + } +} + +static void +getvar_post(CHAR16 *name, EFI_GUID *guid, + UINT32 *attrs, UINTN *size, + VOID *data, EFI_STATUS *status, + const char * const file, const int line, const char * func) +{ + if (EFI_ERROR(*status) && + (*status != EFI_NOT_FOUND && + *status != EFI_BUFFER_TOO_SMALL)) { + printf("%s:%d:%s():Getting "GUID_FMT"-%s ", + file, line, func, + GUID_ARGS(*guid), Str2str(name)); + if (attrs) + printf("attrs:%s\n", format_var_attrs(*attrs)); + else + printf("attrs:NULL\n"); + printf("failed:%s\n", efi_strerror(*status)); + } +} + +static int +test_mok_mirror_0(void) +{ + const char *mok_rt_vars[n_mok_state_variables]; + EFI_STATUS status; + EFI_GUID guid = SHIM_LOCK_GUID; + EFI_GUID mok_config_guid = MOK_VARIABLE_STORE; + int ret = -1; + + struct test_var test_mok_mirror_0_vars[] = { + {.guid = SHIM_LOCK_GUID, + .name = L"MokList", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokListRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokListX", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokListXRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevel", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"SbatLevelRT", + .must_be_present = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokIgnoreDB", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokSBState", + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .ops = { NONE, }, + }, + {.guid = SHIM_LOCK_GUID, + .name = L"MokSBStateRT", + .must_be_absent = true, + .attrs = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + .ops = { NONE, }, + }, + {.guid = { 0, }, + .name = NULL, + } + }; + + struct mock_mok_variable_config_entry test_mok_config_table[] = { + {.name = "MokListRT", + .data_size = sizeof(test_data_efivars_1_MokListRT), + .data = test_data_efivars_1_MokListRT + }, + {.name = "MokListXRT", + .data_size = sizeof(test_data_efivars_1_MokListXRT), + .data = test_data_efivars_1_MokListXRT + }, + {.name = "SbatLevelRT", + .data_size = sizeof(test_data_efivars_1_SbatLevelRT), + .data = test_data_efivars_1_SbatLevelRT + }, + {.name = { 0, }, + .data_size = 0, + .data = NULL, + } + }; + + for (size_t i = 0; i < n_mok_state_variables; i++) { + mok_rt_vars[i] = mok_state_variables[i].rtname8; + } + + mock_load_variables("test-data/efivars-1", mok_rt_vars, true); + + mock_set_variable_post_hook = setvar_post; + mock_get_variable_post_hook = getvar_post; + test_vars = &test_mok_mirror_0_vars[0]; + + import_mok_state(NULL); + + for (size_t i = 0; test_mok_mirror_0_vars[i].name != NULL; i++) { + struct test_var *tv = &test_mok_mirror_0_vars[i]; + list_t *pos = NULL; + bool found = false; + char buf[1]; + UINTN size = 0; + UINT32 attrs = 0; + bool present = false; + + list_for_each(pos, &mock_variables) { + struct mock_variable *var; + bool deleted; + bool created; + + var = list_entry(pos, struct mock_variable, list); + if (CompareGuid(&tv->guid, &var->guid) != 0 || + StrCmp(var->name, tv->name) != 0) + continue; + found = true; + assert_equal_goto(var->attrs, tv->attrs, err, + "\"%s\": wrong attrs; got %s expected %s\n", + Str2str(tv->name), + format_var_attrs(var->attrs), + format_var_attrs(tv->attrs)); + for (UINTN j = 0; j < N_TEST_VAR_OPS + && tv->ops[j] != NONE; j++) { + switch (tv->ops[j]) { + case NONE: + default: + break; + case CREATE: + if (tv->results[j] == EFI_SUCCESS) + created = true; + break; + case DELETE: + assert_goto(tv->results[j] != EFI_SUCCESS, err, + "Tried to delete absent variable \"%s\"\n", + Str2str(tv->name)); + assert_goto(created == false, err, + "Deleted variable \"%s\" was previously created.\n", + Str2str(tv->name)); + break; + case APPEND: + assert_goto(false, err, + "No append action should have been tested\n"); + break; + case REPLACE: + assert_goto(false, err, + "No replace action should have been tested\n"); + break; + } + } + } + if (tv->must_be_present) { + assert_goto(found == true, err, + "variable \"%s\" was not found.\n", + Str2str(tv->name)); + } + + if (tv->must_be_absent) { + assert_goto(found == false, err, + "variable \"%s\" was found.\n", + Str2str(tv->name)); + } + } + + uint8_t *pos = NULL; + for (size_t i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + + if (CompareGuid(&ct->VendorGuid, &mok_config_guid) != 0) + continue; + + pos = (void *)ct->VendorTable; + break; + } + + assert_nonzero_goto(pos, err, "%p != 0\n"); + + size_t i = 0; + while (pos) { + struct mock_mok_variable_config_entry *mock_entry = + &test_mok_config_table[i]; + struct mok_variable_config_entry *mok_entry = + (struct mok_variable_config_entry *)pos; + + /* + * If the tables are different lengths, this will trigger. + */ + assert_equal_goto(mok_entry->name[0], mock_entry->name[0], err, + "mok.name[0] %ld != test.name[0] %ld\n"); + if (mock_entry->name[0] == 0) + break; + + assert_nonzero_goto(mok_entry->name[0], err, "%ld != %ld\n"); + assert_zero_goto(strncmp(mok_entry->name, mock_entry->name, + sizeof(mock_entry->name)), + err, "%ld != %ld: strcmp(\"%s\",\"%s\")\n", + mok_entry->name, mock_entry->name); + + /* + * As of 7501b6bb449f ("mok: fix potential buffer overrun in + * import_mok_state"), we should not see any mok config + * variables with data_size == 0. + */ + assert_nonzero_goto(mok_entry->data_size, err, "%ld != 0\n"); + + assert_equal_goto(mok_entry->data_size, mock_entry->data_size, + err, "%ld != %ld\n"); + assert_zero_goto(CompareMem(mok_entry->data, mock_entry->data, + mok_entry->data_size), + err, "%ld != %ld\n"); + pos += offsetof(struct mok_variable_config_entry, data) + + mok_entry->data_size; + i += 1; + } + + ret = 0; +err: + for (UINTN k = 0; k < n_mok_state_variables; k++) { + struct mok_state_variable *v = + &mok_state_variables[k]; + if (v->data_size && v->data) { + free(v->data); + v->data = NULL; + v->data_size = 0; + } + } + + test_vars = NULL; + mock_set_variable_post_hook = NULL; + mock_get_variable_post_hook = NULL; + return ret; +} + +int +main(void) +{ + int status = 0; + setbuf(stdout, NULL); + + const char *sort_policy_names[] = { + "MOCK_SORT_DESCENDING", + "MOCK_SORT_PREPEND", + "MOCK_SORT_APPEND", + "MOCK_SORT_ASCENDING", + "MOCK_SORT_MAX_SENTINEL" + }; + + const char *del_policy_names[] = { + "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO", + "MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH", + "MOCK_VAR_DELETE_ATTR_ALLOW_ZERO|MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH", + "MOCK_VAR_DELETE_ATTR_ALLOW_NONE", + NULL + }; + + int delete_policies[] = { + MOCK_VAR_DELETE_ATTR_ALLOW_ZERO, + MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH, + MOCK_VAR_DELETE_ATTR_ALLOW_ZERO + | MOCK_VAR_DELETE_ATTR_ALOW_MISMATCH, + 0 + }; + + for (int i = 0; i < MOCK_SORT_MAX_SENTINEL; i++) { + mock_variable_sort_policy = i; + mock_config_table_sort_policy = i; + int j = 0; + + printf("%s: setting variable sort policy to %s\n", + program_invocation_short_name, sort_policy_names[i]); + do { + printf("%s: setting delete policy to %s\n", + program_invocation_short_name, + del_policy_names[j]); + + mock_variable_delete_attr_policy = delete_policies[j]; + test(test_mok_mirror_0); + mock_finalize_vars_and_configs(); + + if (delete_policies[j] == 0) + break; + } while (++j); + } + + return status; +} + +// vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 35dc110c75b53ff2c8caf808abec624534f5ad20 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 4 Aug 2021 17:47:54 -0400 Subject: mok: Fix memory leak in mok mirroring Currently valgrind shows a minor issue which is not introduced in this patch series: ==2595397== ==2595397== HEAP SUMMARY: ==2595397== in use at exit: 16,368 bytes in 48 blocks ==2595397== total heap usage: 6,953 allocs, 6,905 frees, 9,146,749 bytes allocated ==2595397== ==2595397== 16,368 bytes in 48 blocks are definitely lost in loss record 1 of 1 ==2595397== at 0x4845464: calloc (vg_replace_malloc.c:1117) ==2595397== by 0x4087F2: mock_efi_allocate_pool (test.c:72) ==2595397== by 0x4098DE: UnknownInlinedFun (misc.c:33) ==2595397== by 0x4098DE: AllocateZeroPool (misc.c:48) ==2595397== by 0x403D40: get_variable_attr (variables.c:301) ==2595397== by 0x4071C4: import_one_mok_state (mok.c:831) ==2595397== by 0x4072F4: import_mok_state (mok.c:908) ==2595397== by 0x407FA6: test_mok_mirror_0 (test-mok-mirror.c:205) ==2595397== by 0x4035B2: main (test-mok-mirror.c:378) ==2595397== ==2595397== LEAK SUMMARY: ==2595397== definitely lost: 16,368 bytes in 48 blocks ==2595397== indirectly lost: 0 bytes in 0 blocks ==2595397== possibly lost: 0 bytes in 0 blocks ==2595397== still reachable: 0 bytes in 0 blocks ==2595397== suppressed: 0 bytes in 0 blocks ==2595397== This is because we're doing get_variable_attr() on the same variable more than once and saving the value to our variables table. Each additional time we do so leaks the previous one. This patch solves the issue by not getting the variable again if it's already set in the table, and adds a test case to check if we're doing get_variable() of any variety on the same variable more than once. Signed-off-by: Peter Jones --- include/mock-variables.h | 2 ++ mok.c | 48 +++++++++++++++++++++++++----------------------- test-mok-mirror.c | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index 3f282a68..9f276e63 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -122,6 +122,7 @@ typedef enum { DELETE, APPEND, REPLACE, + GET, } mock_variable_op_t; static inline const char * @@ -133,6 +134,7 @@ format_var_op(mock_variable_op_t op) "DELETE", "APPEND", "REPLACE", + "GET", NULL }; diff --git a/mok.c b/mok.c index 801379ee..7755eea9 100644 --- a/mok.c +++ b/mok.c @@ -828,30 +828,32 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, dprint(L"importing mok state for \"%s\"\n", v->name); - efi_status = get_variable_attr(v->name, - &v->data, &v->data_size, - *v->guid, &attrs); - if (efi_status == EFI_NOT_FOUND) { - v->data = NULL; - v->data_size = 0; - } else if (EFI_ERROR(efi_status)) { - perror(L"Could not verify %s: %r\n", v->name, - efi_status); - delete = TRUE; - } else { - if (!(attrs & v->yes_attr)) { - perror(L"Variable %s is missing attributes:\n", - v->name); - perror(L" 0x%08x should have 0x%08x set.\n", - attrs, v->yes_attr); - delete = TRUE; - } - if (attrs & v->no_attr) { - perror(L"Variable %s has incorrect attribute:\n", - v->name); - perror(L" 0x%08x should not have 0x%08x set.\n", - attrs, v->no_attr); + if (!v->data && !v->data_size) { + efi_status = get_variable_attr(v->name, + &v->data, &v->data_size, + *v->guid, &attrs); + if (efi_status == EFI_NOT_FOUND) { + v->data = NULL; + v->data_size = 0; + } else if (EFI_ERROR(efi_status)) { + perror(L"Could not verify %s: %r\n", v->name, + efi_status); delete = TRUE; + } else { + if (!(attrs & v->yes_attr)) { + perror(L"Variable %s is missing attributes:\n", + v->name); + perror(L" 0x%08x should have 0x%08x set.\n", + attrs, v->yes_attr); + delete = TRUE; + } + if (attrs & v->no_attr) { + perror(L"Variable %s has incorrect attribute:\n", + v->name); + perror(L" 0x%08x should not have 0x%08x set.\n", + attrs, v->no_attr); + delete = TRUE; + } } } if (delete == TRUE) { diff --git a/test-mok-mirror.c b/test-mok-mirror.c index d7829843..3479ddf8 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -78,6 +78,20 @@ getvar_post(CHAR16 *name, EFI_GUID *guid, printf("attrs:NULL\n"); printf("failed:%s\n", efi_strerror(*status)); } + + if (!test_vars) + return; + + for (UINTN i = 0; test_vars[i].name != NULL; i++) { + struct test_var *tv = &test_vars[i]; + + if (CompareGuid(&tv->guid, guid) != 0 || + StrCmp(tv->name, name) != 0) + continue; + tv->ops[tv->n_ops] = GET; + tv->results[tv->n_ops] = *status; + tv->n_ops += 1; + } } static int @@ -201,6 +215,7 @@ test_mok_mirror_0(void) struct mock_variable *var; bool deleted; bool created; + int gets = 0; var = list_entry(pos, struct mock_variable, list); if (CompareGuid(&tv->guid, &var->guid) != 0 || @@ -238,8 +253,14 @@ test_mok_mirror_0(void) assert_goto(false, err, "No replace action should have been tested\n"); break; + case GET: + if (tv->results[j] == EFI_SUCCESS) + gets += 1; + break; } } + assert_goto(gets == 0 || gets == 1, err, + "Variable should not be read %d times.\n", gets); } if (tv->must_be_present) { assert_goto(found == true, err, -- cgit v1.2.3 From c1a84dc0a695782e55a97173e4f9f8a98432f9e1 Mon Sep 17 00:00:00 2001 From: Jonas Witschel Date: Thu, 9 Sep 2021 17:53:45 +0200 Subject: tests: add "include-fixed" GCC directory to include directories On Arch Linux, the GCC version of "limits.h" is in the "include-fixed" instead of the "include" directory. It needs to be included in the include directories list, otherwise attempting to compile the test suite fails with the following error: In file included from /usr/include/efivar/efivar-dp.h:22, from /usr/include/efivar/efivar.h:238, from include/test.h:51, from shim.h:68, from csv.c:6: /usr/include/limits.h:124:16: fatal error: limits.h: No such file or directory 124 | # include_next | ^~~~~~~~~~ compilation terminated. --- include/test.mk | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 343053d3..1a4fc220 100644 --- a/include/test.mk +++ b/include/test.mk @@ -46,6 +46,10 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ -DSHIM_UNIT_TEST \ "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" +# On some systems (e.g. Arch Linux), limits.h is in the "include-fixed" instead +# of the "include" directory +CFLAGS += -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include-fixed) + export CFLAGS_LTO CFLAGS_GCOV libefi-test.a : -- cgit v1.2.3 From 26998367eb6153cd24b6e82949d5f7874a036372 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 3 Sep 2021 22:38:40 +0200 Subject: pe: simplify generate_hash() Copying the value of datasize_in to two further variables and then using all three randomly in the code makes it hard to read. datasize_in is never changed in generate_hash() so we can do with this parameter alone. Rename it to datasize. Signed-off-by: Heinrich Schuchardt --- include/pe.h | 2 +- pe.c | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/pe.h b/include/pe.h index 79bf440c..43727f5e 100644 --- a/include/pe.h +++ b/include/pe.h @@ -25,7 +25,7 @@ handle_image (void *data, unsigned int datasize, UINTN *alloc_pages); EFI_STATUS -generate_hash (char *data, unsigned int datasize_in, +generate_hash (char *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash, UINT8 *sha1hash); diff --git a/pe.c b/pe.c index 5db45086..d72d814d 100644 --- a/pe.c +++ b/pe.c @@ -298,26 +298,22 @@ get_section_vma_by_name (char *name, size_t namesz, */ EFI_STATUS -generate_hash(char *data, unsigned int datasize_in, +generate_hash(char *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash, UINT8 *sha1hash) { unsigned int sha256ctxsize, sha1ctxsize; - unsigned int size = datasize_in; void *sha256ctx = NULL, *sha1ctx = NULL; char *hashbase; unsigned int hashsize; unsigned int SumOfBytesHashed, SumOfSectionBytes; unsigned int index, pos; - unsigned int datasize; EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; EFI_STATUS efi_status = EFI_SUCCESS; EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data; unsigned int PEHdr_offset = 0; - size = datasize = datasize_in; - if (datasize <= sizeof (*DosHdr) || DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { perror(L"Invalid signature\n"); @@ -346,7 +342,7 @@ generate_hash(char *data, unsigned int datasize_in, hashbase = data; hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - hashbase; - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { @@ -359,7 +355,7 @@ generate_hash(char *data, unsigned int datasize_in, hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum + sizeof (int); hashsize = (char *)context->SecDir - hashbase; - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { @@ -372,12 +368,12 @@ generate_hash(char *data, unsigned int datasize_in, EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1; hashbase = (char *)dd; hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data); - if (hashsize > datasize_in) { + if (hashsize > datasize) { perror(L"Data Directory size %d is invalid\n", hashsize); efi_status = EFI_INVALID_PARAMETER; goto done; } - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { @@ -491,7 +487,8 @@ generate_hash(char *data, unsigned int datasize_in, continue; } - hashbase = ImageAddress(data, size, Section->PointerToRawData); + hashbase = ImageAddress(data, datasize, + Section->PointerToRawData); if (!hashbase) { perror(L"Malformed section header\n"); efi_status = EFI_INVALID_PARAMETER; @@ -506,7 +503,7 @@ generate_hash(char *data, unsigned int datasize_in, goto done; } hashsize = (unsigned int) Section->SizeOfRawData; - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { @@ -532,7 +529,7 @@ generate_hash(char *data, unsigned int datasize_in, efi_status = EFI_INVALID_PARAMETER; goto done; } - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { @@ -552,7 +549,7 @@ generate_hash(char *data, unsigned int datasize_in, hashbase = data + SumOfBytesHashed; hashsize = datasize - SumOfBytesHashed; - check_size(data, datasize_in, hashbase, hashsize); + check_size(data, datasize, hashbase, hashsize); if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || !(Sha1Update(sha1ctx, hashbase, hashsize))) { -- cgit v1.2.3 From 35ca373d20fbeeb80aff2202077d614bc89575c0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 16 Sep 2021 16:46:55 -0400 Subject: console: add a clear_screen() primitive Several places in e.g. MokManager and our console library use ST->ConOut->ClearScreen directly, without checking for the existence of a console output device. This patch adds function to our console library to do that correctly, instead of using the bug-prone ad hoc implementation everywhere. Signed-off-by: Peter Jones --- MokManager.c | 10 +++++----- include/console.h | 3 +++ lib/console.c | 13 ++++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/MokManager.c b/MokManager.c index 4b6ee146..08b15d6d 100644 --- a/MokManager.c +++ b/MokManager.c @@ -1005,7 +1005,7 @@ static EFI_STATUS mok_reset_prompt(BOOLEAN MokX) EFI_STATUS efi_status; CHAR16 *prompt[] = { NULL, NULL }; - ST->ConOut->ClearScreen(ST->ConOut); + clear_screen(); if (MokX) prompt[0] = L"Erase all stored keys in MokListX?"; @@ -1468,7 +1468,7 @@ static EFI_STATUS mok_sb_prompt(void *MokSB, UINTN MokSBSize) return EFI_INVALID_PARAMETER; } - ST->ConOut->ClearScreen(ST->ConOut); + clear_screen(); message[0] = L"Change Secure Boot state"; message[1] = NULL; @@ -1583,7 +1583,7 @@ static EFI_STATUS mok_db_prompt(void *MokDB, UINTN MokDBSize) return EFI_INVALID_PARAMETER; } - ST->ConOut->ClearScreen(ST->ConOut); + clear_screen(); message[0] = L"Change DB state"; message[1] = NULL; @@ -1691,7 +1691,7 @@ static EFI_STATUS mok_pw_prompt(void *MokPW, UINTN MokPWSize) return EFI_INVALID_PARAMETER; } - ST->ConOut->ClearScreen(ST->ConOut); + clear_screen(); SetMem(hash, PASSWORD_CRYPT_SIZE, 0); @@ -2008,7 +2008,7 @@ static BOOLEAN verify_pw(BOOLEAN * protected) if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) return TRUE; - ST->ConOut->ClearScreen(ST->ConOut); + clear_screen(); /* Draw the background */ console_save_and_set_mode(&SavedMode); diff --git a/include/console.h b/include/console.h index c832b20e..0c4a5137 100644 --- a/include/console.h +++ b/include/console.h @@ -50,6 +50,9 @@ void console_reset(void); void console_mode_handle(void); +void +clear_screen(void); + #define NOSEL 0x7fffffff typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL EFI_CONSOLE_CONTROL_PROTOCOL; diff --git a/lib/console.c b/lib/console.c index 6b1e4c2f..7be5d543 100644 --- a/lib/console.c +++ b/lib/console.c @@ -580,7 +580,7 @@ console_mode_handle(VOID) efi_status = co->SetMode(co, mode_set); } - co->ClearScreen(co); + clear_screen(); if (EFI_ERROR(efi_status)) { console_error(L"Console set mode fail", efi_status); @@ -683,6 +683,17 @@ console_reset(void) co->ClearScreen(co); } +void +clear_screen(void) +{ + SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; + + if (!co) + return; + + co->ClearScreen(co); +} + VOID setup_verbosity(VOID) { -- cgit v1.2.3 From 2e78cd93390f83648a20b5b3bb934f846537e54c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 10 Dec 2021 16:18:41 -0500 Subject: stdarg: use sysv varargs when we build with coverity cov-analysis-linux64-2020.09 is a lot more successful than the older versions at building, but it still has some... issues. Among them, it is of the belief that this: void foo(char *fmt, ...) { __builtin_va_list ap; __builtin_ms_va_start(ap, fmt); /* <- here */ ... } is an uninitialized use of "ap". This patch adds defined(__COVERITY__) to the list of criteria for using sysv va lists, which it has no such confusion about. Signed-off-by: Peter Jones --- include/system/stdarg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/system/stdarg.h b/include/system/stdarg.h index 4c956f70..68c171b8 100644 --- a/include/system/stdarg.h +++ b/include/system/stdarg.h @@ -24,7 +24,7 @@ typedef __builtin_va_list __builtin_sysv_va_list; #endif #if defined(__aarch64__) || defined(__arm__) || defined(__i386__) || \ - defined(__i486__) || defined(__i686__) + defined(__i486__) || defined(__i686__) || defined(__COVERITY__) typedef __builtin_va_list ms_va_list; typedef __builtin_va_list __builtin_ms_va_list; -- cgit v1.2.3 From d0df9304c7a777557e1925dc9f75406ec00e6179 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 9 Dec 2021 17:21:45 -0500 Subject: Minor coverity fixes - one missing free - one minor deadcode issue - two unchecked allocations - one debug hexdump of a variable we just freed Signed-off-by: Peter Jones --- MokManager.c | 1 + fallback.c | 7 +++++-- include/hexdump.h | 7 ++++++- lib/variables.c | 2 ++ mok.c | 4 +++- 5 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/MokManager.c b/MokManager.c index c195cadc..1359af83 100644 --- a/MokManager.c +++ b/MokManager.c @@ -339,6 +339,7 @@ static void show_x509_info(X509 * X509Cert, UINT8 * hash) for (i = 0; i < n; i++) { CatPrint(&serial_string, L"%02x:", hexbuf[i]); } + BN_free(bnser); } if (serial_string.str) diff --git a/fallback.c b/fallback.c index 5da867dd..8e6327be 100644 --- a/fallback.c +++ b/fallback.c @@ -230,8 +230,11 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, StrLen(label)*2 + 2 + DevicePathSize(hddp) + StrLen(arguments) * 2; - CHAR8 *data = AllocateZeroPool(size + 2); - CHAR8 *cursor = data; + CHAR8 *data, *cursor; + cursor = data = AllocateZeroPool(size + 2); + if (!data) + return EFI_OUT_OF_RESOURCES; + *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; cursor += sizeof (UINT32); *(UINT16 *)cursor = DevicePathSize(hddp); diff --git a/include/hexdump.h b/include/hexdump.h index 1a20339b..e8f4fe1a 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -71,7 +71,7 @@ prepare_text(const void *data, size_t size, char *buf, unsigned int position) else buf[offset++] = '.'; } - buf[offset++] = size > 0 ? '|' : 'X'; + buf[offset++] = '|'; buf[offset] = '\0'; } @@ -89,6 +89,11 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, if (verbose == 0) return; + if (!data || !size) { + dprint(L"hexdump of a NULL pointer!\n"); + return; + } + while (offset < size) { char hexbuf[49]; char txtbuf[19]; diff --git a/lib/variables.c b/lib/variables.c index 3ec05478..8e63aa8f 100644 --- a/lib/variables.c +++ b/lib/variables.c @@ -68,6 +68,8 @@ fill_esl_with_one_signature(const uint8_t *data, const uint32_t data_len, if (out) { sd = AllocateZeroPool(sig_size); + if (!sd) + return EFI_OUT_OF_RESOURCES; if (owner) CopyMem(sd, (void *)owner, sizeof(EFI_GUID)); CopyMem(sd->SignatureData, (void *)data, data_len); diff --git a/mok.c b/mok.c index 52dffc3e..930e52a2 100644 --- a/mok.c +++ b/mok.c @@ -883,7 +883,9 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, } dprint(L"maybe mirroring \"%s\". original data:\n", v->name); - dhexdumpat(v->data, v->data_size, 0); + if (v->data && v->data_size) { + dhexdumpat(v->data, v->data_size, 0); + } ret = maybe_mirror_one_mok_variable(v, ret, only_first); dprint(L"returning %r\n", ret); -- cgit v1.2.3 From a2da05fcb8972628bec08e4adfc13abbafc319ad Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 28 Feb 2022 21:29:16 +0000 Subject: shim: implement SBAT verification for the shim_lock protocol This implements SBAT verification via the shim_lock protocol by moving verification inside the existing verify_buffer() function that is shared by both shim_verify() and handle_image(). The .sbat section is optional for code verified via the shim_lock protocol, unlike for code that is verified and executed directly by shim. For executables that don't have a .sbat section, verification is skipped when using the protocol. A vendor can enforce SBAT verification for code verified via the shim_lock protocol by revoking all pre-SBAT binaries via a dbx update or by using vendor_dbx and then only signing binaries that have a .sbat section from that point. Signed-off-by: Chris Coulson --- include/pe.h | 2 +- pe.c | 46 ++++++++------------------------------ shim.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 79 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/pe.h b/include/pe.h index 43727f5e..b86e1b3a 100644 --- a/include/pe.h +++ b/include/pe.h @@ -15,7 +15,7 @@ read_header(void *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context); EFI_STATUS -handle_sbat(char *SBATBase, size_t SBATSize); +verify_sbat_section(char *SBATBase, size_t SBATSize); EFI_STATUS handle_image (void *data, unsigned int datasize, diff --git a/pe.c b/pe.c index 92c2804b..554e77cf 100644 --- a/pe.c +++ b/pe.c @@ -820,7 +820,7 @@ read_header(void *data, unsigned int datasize, } EFI_STATUS -handle_sbat(char *SBATBase, size_t SBATSize) +verify_sbat_section(char *SBATBase, size_t SBATSize) { unsigned int i; EFI_STATUS efi_status; @@ -834,7 +834,12 @@ handle_sbat(char *SBATBase, size_t SBATSize) if (SBATBase == NULL || SBATSize == 0) { dprint(L"No .sbat section data\n"); - return EFI_SECURITY_VIOLATION; + /* + * SBAT is mandatory for binaries loaded by shim, but optional + * for binaries loaded outside of shim but verified via the + * protocol. + */ + return in_protocol ? EFI_SUCCESS : EFI_SECURITY_VIOLATION; } sbat_size = SBATSize + 1; @@ -980,9 +985,6 @@ handle_image (void *data, unsigned int datasize, EFI_IMAGE_SECTION_HEADER *RelocSection = NULL; - char *SBATBase = NULL; - size_t SBATSize = 0; - /* * Copy the executable's sections to their desired offsets */ @@ -1027,33 +1029,6 @@ handle_image (void *data, unsigned int datasize, RelocBaseEnd == end) { RelocSection = Section; } - } else if (CompareMem(Section->Name, ".sbat\0\0\0", 8) == 0) { - if (SBATBase || SBATSize) { - perror(L"Image has multiple SBAT sections\n"); - return EFI_UNSUPPORTED; - } - - if (Section->NumberOfRelocations != 0 || - Section->PointerToRelocations != 0) { - perror(L"SBAT section has relocations\n"); - return EFI_UNSUPPORTED; - } - - /* The virtual size corresponds to the size of the SBAT - * metadata and isn't necessarily a multiple of the file - * alignment. The on-disk size is a multiple of the file - * alignment and is zero padded. Make sure that the - * on-disk size is at least as large as virtual size, - * and ignore the section if it isn't. */ - if (Section->SizeOfRawData && - Section->SizeOfRawData >= Section->Misc.VirtualSize && - base && end) { - SBATBase = base; - /* +1 because of size vs last byte location */ - SBATSize = end - base + 1; - dprint(L"sbat section base:0x%lx size:0x%lx\n", - SBATBase, SBATSize); - } } if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) { @@ -1095,11 +1070,8 @@ handle_image (void *data, unsigned int datasize, } if (secure_mode ()) { - efi_status = handle_sbat(SBATBase, SBATSize); - - if (!EFI_ERROR(efi_status)) - efi_status = verify_buffer(data, datasize, - &context, sha256hash, sha1hash); + efi_status = verify_buffer(data, datasize, &context, sha256hash, + sha1hash); if (EFI_ERROR(efi_status)) { if (verbose) diff --git a/shim.c b/shim.c index 604c0db1..6d6b1e52 100644 --- a/shim.c +++ b/shim.c @@ -559,9 +559,9 @@ verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, * Check that the signature is valid and matches the binary */ EFI_STATUS -verify_buffer (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) +verify_buffer_authenticode (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) { EFI_STATUS ret_efi_status; size_t size = datasize; @@ -695,6 +695,71 @@ verify_buffer (char *data, int datasize, return ret_efi_status; } +/* + * Check that the binary is permitted to load by SBAT. + */ +EFI_STATUS +verify_buffer_sbat (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + int i; + EFI_IMAGE_SECTION_HEADER *Section; + char *SBATBase = NULL; + size_t SBATSize = 0; + + Section = context->FirstSection; + for (i = 0; i < context->NumberOfSections; i++, Section++) { + if (CompareMem(Section->Name, ".sbat\0\0\0", 8) != 0) + continue; + + if (SBATBase || SBATSize) { + perror(L"Image has multiple SBAT sections\n"); + return EFI_UNSUPPORTED; + } + + if (Section->NumberOfRelocations != 0 || + Section->PointerToRelocations != 0) { + perror(L"SBAT section has relocations\n"); + return EFI_UNSUPPORTED; + } + + /* The virtual size corresponds to the size of the SBAT + * metadata and isn't necessarily a multiple of the file + * alignment. The on-disk size is a multiple of the file + * alignment and is zero padded. Make sure that the + * on-disk size is at least as large as virtual size, + * and ignore the section if it isn't. */ + if (Section->SizeOfRawData && + Section->SizeOfRawData >= Section->Misc.VirtualSize) { + SBATBase = ImageAddress(data, datasize, + Section->PointerToRawData); + SBATSize = Section->SizeOfRawData; + dprint(L"sbat section base:0x%lx size:0x%lx\n", + SBATBase, SBATSize); + } + } + + return verify_sbat_section(SBATBase, SBATSize); +} + +/* + * Check that the signature is valid and matches the binary and that + * the binary is permitted to load by SBAT. + */ +EFI_STATUS +verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) +{ + EFI_STATUS efi_status; + + efi_status = verify_buffer_sbat(data, datasize, context); + if (EFI_ERROR(efi_status)) + return efi_status; + + return verify_buffer_authenticode(data, datasize, context, sha256hash, sha1hash); +} + static int is_removable_media_path(EFI_LOADED_IMAGE *li) { @@ -1542,7 +1607,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) goto die; } - efi_status = handle_sbat(sbat_start, sbat_end - sbat_start - 1); + efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1); if (EFI_ERROR(efi_status)) { perror(L"Verifiying shim SBAT data failed: %r\n", efi_status); -- cgit v1.2.3 From 31094e5a08cbad78054c2da53d55b50e8742bcd4 Mon Sep 17 00:00:00 2001 From: Steve McIntyre Date: Thu, 28 Apr 2022 12:32:18 +0100 Subject: tests: also look for system headers in multi-arch directories On Debian(-derived) systems low-level system headers are under /usr/include/, so look there too. Otherwise we see stuff like: gcc -O2 -fno-diagnostics-color -ggdb -std=gnu11 -isystem /shim.git/include/system -I/shim.git/gnu-efi/inc -I/shim.git/gnu-efi/inc/ia32 -I/shim.git/gnu-efi/inc/protocol -Iinclude -iquote . -isystem /usr/include -isystem /usr/lib/gcc/i686-linux-gnu/11/include -mno-mmx -mno-sse -mno-red-zone -nostdinc -maccumulate-outgoing-args -m32 -DMDE_CPU_IA32 -DPAGE_SIZE=4096 -fshort-wchar -fno-builtin -rdynamic -fno-inline -fno-eliminate-unused-debug-types -fno-eliminate-unused-debug-symbols -gpubnames -grecord-gcc-switches -Wall -Wextra -Wno-missing-field-initializers -Wsign-compare -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-unused-variable -Wno-pointer-sign -Werror -Werror=nonnull -Werror=nonnull-compare -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI -DPAGE_SIZE=4096 -DSHIM_UNIT_TEST -DDEFAULT_DEBUG_PRINT_STATE=0 -isystem include-fixed -o test-csv csv.c test-csv.c test.c libefi-test.a -lefivar In file included from /usr/include/bits/errno.h:26, from /usr/include/errno.h:28, from /usr/include/efivar/efivar.h:24, from include/test.h:51, from shim.h:68, from csv.c:6: /usr/include/linux/errno.h:1:10: fatal error: asm/errno.h: No such file or directory 1 | #include | ^~~~~~~~~~~~~ compilation terminated. In file included from /usr/include/bits/errno.h:26, from /usr/include/errno.h:28, from /usr/include/efivar/efivar.h:24, from include/test.h:51, from shim.h:68, from test-csv.c:9: /usr/include/linux/errno.h:1:10: fatal error: asm/errno.h: No such file or directory 1 | #include | ^~~~~~~~~~~~~ compilation terminated. In file included from /usr/include/bits/errno.h:26, from /usr/include/errno.h:28, from /usr/include/efivar/efivar.h:24, from include/test.h:51, from shim.h:68, from test.c:7: /usr/include/linux/errno.h:1:10: fatal error: asm/errno.h: No such file or directory 1 | #include | ^~~~~~~~~~~~~ compilation terminated. Signed-off-by: Steve McIntyre --- include/test.mk | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index 1a4fc220..e965c600 100644 --- a/include/test.mk +++ b/include/test.mk @@ -50,6 +50,9 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ # of the "include" directory CFLAGS += -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include-fixed) +# And on Debian also check the multi-arch include path +CFLAGS += -isystem /usr/include/$(shell $(CC) $(ARCH_CFLAGS) -print-multiarch) + export CFLAGS_LTO CFLAGS_GCOV libefi-test.a : -- cgit v1.2.3 From 2670c6a17edb239949152c471445fc533d8525aa Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 17 Feb 2022 18:29:58 -0500 Subject: Allow MokListTrusted to be enabled by default Within previous versions of shim the MokListTrusted var did not exist. The user had to opt in to using the feature. Change the default behavior to an opt out model. Since old shims will not have the BS MokListTrusted set, use inverse logic that sets the MokListTrustedRT to 1 when the boot service variable is missing. Many Linux distros carry out of tree patches to trust the mok keys by default. These out of tree patches can be dropped when using a Linux kernel that supports MokListTrustedRT. Signed-off-by: Eric Snowberg --- MokManager.c | 10 ++++------ include/test-data-efivars-1.h | 4 ++++ mok.c | 15 ++++++++++++++- test-mok-mirror.c | 4 ++++ 4 files changed, 26 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/MokManager.c b/MokManager.c index 1359af83..ffcd6a6e 100644 --- a/MokManager.c +++ b/MokManager.c @@ -1776,27 +1776,25 @@ static EFI_STATUS mok_tml_prompt(void *MokTML, UINTN MokTMLSize) LibDeleteVariable(L"MokListTrustedNew", &SHIM_LOCK_GUID); return EFI_ABORTED; } - if (var->MokTMLState == 0) { efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - 1, &dbval); + 0, NULL); if (EFI_ERROR(efi_status)) { - console_notify(L"Failed to set MokListTrusted state"); + console_notify(L"Failed to delete MokListTrusted state"); return efi_status; } } else { efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - 0, NULL); + 1, &dbval); if (EFI_ERROR(efi_status)) { - console_notify(L"Failed to delete MokListTrusted state"); + console_notify(L"Failed to set MokListTrusted state"); return efi_status; } } - return EFI_SUCCESS; } diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index 55090ede..2831bd23 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -102,5 +102,9 @@ static const unsigned char test_data_efivars_1_SbatLevelRT[] = { 0x32, 0x31, 0x30, 0x33, 0x30, 0x32, 0x31, 0x38, 0x0a }; +static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={ + 0x01 +}; + #endif /* !TEST_DATA_EFIVARS_1_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/mok.c b/mok.c index 28962853..f4de8081 100644 --- a/mok.c +++ b/mok.c @@ -84,6 +84,7 @@ categorize_deauthorized(struct mok_state_variable *v) #define MOK_MIRROR_DELETE_FIRST 0x02 #define MOK_VARIABLE_MEASURE 0x04 #define MOK_VARIABLE_LOG 0x08 +#define MOK_VARIABLE_INVERSE 0x10 struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", @@ -176,6 +177,7 @@ struct mok_state_variable mok_state_variable_data[] = { .no_attr = EFI_VARIABLE_RUNTIME_ACCESS, .flags = MOK_MIRROR_DELETE_FIRST | MOK_VARIABLE_MEASURE | + MOK_VARIABLE_INVERSE | MOK_VARIABLE_LOG, .pcr = 14, .state = &trust_mok_list, @@ -846,7 +848,13 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, efi_status = get_variable_attr(v->name, &v->data, &v->data_size, *v->guid, &attrs); - if (efi_status == EFI_NOT_FOUND) { + if (efi_status == EFI_NOT_FOUND && + v->flags & MOK_VARIABLE_INVERSE) { + v->data = AllocateZeroPool(4); + v->data[0] = 0x01; + v->data_size = 1; + efi_status = EFI_SUCCESS; + } else if (efi_status == EFI_NOT_FOUND) { v->data = NULL; v->data_size = 0; } else if (EFI_ERROR(efi_status)) { @@ -868,6 +876,11 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, attrs, v->no_attr); delete = TRUE; } + if (v->flags & MOK_VARIABLE_INVERSE) { + FreePool(v->data); + v->data = NULL; + v->data_size = 0; + } } } if (delete == TRUE) { diff --git a/test-mok-mirror.c b/test-mok-mirror.c index 3479ddf8..f34f62a9 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -184,6 +184,10 @@ test_mok_mirror_0(void) .data_size = sizeof(test_data_efivars_1_SbatLevelRT), .data = test_data_efivars_1_SbatLevelRT }, + {.name = "MokListTrustedRT", + .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), + .data = test_data_efivars_1_MokListTrustedRT + }, {.name = { 0, }, .data_size = 0, .data = NULL, -- cgit v1.2.3 From bb4b60e800823c1e48220935e3b9180c8c82e1a7 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 27 Jan 2022 17:33:01 -0500 Subject: Add verify_image In the future we will want to examine binaries without wanting to execute them. Create verify_image based off existing handle_image code. Signed-off-by: Eric Snowberg --- include/pe.h | 4 ++++ pe.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'include') diff --git a/include/pe.h b/include/pe.h index b86e1b3a..ccc8798b 100644 --- a/include/pe.h +++ b/include/pe.h @@ -14,6 +14,10 @@ EFI_STATUS read_header(void *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context); +EFI_STATUS verify_image(void *data, unsigned int datasize, + EFI_LOADED_IMAGE *li, + PE_COFF_LOADER_IMAGE_CONTEXT *context); + EFI_STATUS verify_sbat_section(char *SBATBase, size_t SBATSize); diff --git a/pe.c b/pe.c index 554e77cf..535d463a 100644 --- a/pe.c +++ b/pe.c @@ -878,6 +878,60 @@ err: return efi_status; } +EFI_STATUS verify_image(void *data, unsigned int datasize, + EFI_LOADED_IMAGE *li, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + EFI_STATUS efi_status; + UINT8 sha1hash[SHA1_DIGEST_SIZE]; + UINT8 sha256hash[SHA256_DIGEST_SIZE]; + + /* + * The binary header contains relevant context and section pointers + */ + efi_status = read_header(data, datasize, context); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to read header: %r\n", efi_status); + return efi_status; + } + + /* + * We only need to verify the binary if we're in secure mode + */ + efi_status = generate_hash(data, datasize, context, sha256hash, + sha1hash); + if (EFI_ERROR(efi_status)) + return efi_status; + + /* Measure the binary into the TPM */ +#ifdef REQUIRE_TPM + efi_status = +#endif + tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, + (EFI_PHYSICAL_ADDRESS)(UINTN)context->ImageAddress, + li->FilePath, sha1hash, 4); +#ifdef REQUIRE_TPM + if (efi_status != EFI_SUCCESS) { + return efi_status; + } +#endif + + if (secure_mode()) { + efi_status = verify_buffer(data, datasize, + context, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + if (verbose) + console_print(L"Verification failed: %r\n", efi_status); + else + console_error(L"Verification failed", efi_status); + return efi_status; + } else if (verbose) + console_print(L"Verification succeeded\n"); + } + + return EFI_SUCCESS; +} + /* * Once the image has been loaded it needs to be validated and relocated */ -- cgit v1.2.3 From 35d7378d29b9ad6f664df20efc4121e210859e65 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Tue, 1 Feb 2022 15:49:51 -0500 Subject: Load additional certs from a signed binary Heavily inspired by Matthew Garrett's patch "Allow additional certificates to be loaded from a signed binary". Add support for loading a binary, verifying its signature, and then scanning it for embedded certificates. This is intended to make it possible to decouple shim builds from vendor signatures. In order to add new signatures to shim, an EFI Signature List should be generated and then added to the .db section of a well-formed EFI binary. This binary should then be signed with a key that shim already trusts (either a built-in key, one present in the platform firmware or one present in MOK) and placed in the same directory as shim with a filename starting "shim_certificate" (eg, "shim_certificate_oracle"). Shim will read multiple files and incorporate the signatures from all of them. Note that each section *must* be an EFI Signature List, not a raw certificate. Signed-off-by: Eric Snowberg --- globals.c | 3 ++ include/mok.h | 3 ++ mok.c | 9 ++++- shim.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shim.h | 3 ++ 5 files changed, 144 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/globals.c b/globals.c index 4a1f432f..30d10630 100644 --- a/globals.c +++ b/globals.c @@ -12,6 +12,9 @@ UINT8 *vendor_authorized = NULL; UINT32 vendor_deauthorized_size = 0; UINT8 *vendor_deauthorized = NULL; +UINT32 user_cert_size; +UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) UINT32 build_cert_size; UINT8 *build_cert; diff --git a/include/mok.h b/include/mok.h index 96da397a..6f99a105 100644 --- a/include/mok.h +++ b/include/mok.h @@ -59,6 +59,9 @@ struct mok_state_variable { UINT8 **addend; UINT32 *addend_size; + UINT8 **user_cert; + UINT32 *user_cert_size; + /* * build_cert is our build-time cert. Like addend, this is added * to the input variable, as part of the runtime variable, so that diff --git a/mok.c b/mok.c index f4de8081..94101843 100644 --- a/mok.c +++ b/mok.c @@ -98,6 +98,8 @@ struct mok_state_variable mok_state_variable_data[] = { .categorize_addend = categorize_authorized, .addend = &vendor_authorized, .addend_size = &vendor_authorized_size, + .user_cert = &user_cert, + .user_cert_size = &user_cert_size, #if defined(ENABLE_SHIM_CERT) .build_cert = &build_cert, .build_cert_size = &build_cert_size, @@ -588,7 +590,8 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:0x%lx FullData:0x%llx\n", FullDataSize, FullData); } - + if (v->user_cert_size) + FullDataSize += *v->user_cert_size; } /* @@ -702,6 +705,10 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); } + if (v->user_cert_size) { + CopyMem(p, *v->user_cert, *v->user_cert_size); + p += *v->user_cert_size; + } } /* diff --git a/shim.c b/shim.c index 51e72e7e..c4f9461a 100644 --- a/shim.c +++ b/shim.c @@ -1391,6 +1391,126 @@ uninstall_shim_protocols(void) #endif } +EFI_STATUS +load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; + PE_COFF_LOADER_IMAGE_CONTEXT context; + EFI_IMAGE_SECTION_HEADER *Section; + EFI_SIGNATURE_LIST *certlist; + CHAR16 *PathName = NULL; + void *pointer; + UINT32 original; + int datasize; + void *data; + int i; + + efi_status = read_image(image_handle, filename, PathName, + &data, &datasize); + + if (EFI_ERROR(efi_status)) + return efi_status; + + efi_status = verify_image(data, datasize, li, &context); + if (EFI_ERROR(efi_status)) + return efi_status; + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + if (CompareMem(Section->Name, ".db\0\0\0\0\0", 8) == 0) { + original = user_cert_size; + if (Section->SizeOfRawData < sizeof(EFI_SIGNATURE_LIST)) { + continue; + } + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + certlist = pointer; + user_cert_size += certlist->SignatureListSize;; + user_cert = ReallocatePool(user_cert, original, + user_cert_size); + memcpy(user_cert + original, pointer, + certlist->SignatureListSize); + } + } + FreePool(data); + return EFI_SUCCESS; +} + +/* Read additional certificates from files (after verifying signatures) */ +EFI_STATUS +load_certs(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; + CHAR16 *PathName = NULL; + EFI_FILE *root, *dir; + EFI_FILE_INFO *info; + EFI_HANDLE device; + EFI_FILE_IO_INTERFACE *drive; + UINTN buffersize = 0; + void *buffer = NULL; + + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to init protocol\n"); + return efi_status; + } + + efi_status = generate_path_from_image_path(li, L"", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **)&drive); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to find fs: %r\n", efi_status); + goto done; + } + + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open fs: %r\n", efi_status); + goto done; + } + + efi_status = root->Open(root, &dir, PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + while (1) { + int old = buffersize; + efi_status = dir->Read(dir, &buffersize, buffer); + if (efi_status == EFI_BUFFER_TOO_SMALL) { + buffer = ReallocatePool(buffer, old, buffersize); + continue; + } else if (EFI_ERROR(efi_status)) { + perror(L"Failed to read directory %s - %r\n", PathName, + efi_status); + goto done; + } + + if (buffersize == 0) + goto done; + + info = (EFI_FILE_INFO *)buffer; + if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + load_cert_file(image_handle, info->FileName); + } + } +done: + FreePool(buffer); + FreePool(PathName); + return efi_status; +} + EFI_STATUS shim_init(void) { @@ -1626,6 +1746,13 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) init_openssl(); + if (secure_mode()) { + efi_status = load_certs(global_image_handle); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to load addon certificates\n"); + } + } + /* * Before we do anything else, validate our non-volatile, * boot-services-only state variables are what we think they are. diff --git a/shim.h b/shim.h index db264cb7..703c1145 100644 --- a/shim.h +++ b/shim.h @@ -248,6 +248,9 @@ extern UINT8 *vendor_authorized; extern UINT32 vendor_deauthorized_size; extern UINT8 *vendor_deauthorized; +extern UINT32 user_cert_size; +extern UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) extern UINT32 build_cert_size; extern UINT8 *build_cert; -- cgit v1.2.3 From 465663e5f6b350abdb18f0ab51ec8924e739bc78 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 2 Dec 2021 17:43:29 -0500 Subject: Add some missing PE image flag definitions This patch adds some missing definitions for PE header flags. We don't use all of them, but it's less confusing with the list matching the spec, except where the spec is obviously wrong. Signed-off-by: Peter Jones --- include/peimage.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/peimage.h b/include/peimage.h index 3b3f01a7..4bcb940d 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -236,6 +236,24 @@ typedef struct { EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; } EFI_IMAGE_OPTIONAL_HEADER64; +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0001 0x0001 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0002 0x0002 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0004 0x0004 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0008 0x0008 +#if 0 /* This is not in the PE spec. */ +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0010 0x0010 +#endif +#define EFI_IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 +#define EFI_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define EFI_IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define EFI_IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 +#define EFI_IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define EFI_IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 +#define EFI_IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 /// /// @attention @@ -303,16 +321,31 @@ typedef struct { // // Section Flags Values // -#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 ///< Reserved. +#define EFI_IMAGE_SCN_RESERVED_00000000 0x00000000 +#define EFI_IMAGE_SCN_RESERVED_00000001 0x00000001 +#define EFI_IMAGE_SCN_RESERVED_00000002 0x00000002 +#define EFI_IMAGE_SCN_RESERVED_00000004 0x00000004 +#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 +#define EFI_IMAGE_SCN_RESERVED_00000010 0x00000010 #define EFI_IMAGE_SCN_CNT_CODE 0x00000020 #define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 #define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 - -#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 ///< Reserved. -#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 ///< Section contains comments or some other type of information. -#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 ///< Section contents will not become part of image. +#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 +#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 +#define EFI_IMAGE_SCN_RESERVED_00000400 0x00000400 +#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 #define EFI_IMAGE_SCN_LNK_COMDAT 0x00001000 - +#define EFI_IMAGE_SCN_RESERVED_00002000 0x00002000 +#define EFI_IMAGE_SCN_RESERVED_00004000 0x00004000 +#define EFI_IMAGE_SCN_GPREL 0x00008000 +/* + * PE 9.3 says both IMAGE_SCN_MEM_PURGEABLE and IMAGE_SCN_MEM_16BIT are + * 0x00020000, but I think it's wrong. --pjones + */ +#define EFI_IMAGE_SCN_MEM_PURGEABLE 0x00010000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_16BIT 0x00020000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_LOCKED 0x00040000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_PRELOAD 0x00080000 // "Reserved for future use." #define EFI_IMAGE_SCN_ALIGN_1BYTES 0x00100000 #define EFI_IMAGE_SCN_ALIGN_2BYTES 0x00200000 #define EFI_IMAGE_SCN_ALIGN_4BYTES 0x00300000 @@ -320,7 +353,14 @@ typedef struct { #define EFI_IMAGE_SCN_ALIGN_16BYTES 0x00500000 #define EFI_IMAGE_SCN_ALIGN_32BYTES 0x00600000 #define EFI_IMAGE_SCN_ALIGN_64BYTES 0x00700000 - +#define EFI_IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define EFI_IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define EFI_IMAGE_SCN_ALIGN_512BYTES 0x00a00000 +#define EFI_IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 +#define EFI_IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 +#define EFI_IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 +#define EFI_IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 +#define EFI_IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 #define EFI_IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define EFI_IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define EFI_IMAGE_SCN_MEM_NOT_PAGED 0x08000000 -- cgit v1.2.3 From 226fee25ffcbd29988399ba080c7706eb1d52251 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 2 Dec 2021 18:29:50 -0500 Subject: PE Loader: support and require NX This adds support in our PE loader for NX support utilizing the EFI_MEMORY_ATTRIBUTE protocol. Specifically, it changes the loader such that: - binaries without the EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag set in the Optional Header are rejected as EFI_UNSUPPORTED - binaries with non-discardable sections that have both the EFI_SCN_MEM_WRITE and EFI_SCN_MEM_EXECUTE flags set are rejected as EFI_UNSUPPORTED - if the EFI_MEMORY_ATTRIBUTE protocol is installed, then: - sections without the EFI_SCN_MEM_READ flag set will be marked with EFI_MEMORY_RP - sections without the EFI_SCN_MEM_WRITE flag set will be marked with EFI_MEMORY_RO - sections without the EFI_SCN_MEM_EXECUTE flag set will be marked with EFI_MEMORY_XP Signed-off-by: Peter Jones --- include/guid.h | 2 +- lib/guid.c | 2 +- pe.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shim.h | 4 ++ 4 files changed, 209 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/guid.h b/include/guid.h index 07a19a91..d9910ff1 100644 --- a/include/guid.h +++ b/include/guid.h @@ -33,8 +33,8 @@ extern EFI_GUID EFI_SECURE_BOOT_DB_GUID; extern EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID; extern EFI_GUID SECURITY_PROTOCOL_GUID; extern EFI_GUID SECURITY2_PROTOCOL_GUID; +extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; extern EFI_GUID SHIM_LOCK_GUID; - extern EFI_GUID MOK_VARIABLE_STORE; #endif /* SHIM_GUID_H */ diff --git a/lib/guid.c b/lib/guid.c index 143e0bbd..e100c92e 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -32,6 +32,6 @@ EFI_GUID EFI_SECURE_BOOT_DB_GUID = { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } }; EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; - +EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} }; EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} }; diff --git a/pe.c b/pe.c index 535d463a..9fa6fffd 100644 --- a/pe.c +++ b/pe.c @@ -696,6 +696,7 @@ read_header(void *data, unsigned int datasize, EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; unsigned long FileAlignment = 0; + UINT16 DllFlags; if (datasize < sizeof (PEHdr->Pe32)) { perror(L"Invalid image\n"); @@ -790,13 +791,20 @@ read_header(void *data, unsigned int datasize, context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; } else { context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; } + if (!(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + perror(L"Image does not support NX\n"); + return EFI_UNSUPPORTED; + } + context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); if (context->ImageSize < context->SizeOfHeaders) { @@ -878,6 +886,139 @@ err: return efi_status; } +static inline uint64_t +shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) +{ + uint64_t ret = EFI_MEMORY_RP | + EFI_MEMORY_RO | + EFI_MEMORY_XP; + + if (attrs & MEM_ATTR_R) + ret &= ~EFI_MEMORY_RP; + + if (attrs & MEM_ATTR_W) + ret &= ~EFI_MEMORY_RO; + + if (attrs & MEM_ATTR_X) + ret &= ~EFI_MEMORY_XP; + + return ret; +} + +static inline uint64_t +uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) +{ + uint64_t ret = MEM_ATTR_R | + MEM_ATTR_W | + MEM_ATTR_X; + + if (attrs & EFI_MEMORY_RP) + ret &= ~MEM_ATTR_R; + + if (attrs & EFI_MEMORY_RO) + ret &= ~MEM_ATTR_W; + + if (attrs & EFI_MEMORY_XP) + ret &= ~MEM_ATTR_X; + + return ret; +} + +static EFI_STATUS +get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) + return efi_status; + + if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); + *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); + + return efi_status; +} + +static EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status, ret; + uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) + return efi_status; + + efi_status = get_mem_attrs (addr, size, &before); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &before, efi_status); + + if (physaddr & 0xfff || size & 0xfff || size == 0) { + dprint(L"%a called on 0x%llx-0x%llx (size 0x%llx) +%a%a%a -%a%a%a\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + return 0; + } + + uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + efi_status = EFI_SUCCESS; + if (uefi_set_attrs) + efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); + if (!EFI_ERROR(efi_status) && uefi_clear_attrs) + efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); + ret = efi_status; + + efi_status = get_mem_attrs (addr, size, &after); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &after, efi_status); + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); + + return ret; +} + EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, PE_COFF_LOADER_IMAGE_CONTEXT *context) @@ -1013,6 +1154,11 @@ handle_image (void *data, unsigned int datasize, } buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment); + dprint(L"Loading 0x%llx bytes at 0x%llx\n", + (unsigned long long)context.ImageSize, + (unsigned long long)(uintptr_t)buffer); + update_mem_attrs((uintptr_t)buffer, alloc_size, MEM_ATTR_R|MEM_ATTR_W, + MEM_ATTR_X); CopyMem(buffer, data, context.SizeOfHeaders); @@ -1049,6 +1195,19 @@ handle_image (void *data, unsigned int datasize, !Section->Misc.VirtualSize) continue; + /* + * Skip sections that aren't marked readable. + */ + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ)) + continue; + + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE)) { + perror(L"Section %d is writable and executable\n", i); + return EFI_UNSUPPORTED; + } + base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress); end = ImageAddress (buffer, context.ImageSize, @@ -1159,6 +1318,50 @@ handle_image (void *data, unsigned int datasize, } } + /* + * Now set the page permissions appropriately. + */ + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + uint64_t set_attrs = MEM_ATTR_R; + uint64_t clear_attrs = MEM_ATTR_W|MEM_ATTR_X; + uintptr_t addr; + uint64_t length; + + /* + * Skip discardable sections with zero size + */ + if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && + !Section->Misc.VirtualSize) + continue; + + /* + * Skip sections that aren't marked readable. + */ + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ)) + continue; + + base = ImageAddress (buffer, context.ImageSize, + Section->VirtualAddress); + end = ImageAddress (buffer, context.ImageSize, + Section->VirtualAddress + + Section->Misc.VirtualSize - 1); + + addr = (uintptr_t)base; + length = (uintptr_t)end - (uintptr_t)base + 1; + + if (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) { + set_attrs |= MEM_ATTR_W; + clear_attrs &= ~MEM_ATTR_W; + } + if (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) { + set_attrs |= MEM_ATTR_X; + clear_attrs &= ~MEM_ATTR_X; + } + update_mem_attrs(addr, length, set_attrs, clear_attrs); + } + + /* * grub needs to know its location and size in memory, so fix up * the loaded image protocol values diff --git a/shim.h b/shim.h index 703c1145..dc3cda73 100644 --- a/shim.h +++ b/shim.h @@ -195,6 +195,10 @@ #include "Cryptlib/Include/OpenSslSupport.h" #endif +#define MEM_ATTR_R 4 +#define MEM_ATTR_W 2 +#define MEM_ATTR_X 1 + INTERFACE_DECL(_SHIM_LOCK); typedef -- cgit v1.2.3 From df96f48f28fa94b62d06f39a3b014133dd38def5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 31 Mar 2022 16:19:53 -0400 Subject: Add MokPolicy variable and MOK_POLICY_REQUIRE_NX This adds a new MoK variable, MokPolicy (&MokPolicyRT) that's intended as a bitmask of machine owner policy choices, and the bit MOK_POLICY_REQUIRE_NX. This bit specifies whether it is permissible to load binaries which do not support NX mitigations, and it currently defaults to allowing such binaries to be loaded. The broader intention here is to migrate all of the MoK policy variables that are really just on/off flags to this variable. Signed-off-by: Peter Jones --- globals.c | 1 + include/mok.h | 5 +++++ mok.c | 13 +++++++++++++ pe.c | 8 +++++--- shim.h | 2 ++ 5 files changed, 26 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/globals.c b/globals.c index 30d10630..b4e80dd3 100644 --- a/globals.c +++ b/globals.c @@ -29,6 +29,7 @@ int loader_is_participating; UINT8 user_insecure_mode; UINT8 ignore_db; UINT8 trust_mok_list; +UINT8 mok_policy = 0; UINT32 verbose = 0; diff --git a/include/mok.h b/include/mok.h index 6f99a105..fb19423b 100644 --- a/include/mok.h +++ b/include/mok.h @@ -100,5 +100,10 @@ struct mok_variable_config_entry { UINT8 data[]; }; +/* + * bit definitions for MokPolicy + */ +#define MOK_POLICY_REQUIRE_NX 1 + #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/mok.c b/mok.c index 94101843..a8c8be6b 100644 --- a/mok.c +++ b/mok.c @@ -184,6 +184,19 @@ struct mok_state_variable mok_state_variable_data[] = { .pcr = 14, .state = &trust_mok_list, }, + {.name = L"MokPolicy", + .name8 = "MokPolicy", + .rtname = L"MokPolicyRT", + .rtname8 = "MokPolicyRT", + .guid = &SHIM_LOCK_GUID, + .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .no_attr = EFI_VARIABLE_RUNTIME_ACCESS, + .flags = MOK_MIRROR_DELETE_FIRST | + MOK_VARIABLE_LOG, + .pcr = 14, + .state = &mok_policy, + }, { NULL, } }; size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); diff --git a/pe.c b/pe.c index 9fa6fffd..5d0c6b0b 100644 --- a/pe.c +++ b/pe.c @@ -800,8 +800,9 @@ read_header(void *data, unsigned int datasize, DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; } - if (!(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { - perror(L"Image does not support NX\n"); + if ((mok_policy & MOK_POLICY_REQUIRE_NX) && + !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + perror(L"Policy requires NX, but image does not support NX\n"); return EFI_UNSUPPORTED; } @@ -1203,7 +1204,8 @@ handle_image (void *data, unsigned int datasize, if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) && - (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE)) { + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) && + (mok_policy & MOK_POLICY_REQUIRE_NX)) { perror(L"Section %d is writable and executable\n", i); return EFI_UNSUPPORTED; } diff --git a/shim.h b/shim.h index dc3cda73..b5272b9c 100644 --- a/shim.h +++ b/shim.h @@ -263,6 +263,8 @@ extern UINT8 *build_cert; extern UINT8 user_insecure_mode; extern UINT8 ignore_db; extern UINT8 trust_mok_list; +extern UINT8 mok_policy; + extern UINT8 in_protocol; extern void *load_options; extern UINT32 load_options_size; -- cgit v1.2.3 From f81a7cc34e0b1a4f2d3104f44df80f93497eaa9e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 22 Apr 2022 13:13:20 -0700 Subject: SBAT revocation management Support for updating SBAT revocations to latest or previous revocations. Allow SBAT revocations to be reset to empty metadata only when UEFI Secure Boot is disabled. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 33 +++++++++++++++++++++++++++++--- sbat.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++--------- test-sbat.c | 52 +++++++++++++++++++++++++++++++++++++++++++------- test.c | 6 ++++++ 4 files changed, 132 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 8551b74a..f2ae93a5 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -8,8 +8,31 @@ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," -#define SBAT_VAR_DATE "2021030218" -#define SBAT_VAR SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_DATE "\n" +#define SBAT_VAR_ORIGINAL_DATE "2021030218" +#define SBAT_VAR_ORIGINAL SBAT_VAR_SIG SBAT_VAR_VERSION \ + SBAT_VAR_ORIGINAL_DATE "\n" + +#if defined(ENABLE_SHIM_DEVEL) +#define SBAT_VAR_PREVIOUS_DATE "2022020101" +#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" +#define SBAT_VAR_PREVIOUS SBAT_VAR_SIG SBAT_VAR_VERSION \ + SBAT_VAR_PREVIOUS_DATE "\n" SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE "2022050100" +#define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" +#define SBAT_VAR_LATEST SBAT_VAR_SIG SBAT_VAR_VERSION \ + SBAT_VAR_LATEST_DATE "\n" SBAT_VAR_LATEST_REVOCATIONS +#else /* !ENABLE_SHIM_DEVEL */ +#define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE +#define SBAT_VAR_PREVIOUS_REVOCATIONS +#define SBAT_VAR_PREVIOUS SBAT_VAR_SIG SBAT_VAR_VERSION \ + SBAT_VAR_PREVIOUS_DATE "\n" SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE SBAT_VAR_ORIGINAL_DATE +#define SBAT_VAR_LATEST_REVOCATIONS +#define SBAT_VAR_LATEST SBAT_VAR_SIG SBAT_VAR_VERSION \ + SBAT_VAR_LATEST_DATE "\n" SBAT_VAR_LATEST_REVOCATIONS +#endif /* ENABLE_SHIM_DEVEL */ #define UEFI_VAR_NV_BS \ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) @@ -33,6 +56,9 @@ #define SBAT_VAR_ATTRS UEFI_VAR_NV_BS #endif +#define SBAT_POLICY L"SbatPolicy" +#define SBAT_POLICY8 "SbatPolicy" + extern UINTN _sbat, _esbat; struct sbat_var_entry { @@ -51,7 +77,8 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); EFI_STATUS set_sbat_uefi_variable(void); -bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes); +bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, + UINT32 attributes, char *sbar_var); struct sbat_section_entry { const CHAR8 *component_name; diff --git a/sbat.c b/sbat.c index 76c9d2c0..461d4859 100644 --- a/sbat.c +++ b/sbat.c @@ -320,11 +320,15 @@ check_sbat_var_attributes(UINT32 attributes) } bool -preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes) +preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes, + char *sbat_var) { + const char *sbatc = (const char *)sbat; + return check_sbat_var_attributes(attributes) && - sbatsize >= strlen(SBAT_VAR_SIG "1") && - !strncmp((const char *)sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG)); + sbatsize >= strlen(SBAT_VAR_ORIGINAL) && + !strcmp(sbatc, SBAT_VAR_SIG SBAT_VAR_VERSION) && + strcmp(sbatc, sbat_var) >= 0; } EFI_STATUS @@ -334,7 +338,44 @@ set_sbat_uefi_variable(void) UINT32 attributes = 0; UINT8 *sbat = NULL; + UINT8 *sbat_policy = NULL; UINTN sbatsize = 0; + UINTN sbat_policysize = 0; + + char *sbat_var = NULL; + bool reset_sbat = false; + + efi_status = get_variable_attr(SBAT_POLICY, &sbat_policy, + &sbat_policysize, SHIM_LOCK_GUID, + &attributes); + if (EFI_ERROR(efi_status)) { + dprint("Default sbat policy: previous\n"); + sbat_var = SBAT_VAR_PREVIOUS; + } else { + switch (*sbat_policy) { + case 1: + dprint("Custom sbat policy: latest\n"); + sbat_var = SBAT_VAR_LATEST; + break; + case 2: + dprint("Custom sbat policy: previous\n"); + sbat_var = SBAT_VAR_PREVIOUS; + break; + case 3: + if (secure_mode()) { + console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); + sbat_var = SBAT_VAR_PREVIOUS; + } else { + dprint(L"Custom SBAT policy: reset OK\n"); + reset_sbat = true; + sbat_var = SBAT_VAR_ORIGINAL; + } + efi_status = del_variable(SBAT_POLICY, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) + console_error(L"Could not reset SBAT Policy", + efi_status); + } + } efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, SHIM_LOCK_GUID, &attributes); @@ -346,8 +387,9 @@ set_sbat_uefi_variable(void) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes)) { - dprint(L"%s variable is %d bytes, attributes are 0x%08x\n", + } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, sbat_var) + && !reset_sbat) { + dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); return EFI_SUCCESS; @@ -369,7 +411,7 @@ set_sbat_uefi_variable(void) /* set variable */ efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, - sizeof(SBAT_VAR)-1, SBAT_VAR); + strlen(sbat_var), sbat_var); if (EFI_ERROR(efi_status)) { dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME, efi_status); @@ -384,10 +426,10 @@ set_sbat_uefi_variable(void) return efi_status; } - if (sbatsize != strlen(SBAT_VAR) || - strncmp((const char *)sbat, SBAT_VAR, strlen(SBAT_VAR)) != 0) { + if (sbatsize != strlen(sbat_var) || + strncmp((const char *)sbat, sbat_var, strlen(sbat_var)) != 0) { dprint("new sbatsize is %d, expected %d\n", sbatsize, - strlen(SBAT_VAR)); + strlen(sbat_var)); efi_status = EFI_INVALID_PARAMETER; } else { dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME); diff --git a/test-sbat.c b/test-sbat.c index b64aa1e9..d0ed713f 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -14,6 +14,11 @@ list_t sbat_var; +BOOLEAN +secure_mode() { + return 1; +} + #if 0 /* * Mock test helpers @@ -965,11 +970,39 @@ err: int test_preserve_sbat_uefi_variable_good(void) { - char sbat[] = "sbat,1,\ncomponent,2,\n"; + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return 0; + else + return -1; +} + +int +test_preserve_sbat_uefi_variable_newer(void) +{ + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2025030218\ncomponent,5,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return -1; + else + return 0; +} +int +test_preserve_sbat_uefi_variable_older(void) +{ + char sbat[] = "sbat,1,2025030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2020030218\ncomponent,1,\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return 0; else return -1; @@ -978,11 +1011,12 @@ test_preserve_sbat_uefi_variable_good(void) int test_preserve_sbat_uefi_variable_bad_sig(void) { - char sbat[] = "bad_sig,1,\ncomponent,2,\n"; + char sbat[] = "bad_sig,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -991,11 +1025,12 @@ test_preserve_sbat_uefi_variable_bad_sig(void) int test_preserve_sbat_uefi_variable_bad_attr(void) { - char sbat[] = "sbat,1,\ncomponent,2,\n"; + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = 0; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -1005,10 +1040,11 @@ int test_preserve_sbat_uefi_variable_bad_short(void) { char sbat[] = "sba"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -1052,6 +1088,8 @@ main(void) test(test_parse_and_verify); test(test_preserve_sbat_uefi_variable_good); + test(test_preserve_sbat_uefi_variable_newer); + test(test_preserve_sbat_uefi_variable_older); test(test_preserve_sbat_uefi_variable_bad_sig); test(test_preserve_sbat_uefi_variable_bad_attr); test(test_preserve_sbat_uefi_variable_bad_short); diff --git a/test.c b/test.c index 46cab533..c7978fa2 100644 --- a/test.c +++ b/test.c @@ -259,6 +259,12 @@ console_print(const CHAR16 *fmt, ...) return 0; } +void +console_error(CHAR16 *err, EFI_STATUS efi_status) +{ + return; +} + #ifndef HAVE_START_IMAGE EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) -- cgit v1.2.3 From 610a1ac7614d2ad97b81b250dc37643df610d4f5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 18 May 2022 14:39:39 -0400 Subject: sbat.h: minor reformatting for legibility Signed-off-by: Peter Jones --- include/sbat.h | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index f2ae93a5..eb01ee89 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -9,29 +9,33 @@ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," #define SBAT_VAR_ORIGINAL_DATE "2021030218" -#define SBAT_VAR_ORIGINAL SBAT_VAR_SIG SBAT_VAR_VERSION \ - SBAT_VAR_ORIGINAL_DATE "\n" +#define SBAT_VAR_ORIGINAL \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" #if defined(ENABLE_SHIM_DEVEL) #define SBAT_VAR_PREVIOUS_DATE "2022020101" #define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" -#define SBAT_VAR_PREVIOUS SBAT_VAR_SIG SBAT_VAR_VERSION \ - SBAT_VAR_PREVIOUS_DATE "\n" SBAT_VAR_PREVIOUS_REVOCATIONS +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" -#define SBAT_VAR_LATEST SBAT_VAR_SIG SBAT_VAR_VERSION \ - SBAT_VAR_LATEST_DATE "\n" SBAT_VAR_LATEST_REVOCATIONS +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS #else /* !ENABLE_SHIM_DEVEL */ #define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE #define SBAT_VAR_PREVIOUS_REVOCATIONS -#define SBAT_VAR_PREVIOUS SBAT_VAR_SIG SBAT_VAR_VERSION \ - SBAT_VAR_PREVIOUS_DATE "\n" SBAT_VAR_PREVIOUS_REVOCATIONS +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS #define SBAT_VAR_LATEST_DATE SBAT_VAR_ORIGINAL_DATE #define SBAT_VAR_LATEST_REVOCATIONS -#define SBAT_VAR_LATEST SBAT_VAR_SIG SBAT_VAR_VERSION \ - SBAT_VAR_LATEST_DATE "\n" SBAT_VAR_LATEST_REVOCATIONS +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS #endif /* ENABLE_SHIM_DEVEL */ #define UEFI_VAR_NV_BS \ -- cgit v1.2.3 From f28833f7cbb3f536081b19c8a2cc6f709e772128 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 18 May 2022 14:54:06 -0400 Subject: peimage.h: make our signature macros force the type scan-build invoked clang in a way that complains about our SIGNATURE_XX() macro's sizes being used to assign to things that are that size in post-process-pe.c. This patch makes them cast the results to the appropriately sized type. Signed-off-by: Peter Jones --- include/peimage.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/peimage.h b/include/peimage.h index 4bcb940d..e97b29c4 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -17,10 +17,14 @@ #include "wincert.h" -#define SIGNATURE_16(A, B) ((A) | (B << 8)) -#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) -#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ - (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) +#define SIGNATURE_16(A, B) \ + ((UINT16)(((UINT16)(A)) | (((UINT16)(B)) << ((UINT16)8)))) +#define SIGNATURE_32(A, B, C, D) \ + ((UINT32)(((UINT32)SIGNATURE_16(A, B)) | \ + (((UINT32)SIGNATURE_16(C, D)) << (UINT32)16))) +#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ + ((UINT64)((UINT64)SIGNATURE_32(A, B, C, D) | \ + ((UINT64)(SIGNATURE_32(E, F, G, H)) << (UINT64)32))) #define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) #define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) -- cgit v1.2.3 From a50d3645467fcfef970a00154d6d49494355afc9 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 18 May 2022 15:14:12 -0400 Subject: sbat policy: make our policy change actions symbolic There are a couple of places where the code we've got right now just uses integers to decode one of our MoK variables. That's bad. This patch replaces those with symbolic names. Signed-off-by: Peter Jones --- include/sbat.h | 4 ++++ sbat.c | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index eb01ee89..904880de 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -63,6 +63,10 @@ #define SBAT_POLICY L"SbatPolicy" #define SBAT_POLICY8 "SbatPolicy" +#define SBAT_POLICY_LATEST 1 +#define SBAT_POLICY_PREVIOUS 2 +#define SBAT_POLICY_RESET 3 + extern UINTN _sbat, _esbat; struct sbat_var_entry { diff --git a/sbat.c b/sbat.c index 461d4859..1bc34b88 100644 --- a/sbat.c +++ b/sbat.c @@ -353,15 +353,15 @@ set_sbat_uefi_variable(void) sbat_var = SBAT_VAR_PREVIOUS; } else { switch (*sbat_policy) { - case 1: + case SBAT_POLICY_LATEST: dprint("Custom sbat policy: latest\n"); sbat_var = SBAT_VAR_LATEST; break; - case 2: + case SBAT_POLICY_PREVIOUS: dprint("Custom sbat policy: previous\n"); sbat_var = SBAT_VAR_PREVIOUS; break; - case 3: + case SBAT_POLICY_RESET: if (secure_mode()) { console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); sbat_var = SBAT_VAR_PREVIOUS; @@ -374,6 +374,16 @@ set_sbat_uefi_variable(void) if (EFI_ERROR(efi_status)) console_error(L"Could not reset SBAT Policy", efi_status); + break; + default: + console_error(L"SBAT policy state %llu is invalid", + EFI_INVALID_PARAMETER); + efi_status = del_variable(SBAT_POLICY, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) + console_error(L"Could not reset SBAT Policy", + efi_status); + sbat_var = SBAT_VAR_PREVIOUS; + break; } } -- cgit v1.2.3 From aa61fdf490d16aaa23de0cbe5e9f16d3bc72e582 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 19 May 2022 15:55:40 -0400 Subject: Give the Coverity scanner some more GCC blinders... Coverity complains: CID 373676 (#3 of 3): Unrecoverable parse warning (PARSE_ERROR) 1. arguments_provided_for_attribute: attribute "__malloc__" does not take arguments This is, of course, just plain wrong. Even so, I'm tired of looking at it, so this patch wraps the #define we use for that attribute in a check to see if it's being built by Coverity. Signed-off-by: Peter Jones --- include/compiler.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index 18576724..b4bf1031 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -47,8 +47,12 @@ #define ALIAS(x) __attribute__((weak, alias (#x))) #endif #ifndef ALLOCFUNC +#if defined(__COVERITY__) +#define ALLOCFUNC(a, b) +#else #define ALLOCFUNC(dealloc, dealloc_arg) __attribute__((__malloc__(dealloc, dealloc_arg))) #endif +#endif #ifndef PRINTF #define PRINTF(first, args...) __attribute__((__format__(printf, first, ## args))) #endif -- cgit v1.2.3 From 9a09faf390eea083c3bef1b07c7e043ebe0cc1f6 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Tue, 10 May 2022 14:09:26 -0700 Subject: Update SBAT generation requirements for 05/24/22 bump shim SBAT generation requirement to 2 for CVE-2022-28737 bump GRUB2 SBAT generation requirement to 2 for CVE-2021-3695 Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 904880de..aca43598 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -31,8 +31,8 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ SBAT_VAR_PREVIOUS_REVOCATIONS -#define SBAT_VAR_LATEST_DATE SBAT_VAR_ORIGINAL_DATE -#define SBAT_VAR_LATEST_REVOCATIONS +#define SBAT_VAR_LATEST_DATE "2022052400" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,2\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From 0eb07e11b20680200d3ce9c5bc59299121a75388 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Tue, 31 May 2022 22:21:26 +0100 Subject: Make SBAT variable payload introspectable Given a set of EFI variables and boot assets, it should be possible to compute what the value of PCR 7 will be on the next boot. As shim manages the contents of the SbatLevel variable and this is measured to PCR 7, export the payloads that shim contains in a new COFF section (.sbatlevel) so that it can be introspected by code outside of shim. The new section works a bit like .vendor_cert - it contains a header and then the payload. In this case, the header contains no size fields because the strings are NULL terminated. Shim uses this new section internally in set_sbat_uefi_variable. The .sbatlevel section starts with a 4 byte version field which is not used by shim but may be useful for external auditors if the format of the section contents change in the future. Signed-off-by: Chris Coulson --- Makefile | 7 ++++--- elf_aarch64_efi.lds | 4 ++++ elf_ia32_efi.lds | 4 ++++ elf_ia64_efi.lds | 4 ++++ elf_x86_64_efi.lds | 4 ++++ include/sbat.h | 32 -------------------------------- include/sbat_var_defs.h | 38 ++++++++++++++++++++++++++++++++++++++ include/test.mk | 2 +- sbat.c | 21 ++++++++++++++++----- sbat_var.S | 20 ++++++++++++++++++++ shim.h | 1 + 11 files changed, 96 insertions(+), 41 deletions(-) create mode 100644 include/sbat_var_defs.h create mode 100644 sbat_var.S (limited to 'include') diff --git a/Makefile b/Makefile index 24ac314e..866611c7 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S +ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o @@ -253,7 +253,7 @@ endif $(OBJCOPY) -D -j .text -j .sdata -j .data -j .data.ident \ -j .dynamic -j .rodata -j .rel* \ -j .rela* -j .dyn -j .reloc -j .eh_frame \ - -j .vendor_cert -j .sbat \ + -j .vendor_cert -j .sbat -j .sbatlevel \ $(FORMAT) $< $@ ./post-process-pe -vv $@ @@ -269,6 +269,7 @@ endif $(OBJCOPY) -D -j .text -j .sdata -j .data \ -j .dynamic -j .rodata -j .rel* \ -j .rela* -j .dyn -j .reloc -j .eh_frame -j .sbat \ + -j .sbatlevel \ -j .debug_info -j .debug_abbrev -j .debug_aranges \ -j .debug_line -j .debug_str -j .debug_ranges \ -j .note.gnu.build-id \ diff --git a/elf_aarch64_efi.lds b/elf_aarch64_efi.lds index 60c55ba5..0861f5e8 100644 --- a/elf_aarch64_efi.lds +++ b/elf_aarch64_efi.lds @@ -34,6 +34,10 @@ SECTIONS .data.ident : { *(.data.ident) } + . = ALIGN(4096); + .sbatlevel : { + *(.sbatlevel) + } . = ALIGN(4096); .data : diff --git a/elf_ia32_efi.lds b/elf_ia32_efi.lds index 497a3a15..e8da91bd 100644 --- a/elf_ia32_efi.lds +++ b/elf_ia32_efi.lds @@ -28,6 +28,10 @@ SECTIONS .data.ident : { *(.data.ident) } + . = ALIGN(4096); + .sbatlevel : { + *(.sbatlevel) + } . = ALIGN(4096); .data : diff --git a/elf_ia64_efi.lds b/elf_ia64_efi.lds index 2669b856..a2195609 100644 --- a/elf_ia64_efi.lds +++ b/elf_ia64_efi.lds @@ -34,6 +34,10 @@ SECTIONS .data.ident : { *(.data.ident) } + . = ALIGN(4096); + .sbatlevel : { + *(.sbatlevel) + } . = ALIGN(4096); .data : diff --git a/elf_x86_64_efi.lds b/elf_x86_64_efi.lds index bcc65270..39aff6b0 100644 --- a/elf_x86_64_efi.lds +++ b/elf_x86_64_efi.lds @@ -35,6 +35,10 @@ SECTIONS .data.ident : { *(.data.ident) } + . = ALIGN(4096); + .sbatlevel : { + *(.sbatlevel) + } . = ALIGN(4096); .data : diff --git a/include/sbat.h b/include/sbat.h index aca43598..c94c4fba 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -6,38 +6,6 @@ #ifndef SBAT_H_ #define SBAT_H_ -#define SBAT_VAR_SIG "sbat," -#define SBAT_VAR_VERSION "1," -#define SBAT_VAR_ORIGINAL_DATE "2021030218" -#define SBAT_VAR_ORIGINAL \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" - -#if defined(ENABLE_SHIM_DEVEL) -#define SBAT_VAR_PREVIOUS_DATE "2022020101" -#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" -#define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS - -#define SBAT_VAR_LATEST_DATE "2022050100" -#define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" -#define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS -#else /* !ENABLE_SHIM_DEVEL */ -#define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE -#define SBAT_VAR_PREVIOUS_REVOCATIONS -#define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS - -#define SBAT_VAR_LATEST_DATE "2022052400" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,2\n" -#define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS -#endif /* ENABLE_SHIM_DEVEL */ - #define UEFI_VAR_NV_BS \ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) #define UEFI_VAR_NV_BS_RT \ diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h new file mode 100644 index 00000000..c656b56d --- /dev/null +++ b/include/sbat_var_defs.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#ifndef SBAT_VAR_DEFS_H_ +#define SBAT_VAR_DEFS_H_ + +#define SBAT_VAR_SIG "sbat," +#define SBAT_VAR_VERSION "1," +#define SBAT_VAR_ORIGINAL_DATE "2021030218" +#define SBAT_VAR_ORIGINAL \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" + +#if defined(ENABLE_SHIM_DEVEL) +#define SBAT_VAR_PREVIOUS_DATE "2022020101" +#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE "2022050100" +#define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS +#else /* !ENABLE_SHIM_DEVEL */ +#define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE +#define SBAT_VAR_PREVIOUS_REVOCATIONS +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE "2022052400" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,2\n" +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS +#endif /* ENABLE_SHIM_DEVEL */ + +#endif /* !SBAT_VAR_DEFS_H_ */ diff --git a/include/test.mk b/include/test.mk index e965c600..c0e24095 100644 --- a/include/test.mk +++ b/include/test.mk @@ -92,7 +92,7 @@ test-mock-variables: CFLAGS+=-DHAVE_SHIM_LOCK_GUID test-mok-mirror_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c mock-variables.c test-mok-mirror: CFLAGS+=-DHAVE_START_IMAGE -DHAVE_SHIM_LOCK_GUID -test-sbat_FILES = csv.c lib/variables.c lib/guid.c +test-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID test-str_FILES = lib/string.c diff --git a/sbat.c b/sbat.c index f1d6e98d..a08c5b2a 100644 --- a/sbat.c +++ b/sbat.c @@ -5,6 +5,11 @@ #include "shim.h" +extern struct { + UINT32 previous_offset; + UINT32 latest_offset; +} sbat_var_payload_header; + EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, size_t *n_entries, @@ -399,6 +404,9 @@ set_sbat_uefi_variable(void) EFI_STATUS efi_status = EFI_SUCCESS; UINT32 attributes = 0; + char *sbat_var_previous; + char *sbat_var_latest; + UINT8 *sbat = NULL; UINT8 *sbat_policy = NULL; UINTN sbatsize = 0; @@ -407,27 +415,30 @@ set_sbat_uefi_variable(void) char *sbat_var = NULL; bool reset_sbat = false; + sbat_var_previous = (char *)&sbat_var_payload_header + sbat_var_payload_header.previous_offset; + sbat_var_latest = (char *)&sbat_var_payload_header + sbat_var_payload_header.latest_offset; + efi_status = get_variable_attr(SBAT_POLICY, &sbat_policy, &sbat_policysize, SHIM_LOCK_GUID, &attributes); if (EFI_ERROR(efi_status)) { dprint("Default sbat policy: previous\n"); - sbat_var = SBAT_VAR_PREVIOUS; + sbat_var = sbat_var_previous; } else { switch (*sbat_policy) { case SBAT_POLICY_LATEST: dprint("Custom sbat policy: latest\n"); - sbat_var = SBAT_VAR_LATEST; + sbat_var = sbat_var_latest; clear_sbat_policy(); break; case SBAT_POLICY_PREVIOUS: dprint("Custom sbat policy: previous\n"); - sbat_var = SBAT_VAR_PREVIOUS; + sbat_var = sbat_var_previous; break; case SBAT_POLICY_RESET: if (secure_mode()) { console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); - sbat_var = SBAT_VAR_PREVIOUS; + sbat_var = sbat_var_previous; } else { dprint(L"Custom SBAT policy: reset OK\n"); reset_sbat = true; @@ -438,7 +449,7 @@ set_sbat_uefi_variable(void) default: console_error(L"SBAT policy state %llu is invalid", EFI_INVALID_PARAMETER); - sbat_var = SBAT_VAR_PREVIOUS; + sbat_var = sbat_var_previous; clear_sbat_policy(); break; } diff --git a/sbat_var.S b/sbat_var.S new file mode 100644 index 00000000..a115077a --- /dev/null +++ b/sbat_var.S @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#include "include/sbat_var_defs.h" + + .section .sbatlevel, "a", %progbits + .balignl 4, 0 + .4byte 0 /* format version for external parsers */ + .globl sbat_var_payload_header + .type sbat_var_payload_header, %object + .size sbat_var_payload_header, .Lsbat_var_payload_header_end - sbat_var_payload_header +sbat_var_payload_header: + .4byte .Lsbat_var_previous - sbat_var_payload_header + .4byte .Lsbat_var_latest - sbat_var_payload_header +.Lsbat_var_payload_header_end: + .balign 1, 0 +.Lsbat_var_previous: + .asciz SBAT_VAR_PREVIOUS + .balign 1, 0 +.Lsbat_var_latest: + .asciz SBAT_VAR_LATEST diff --git a/shim.h b/shim.h index b5272b9c..7e9d10eb 100644 --- a/shim.h +++ b/shim.h @@ -179,6 +179,7 @@ #include "include/pe.h" #include "include/replacements.h" #include "include/sbat.h" +#include "include/sbat_var_defs.h" #if defined(OVERRIDE_SECURITY_POLICY) #include "include/security_policy.h" #endif -- cgit v1.2.3 From 4fd484e4c29364b4fdf4d043556fa0a210c5fdfc Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Sun, 22 May 2022 16:02:20 +0800 Subject: Enable TDX measurement to RTMR register Intel Trust Domain Extensions (Intel TDX) extends Virtual Machine Extensions (VMX) and Multi-Key Total Memory Encryption (MK-TME) with a new kind of virtual machine guest called a Trust Domain(TD)[1]. A TD runs in a CPU mode that is designed to protect the confidentiality of its memory contents and its CPU state from any other software, including the hosting Virtual Machine Monitor (VMM). Trust Domain Virtual Firmware (TDVF) is required to provide Intel TDX implementation and service for EFI_CC_MEASUREMENT_PROTOCOL[2]. The bugzilla for TDVF is at https://bugzilla.tianocore.org/show_bug.cgi?id=3625. To support CC measurement/attestation with Intel TDX technology, these 4 RTMR registers will be extended by TDX service like TPM/TPM2 PCR: - RTMR[0] for TDVF configuration - RTMR[1] for the TD OS loader and kernel - RTMR[2] for the OS application - RTMR[3] reserved for special usage only Add a TDX Implementation for CC Measurement protocol along with TPM/TPM2 protocol. References: [1] https://software.intel.com/content/dam/develop/external/us/en/documents/tdx-whitepaper-v4.pdf [2] https://software.intel.com/content/dam/develop/external/us/en/documents/tdx-virtual-firmware-design-guide-rev-1.pdf [3] https://software.intel.com/content/dam/develop/external/us/en/documents/intel-tdx-guest-hypervisor-communication-interface-1.0-344426-002.pdf Signed-off-by: Lu Ken [rharwood: style pass on code and commit message] Signed-off-by: Robbie Harwood --- include/cc.h | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/guid.h | 1 + lib/guid.c | 1 + shim.h | 1 + tpm.c | 48 +++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 include/cc.h (limited to 'include') diff --git a/include/cc.h b/include/cc.h new file mode 100644 index 00000000..8b127208 --- /dev/null +++ b/include/cc.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#ifndef SHIM_CC_H +#define SHIM_CC_H + +typedef struct { + uint8_t Major; + uint8_t Minor; +} EFI_CC_VERSION; + +#define EFI_CC_TYPE_NONE 0 +#define EFI_CC_TYPE_SEV 1 +#define EFI_CC_TYPE_TDX 2 + +typedef struct { + uint8_t Type; + uint8_t SubType; +} EFI_CC_TYPE; + +typedef uint32_t EFI_CC_EVENT_LOG_BITMAP; +typedef uint32_t EFI_CC_EVENT_LOG_FORMAT; +typedef uint32_t EFI_CC_EVENT_ALGORITHM_BITMAP; +typedef uint32_t EFI_CC_MR_INDEX; + +#define TDX_MR_INDEX_MRTD 0 +#define TDX_MR_INDEX_RTMR0 1 +#define TDX_MR_INDEX_RTMR1 2 +#define TDX_MR_INDEX_RTMR2 3 +#define TDX_MR_INDEX_RTMR3 4 + +#define EFI_CC_EVENT_LOG_FORMAT_TCG_2 0x00000002 +#define EFI_CC_BOOT_HASH_ALG_SHA384 0x00000004 +#define EFI_CC_EVENT_HEADER_VERSION 1 + +typedef struct tdEFI_CC_EVENT_HEADER { + uint32_t HeaderSize; + uint16_t HeaderVersion; + EFI_CC_MR_INDEX MrIndex; + uint32_t EventType; +} __attribute__((packed)) EFI_CC_EVENT_HEADER; + +typedef struct tdEFI_CC_EVENT { + uint32_t Size; + EFI_CC_EVENT_HEADER Header; + uint8_t Event[1]; +} __attribute__((packed)) EFI_CC_EVENT; + +typedef struct tdEFI_CC_BOOT_SERVICE_CAPABILITY { + uint8_t Size; + EFI_CC_VERSION StructureVersion; + EFI_CC_VERSION ProtocolVersion; + EFI_CC_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_CC_EVENT_LOG_BITMAP SupportedEventLogs; + EFI_CC_TYPE CcType; +} EFI_CC_BOOT_SERVICE_CAPABILITY; + +struct efi_cc_protocol +{ + EFI_STATUS (EFIAPI *get_capability) ( + struct efi_cc_protocol *this, + EFI_CC_BOOT_SERVICE_CAPABILITY *ProtocolCapability); + EFI_STATUS (EFIAPI *get_event_log) ( + struct efi_cc_protocol *this, + EFI_CC_EVENT_LOG_FORMAT EventLogFormat, + EFI_PHYSICAL_ADDRESS *EventLogLocation, + EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + BOOLEAN *EventLogTruncated); + EFI_STATUS (EFIAPI *hash_log_extend_event) ( + struct efi_cc_protocol *this, + uint64_t Flags, + EFI_PHYSICAL_ADDRESS DataToHash, + uint64_t DataToHashLen, + EFI_CC_EVENT *EfiCcEvent); + EFI_STATUS (EFIAPI *map_pcr_to_mr_index) ( + struct efi_cc_protocol *this, + uint32_t PcrIndex, + EFI_CC_MR_INDEX *MrIndex); +}; + +typedef struct efi_cc_protocol efi_cc_protocol_t; + +#define EFI_CC_FLAG_PE_COFF_IMAGE 0x0000000000000010 + +#endif /* SHIM_CC_H */ +// vim:fenc=utf-8:tw=75 diff --git a/include/guid.h b/include/guid.h index d9910ff1..dad63f0f 100644 --- a/include/guid.h +++ b/include/guid.h @@ -29,6 +29,7 @@ extern EFI_GUID EFI_IP6_CONFIG_GUID; extern EFI_GUID EFI_LOADED_IMAGE_GUID; extern EFI_GUID EFI_TPM_GUID; extern EFI_GUID EFI_TPM2_GUID; +extern EFI_GUID EFI_CC_MEASUREMENT_PROTOCOL_GUID; extern EFI_GUID EFI_SECURE_BOOT_DB_GUID; extern EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID; extern EFI_GUID SECURITY_PROTOCOL_GUID; diff --git a/lib/guid.c b/lib/guid.c index e100c92e..904629eb 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -28,6 +28,7 @@ EFI_GUID EFI_IP6_CONFIG_GUID = { 0x937fe521, 0x95ae, 0x4d1a, {0x89, 0x29, 0x48, EFI_GUID EFI_LOADED_IMAGE_GUID = EFI_LOADED_IMAGE_PROTOCOL_GUID; EFI_GUID EFI_TPM_GUID = { 0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd } }; EFI_GUID EFI_TPM2_GUID = { 0x607f766c, 0x7455, 0x42be, {0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f } }; +EFI_GUID EFI_CC_MEASUREMENT_PROTOCOL_GUID = { 0x96751a3d, 0x72f4, 0x41a6, {0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b } }; EFI_GUID EFI_SECURE_BOOT_DB_GUID = { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f } }; EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } }; diff --git a/shim.h b/shim.h index 7e9d10eb..14824c67 100644 --- a/shim.h +++ b/shim.h @@ -186,6 +186,7 @@ #include "include/simple_file.h" #include "include/str.h" #include "include/tpm.h" +#include "include/cc.h" #include "include/ucs2.h" #include "include/variables.h" #include "include/hexdump.h" diff --git a/tpm.c b/tpm.c index 41f36651..388f8d12 100644 --- a/tpm.c +++ b/tpm.c @@ -108,6 +108,45 @@ static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm, return EFI_NOT_FOUND; } +static EFI_STATUS cc_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, + UINT8 pcr, const CHAR8 *log, UINTN logsize, + UINT32 type, BOOLEAN is_pe_image) +{ + EFI_STATUS efi_status; + EFI_CC_EVENT *event; + efi_cc_protocol_t *cc; + EFI_CC_MR_INDEX mr; + uint64_t flags = is_pe_image ? EFI_CC_FLAG_PE_COFF_IMAGE : 0; + + efi_status = LibLocateProtocol(&EFI_CC_MEASUREMENT_PROTOCOL_GUID, + (VOID **)&cc); + if (EFI_ERROR(efi_status) || !cc) + return EFI_SUCCESS; + + efi_status = cc->map_pcr_to_mr_index(cc, pcr, &mr); + if (EFI_ERROR(efi_status)) + return EFI_NOT_FOUND; + + UINTN event_size = sizeof(*event) - sizeof(event->Event) + logsize; + + event = AllocatePool(event_size); + if (!event) { + perror(L"Unable to allocate event structure\n"); + return EFI_OUT_OF_RESOURCES; + } + + event->Header.HeaderSize = sizeof(EFI_CC_EVENT_HEADER); + event->Header.HeaderVersion = EFI_CC_EVENT_HEADER_VERSION; + event->Header.MrIndex = mr; + event->Header.EventType = type; + event->Size = event_size; + CopyMem(event->Event, (VOID *)log, logsize); + efi_status = cc->hash_log_extend_event(cc, flags, buf, (UINT64)size, + event); + FreePool(event); + return efi_status; +} + static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, const CHAR8 *log, UINTN logsize, UINT32 type, CHAR8 *hash) @@ -118,6 +157,15 @@ static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, BOOLEAN old_caps; EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + /* CC guest like TDX or SEV will measure the buffer and log the event, + extend the result into a specific CC MR like TCG's PCR. It could + coexists with TCG's TPM 1.2 and TPM 2. + */ + efi_status = cc_log_event_raw(buf, size, pcr, log, logsize, type, + (hash != NULL)); + if (EFI_ERROR(efi_status)) + return efi_status; + efi_status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps); if (EFI_ERROR(efi_status)) { #ifdef REQUIRE_TPM -- cgit v1.2.3 From 14d63398298c8de23036a4cf61594108b7345863 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 23 Aug 2022 12:07:16 -0400 Subject: Discard load-options that start with a NUL In 6c8d08c0af4768c715b79c8ec25141d56e34f8b4 ("shim: Ignore UEFI LoadOptions that are just NUL characters."), a check was added to discard load options that are entirely NUL. We now see some firmwares that start LoadOptions with a NUL, and then follow it with garbage (path to directory containing loaders). Widen the check to just discard anything that starts with a NUL. Resolves: #490 Related: #95 See-also: https://bugzilla.redhat.com/show_bug.cgi?id=2113005 Signed-off-by: Robbie Harwood --- include/ucs2.h | 18 ------------------ load-options.c | 7 ++++++- 2 files changed, 6 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/ucs2.h b/include/ucs2.h index ee038ce7..87eab32f 100644 --- a/include/ucs2.h +++ b/include/ucs2.h @@ -63,22 +63,4 @@ StrCSpn(const CHAR16 *s, const CHAR16 *reject) return ret; } -/* - * Test if an entire buffer is nothing but NUL characters. This - * implementation "gracefully" ignores the difference between the - * UTF-8/ASCII 1-byte NUL and the UCS-2 2-byte NUL. - */ -static inline bool -__attribute__((__unused__)) -is_all_nuls(UINT8 *data, UINTN data_size) -{ - UINTN i; - - for (i = 0; i < data_size; i++) { - if (data[i] != 0) - return false; - } - return true; -} - #endif /* SHIM_UCS2_H */ diff --git a/load-options.c b/load-options.c index c6bb7427..a8c6e1a3 100644 --- a/load-options.c +++ b/load-options.c @@ -404,8 +404,13 @@ parse_load_options(EFI_LOADED_IMAGE *li) /* * Apparently sometimes we get L"\0\0"? Which isn't useful at all. + * + * Possibly related, but some boards have additional data before the + * size which is garbage (it's a weird path to the directory + * containing the loaders). Known boards that do this: Kontron VX3040 + * (AMI), ASUS B85M-E, and at least one "older Dell laptop". */ - if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize)) + if (((CHAR16 *)li->LoadOptions)[0] == 0) return EFI_SUCCESS; /* -- cgit v1.2.3 From 5c537b3d0cf8c393dad2e61d49aade68f3af1401 Mon Sep 17 00:00:00 2001 From: dann frazier Date: Tue, 6 Sep 2022 09:28:22 -0600 Subject: shim: Flush the memory region from i-cache before execution We've seen crashes in early GRUB code on an ARM Cortex-A72-based platform that point at seemingly harmless instructions. Flushing the i-cache of those instructions prior to executing has been shown to avoid the problem, which has parallels with this story: https://www.mail-archive.com/osv-dev@googlegroups.com/msg06203.html Add a cache flushing utility function and provide an implementation using a GCC intrinsic. This will need to be extended to support other compilers. Note that this intrinsic is a no-op for x86 platforms. This fixes issue #498. Signed-off-by: dann frazier --- include/compiler.h | 6 ++++++ pe.c | 3 +++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index b4bf1031..b0d595f3 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -192,5 +192,11 @@ */ #define unreachable() __builtin_unreachable() +#if defined(__GNUC__) +#define cache_invalidate(begin, end) __builtin___clear_cache(begin, end) +#else /* __GNUC__ */ +#error shim has no cache_invalidate() implementation for this compiler +#endif /* __GNUC__ */ + #endif /* !COMPILER_H_ */ // vim:fenc=utf-8:tw=75:et diff --git a/pe.c b/pe.c index ba3e2bbc..f94530a2 100644 --- a/pe.c +++ b/pe.c @@ -1196,6 +1196,9 @@ handle_image (void *data, unsigned int datasize, CopyMem(buffer, data, context.SizeOfHeaders); + /* Flush the instruction cache for the region holding the image */ + cache_invalidate(buffer, buffer + context.ImageSize); + *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); if (!*entry_point) { perror(L"Entry point is invalid\n"); -- cgit v1.2.3 From dd8be98cf0fceddd9f156d2917565b18d38c4830 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 16 Nov 2022 13:25:11 -0500 Subject: Bump grub's sbat requirement to grub,3 Due to the issues addressed in the 2022-11-15 batch of grub CVEs[0], we need to bump the sbat version from grub. This patch changes it from 2 to 3. [0] https://lists.gnu.org/archive/html/grub-devel/2022-11/msg00059.html Signed-off-by: Peter Jones --- include/sbat_var_defs.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index c656b56d..6b01573e 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -3,6 +3,9 @@ #ifndef SBAT_VAR_DEFS_H_ #define SBAT_VAR_DEFS_H_ +/* + * This is the entry for the sbat data format + */ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," #define SBAT_VAR_ORIGINAL_DATE "2021030218" @@ -22,14 +25,18 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #else /* !ENABLE_SHIM_DEVEL */ -#define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE -#define SBAT_VAR_PREVIOUS_REVOCATIONS +/* + * As of 2022-11-16, most folks (including Ubuntu, SUSE, openSUSE) don't have + * a "shim,2" yet, so adding that here would end up unbootable. + */ +#define SBAT_VAR_PREVIOUS_DATE "2022052400" +#define SBAT_VAR_PREVIOUS_REVOCATIONS "grub,2\n" #define SBAT_VAR_PREVIOUS \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ SBAT_VAR_PREVIOUS_REVOCATIONS -#define SBAT_VAR_LATEST_DATE "2022052400" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,2\n" +#define SBAT_VAR_LATEST_DATE "2022111500" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,3\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From 657b2483ca6e9fcf2ad8ac7ee577ff546d24c3aa Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 5 Dec 2022 17:57:36 -0500 Subject: Make sbat_var.S parse right with buggy gcc/binutils In https://github.com/rhboot/shim/issues/533 , iokomin noticed that gas in binutils before 2.36 appears to be incorrectly concatenating string literals in '.asciz' directives, including an extra NUL character in between the strings, and this will cause us to incorrectly parse the .sbatlevel section in shim binaries. This patch adds test cases that will cause the build to fail if this has happened, as well as changing sbat_var.S to to use '.ascii' and '.byte' to construct the data, rather than using '.asciz'. Signed-off-by: Peter Jones --- include/test.mk | 2 +- sbat_var.S | 6 ++++-- test-sbat.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index c0e24095..c37b8446 100644 --- a/include/test.mk +++ b/include/test.mk @@ -92,7 +92,7 @@ test-mock-variables: CFLAGS+=-DHAVE_SHIM_LOCK_GUID test-mok-mirror_FILES = mok.c globals.c tpm.c lib/guid.c lib/variables.c mock-variables.c test-mok-mirror: CFLAGS+=-DHAVE_START_IMAGE -DHAVE_SHIM_LOCK_GUID -test-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S +test-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S mock-variables.c test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID test-str_FILES = lib/string.c diff --git a/sbat_var.S b/sbat_var.S index a115077a..2a813a40 100644 --- a/sbat_var.S +++ b/sbat_var.S @@ -14,7 +14,9 @@ sbat_var_payload_header: .Lsbat_var_payload_header_end: .balign 1, 0 .Lsbat_var_previous: - .asciz SBAT_VAR_PREVIOUS + .ascii SBAT_VAR_PREVIOUS + .byte 0 .balign 1, 0 .Lsbat_var_latest: - .asciz SBAT_VAR_LATEST + .ascii SBAT_VAR_LATEST + .byte 0 diff --git a/test-sbat.c b/test-sbat.c index 72bebe7a..65bc6a84 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -1107,6 +1107,36 @@ test_preserve_sbat_uefi_variable_bad_short(void) return 0; } +static int +test_sbat_var_asciz(void) +{ + EFI_STATUS status; + char buf[1024] = ""; + UINT32 attrs = 0; + UINTN size = sizeof(buf); + char expected[] = SBAT_VAR_PREVIOUS; + + status = set_sbat_uefi_variable(); + if (status != EFI_SUCCESS) + return -1; + + status = RT->GetVariable(SBAT_VAR_NAME, &SHIM_LOCK_GUID, &attrs, &size, buf); + if (status != EFI_SUCCESS) + return -1; + + /* + * this should be enough to get past "sbat,", which handles the + * first error. + */ + if (size < (strlen(SBAT_VAR_SIG) + 2) || size != strlen(expected)) + return -1; + + if (strncmp(expected, buf, size) != 0) + return -1; + + return 0; +} + int main(void) { @@ -1155,6 +1185,8 @@ main(void) test(test_preserve_sbat_uefi_variable_version_older); test(test_preserve_sbat_uefi_variable_version_olderlonger); + test(test_sbat_var_asciz); + return 0; } -- cgit v1.2.3 From e4f40ae862b5389c8cc7d4f938a34238421456a1 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 20 Dec 2022 14:40:39 -0500 Subject: pe: Add IS_PAGE_ALIGNED macro This makes some checks in `get_mem_attrs` and `update_mem_attrs` clearer. Also add `test-pe-util.c` with a test for the new macro. The file is named that way instead of `test-pe.c` to avoid having to get `pe.c` building in the unit test environment. Signed-off-by: Nicholas Bishop --- include/peimage.h | 3 +++ pe.c | 4 ++-- test-pe-util.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test-pe-util.c (limited to 'include') diff --git a/include/peimage.h b/include/peimage.h index e97b29c4..6eef1051 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -29,6 +29,9 @@ #define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) #define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) +// Check if `val` is evenly aligned to the page size. +#define IS_PAGE_ALIGNED(val) (!((val) & EFI_PAGE_MASK)) + // // PE32+ Subsystem type for EFI images // diff --git a/pe.c b/pe.c index 5ad0914b..85b64c09 100644 --- a/pe.c +++ b/pe.c @@ -937,7 +937,7 @@ get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) if (EFI_ERROR(efi_status) || !proto) return efi_status; - if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) { + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", __func__, (unsigned long long)physaddr, (unsigned long long)(physaddr+size-1), @@ -971,7 +971,7 @@ update_mem_attrs(uintptr_t addr, uint64_t size, (unsigned long long)addr, (unsigned long long)size, &before, efi_status); - if (physaddr & 0xfff || size & 0xfff || size == 0) { + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { dprint(L"%a called on 0x%llx-0x%llx (size 0x%llx) +%a%a%a -%a%a%a\n", __func__, (unsigned long long)physaddr, (unsigned long long)(physaddr + size - 1), diff --git a/test-pe-util.c b/test-pe-util.c new file mode 100644 index 00000000..d5765488 --- /dev/null +++ b/test-pe-util.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-pe-util.c - test PE utilities + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +static int +test_is_page_aligned(void) +{ + assert_true_return(IS_PAGE_ALIGNED(0), -1, "\n"); + assert_false_return(IS_PAGE_ALIGNED(1), -1, "\n"); + assert_false_return(IS_PAGE_ALIGNED(4095), -1, "\n"); + assert_true_return(IS_PAGE_ALIGNED(4096), -1, "\n"); + assert_false_return(IS_PAGE_ALIGNED(4097), -1, "\n"); + + return 0; +} + +int +main(void) +{ + int status = 0; + test(test_is_page_aligned); + + return status; +} -- cgit v1.2.3 From cca3933f48e3a52863322f358c2e8cb8ea80bd57 Mon Sep 17 00:00:00 2001 From: Steve McIntyre Date: Mon, 30 Jan 2023 18:15:36 +0000 Subject: Block Debian grub binaries with SBAT < 4 (See https://bugs.debian.org/1024617) One of the Debian builds of grub bumped the SBAT to 3, but didn't include the patches needed. Add "grub.debian,4" to block those binaries. Signed-off-by: Steve McIntyre --- include/sbat_var_defs.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 6b01573e..5b1a764f 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -35,8 +35,12 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ SBAT_VAR_PREVIOUS_REVOCATIONS -#define SBAT_VAR_LATEST_DATE "2022111500" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,3\n" +/* + * Debian's grub.3 update was broken - some binaries included the SBAT + * data update but not the security patches :-( + */ +#define SBAT_VAR_LATEST_DATE "2023012900" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From f7a4338f1b5ef03dca83ce44075e9d6e5897e037 Mon Sep 17 00:00:00 2001 From: Kamil Aronowski Date: Mon, 8 May 2023 09:28:24 +0200 Subject: Skip testing msleep() In preparation for renaming msleep() to usleep(), in some cases tests were failing due to a mismatch between our declaration of the usleep() function and what is being provided by unistd.h. This change simply makes our function declared only when not in a unit test environment. Signed-off-by: Kamil Aronowski --- include/console.h | 2 ++ lib/console.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/console.h b/include/console.h index 0c4a5137..2a29c2dc 100644 --- a/include/console.h +++ b/include/console.h @@ -122,7 +122,9 @@ extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, extern EFI_STATUS print_crypto_errors(EFI_STATUS rc, char *file, const char *func, int line); #define crypterr(rc) print_crypto_errors((rc), __FILE__, __func__, __LINE__) +#ifndef SHIM_UNIT_TEST extern VOID msleep(unsigned long msecs); +#endif /* This is used in various things to determine if we should print to the * console */ diff --git a/lib/console.c b/lib/console.c index 6bbb8a7c..c7ca3bc2 100644 --- a/lib/console.c +++ b/lib/console.c @@ -743,11 +743,13 @@ setup_verbosity(VOID) setup_console(-1); } +#ifndef SHIM_UNIT_TEST VOID msleep(unsigned long msecs) { BS->Stall(msecs); } +#endif /* This is used in various things to determine if we should print to the * console */ -- cgit v1.2.3 From 549d34691d68518e55c2edd6e759b19de7f8ddef Mon Sep 17 00:00:00 2001 From: Kamil Aronowski Date: Wed, 12 Apr 2023 18:50:12 +0200 Subject: Rename 'msecs' to 'usecs' to avoid potential confusion The function msleep uses gBS->Stall which waits for a specified number of microseconds. Reference: https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/5_uefi_services/51_services_that_uefi_drivers_commonly_use/517_stall This reference even mentions an example sleeping for 10 microseconds: // Wait 10 uS. Notice the letter 'u'. Therefore it's a good idea to call the function 'usleep' rather than 'msleep', so no one confuses it with milliseconds, and to change the argument name to match as well. Signed-off-by: Kamil Aronowski --- fallback.c | 8 ++++---- include/asm.h | 4 ++-- include/console.h | 2 +- lib/console.c | 4 ++-- model.c | 2 +- replacements.c | 6 +++--- shim.c | 12 ++++++------ 7 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/fallback.c b/fallback.c index da4b25cc..ad70ccf1 100644 --- a/fallback.c +++ b/fallback.c @@ -1013,7 +1013,7 @@ try_start_first_option(EFI_HANDLE parent_image_handle) console_print(L"Verbose enabled, sleeping for %d mseconds... " L"Press the Pause key now to hold for longer.\n", fallback_verbose_wait); - msleep(fallback_verbose_wait); + usleep(fallback_verbose_wait); } if (!first_new_option) { @@ -1036,7 +1036,7 @@ try_start_first_option(EFI_HANDLE parent_image_handle) } console_print(L"\n"); - msleep(500000000); + usleep(500000000); return efi_status; } @@ -1051,7 +1051,7 @@ try_start_first_option(EFI_HANDLE parent_image_handle) efi_status = BS->StartImage(image_handle, NULL, NULL); if (EFI_ERROR(efi_status)) { console_print(L"StartImage failed: %r\n", efi_status); - msleep(500000000); + usleep(500000000); } return efi_status; } @@ -1218,7 +1218,7 @@ reset: console_print(L"Verbose enabled, sleeping for %d mseconds... " L"Press the Pause key now to hold for longer.\n", fallback_verbose_wait); - msleep(fallback_verbose_wait); + usleep(fallback_verbose_wait); } RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); diff --git a/include/asm.h b/include/asm.h index 03b06557..f5118b23 100644 --- a/include/asm.h +++ b/include/asm.h @@ -40,11 +40,11 @@ static inline void wait_for_debug(void) { uint64_t a, b; int x; - extern void msleep(unsigned long msecs); + extern void usleep(unsigned long usecs); a = read_counter(); for (x = 0; x < 1000; x++) { - msleep(1000); + usleep(1000); b = read_counter(); if (a != b) break; diff --git a/include/console.h b/include/console.h index 2a29c2dc..8eb4b47f 100644 --- a/include/console.h +++ b/include/console.h @@ -123,7 +123,7 @@ extern EFI_STATUS print_crypto_errors(EFI_STATUS rc, char *file, const char *fun #define crypterr(rc) print_crypto_errors((rc), __FILE__, __func__, __LINE__) #ifndef SHIM_UNIT_TEST -extern VOID msleep(unsigned long msecs); +extern VOID usleep(unsigned long usecs); #endif /* This is used in various things to determine if we should print to the diff --git a/lib/console.c b/lib/console.c index c7ca3bc2..a751f79d 100644 --- a/lib/console.c +++ b/lib/console.c @@ -745,9 +745,9 @@ setup_verbosity(VOID) #ifndef SHIM_UNIT_TEST VOID -msleep(unsigned long msecs) +usleep(unsigned long usecs) { - BS->Stall(msecs); + BS->Stall(usecs); } #endif diff --git a/model.c b/model.c index e122ba9c..6052ff50 100644 --- a/model.c +++ b/model.c @@ -54,7 +54,7 @@ gcm_gmult_4bit(u64 Xi[2], u128 Htable[16]) } void -msleep(int n) +usleep(int n) { __coverity_sleep__(); } diff --git a/replacements.c b/replacements.c index 6066f9d6..469e73aa 100644 --- a/replacements.c +++ b/replacements.c @@ -109,7 +109,7 @@ replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 * console_print(L"Something has gone seriously wrong: %r\n", efi_status2); console_print(L"shim cannot continue, sorry.\n"); - msleep(5000000); + usleep(5000000); RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); @@ -137,7 +137,7 @@ exit_boot_services(EFI_HANDLE image_key, UINTN map_key) console_print(L"Bootloader has not verified loaded image.\n"); console_print(L"System is compromised. halting.\n"); - msleep(5000000); + usleep(5000000); RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); return EFI_SECURITY_VIOLATION; } @@ -162,7 +162,7 @@ do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, console_print(L"Something has gone seriously wrong: %r\n", efi_status2); console_print(L"shim cannot continue, sorry.\n"); - msleep(5000000); + usleep(5000000); RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); } diff --git a/shim.c b/shim.c index c31e97fb..17afcabb 100644 --- a/shim.c +++ b/shim.c @@ -1207,7 +1207,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) efi_status = start_image(image_handle, MOK_MANAGER); if (EFI_ERROR(efi_status)) { console_print(L"start_image() returned %r\n", efi_status); - msleep(2000000); + usleep(2000000); return efi_status; } @@ -1222,7 +1222,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) console_print( L"start_image() returned %r, falling back to default loader\n", efi_status); - msleep(2000000); + usleep(2000000); load_options = NULL; load_options_size = 0; efi_status = start_image(image_handle, DEFAULT_LOADER); @@ -1230,7 +1230,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) if (EFI_ERROR(efi_status)) { console_print(L"start_image() returned %r\n", efi_status); - msleep(2000000); + usleep(2000000); } return efi_status; @@ -1642,7 +1642,7 @@ devel_egress(devel_egress_action action UNUSED) console_print(L"Waiting to %a...", reasons[action]); for (size_t sleepcount = 0; sleepcount < 10; sleepcount++) { console_print(L"%d...", 10 - sleepcount); - msleep(1000000); + usleep(1000000); } console_print(L"\ndoing %a\n", action); @@ -1780,7 +1780,7 @@ die: #if defined(ENABLE_SHIM_DEVEL) devel_egress(COLD_RESET); #else - msleep(5000000); + usleep(5000000); RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); #endif @@ -1803,7 +1803,7 @@ die: */ if (user_insecure_mode) { console_print(L"Booting in insecure mode\n"); - msleep(2000000); + usleep(2000000); } /* -- cgit v1.2.3 From f132655f458035aa2ed6b9e3a5ae4c04b13c9311 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 28 Apr 2023 14:47:07 -0400 Subject: test: Make our fake dprintf be a statement. In a few places we put dprintf() at places where the compiler will get confused if it isn't a block or a statement. Obviously, it should be a statement, so this makes it one. Signed-off-by: Peter Jones --- include/console.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/console.h b/include/console.h index 8eb4b47f..7ac4e113 100644 --- a/include/console.h +++ b/include/console.h @@ -106,8 +106,8 @@ extern UINT32 verbose; dprint_(L"%a:%d:%a() " fmt, __FILE__, __LINE__ - 1, __func__, \ ##__VA_ARGS__) #else -#define dprint_(...) -#define dprint(fmt, ...) +#define dprint_(...) ({ ; }) +#define dprint(fmt, ...) ({ ; }) #endif extern EFI_STATUS EFIAPI vdprint_(const CHAR16 *fmt, const char *file, int line, -- cgit v1.2.3 From 996496065e9231dc51ca99b903615df8640bb797 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 28 Apr 2023 14:44:34 -0400 Subject: Split pe.c up even more. This moves the parts of pe.c that *don't* depend on Cryptlib into pe-relocate.c, so we can write test cases for them without having to make a second openssl build without EFI support. Signed-off-by: Peter Jones --- Makefile | 4 +- include/pe.h | 14 ++ pe-relocate.c | 506 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pe.c | 496 -------------------------------------------------------- 4 files changed, 522 insertions(+), 498 deletions(-) create mode 100644 pe-relocate.c (limited to 'include') diff --git a/Makefile b/Makefile index 61077bdf..ee8d37e2 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S +ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o diff --git a/include/pe.h b/include/pe.h index ccc8798b..9ea9eb44 100644 --- a/include/pe.h +++ b/include/pe.h @@ -21,6 +21,20 @@ EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_STATUS verify_sbat_section(char *SBATBase, size_t SBATSize); +EFI_STATUS +get_section_vma (UINTN section_num, + char *buffer, size_t bufsz UNUSED, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + char **basep, size_t *sizep, + EFI_IMAGE_SECTION_HEADER **sectionp); + +EFI_STATUS +get_section_vma_by_name (char *name, size_t namesz, + char *buffer, size_t bufsz, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + char **basep, size_t *sizep, + EFI_IMAGE_SECTION_HEADER **sectionp); + EFI_STATUS handle_image (void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, diff --git a/pe-relocate.c b/pe-relocate.c new file mode 100644 index 00000000..ee5a6153 --- /dev/null +++ b/pe-relocate.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * pe-relocate.c - our PE relocation/loading (but not verification) code + * Copyright Peter Jones + */ + +#include "shim.h" + +/* + * Perform basic bounds checking of the intra-image pointers + */ +void * +ImageAddress (void *image, uint64_t size, uint64_t address) +{ + /* ensure our local pointer isn't bigger than our size */ + if (address > size) + return NULL; + + /* Insure our math won't overflow */ + if (UINT64_MAX - address < (uint64_t)(intptr_t)image) + return NULL; + + /* return the absolute pointer */ + return image + address; +} + +/* + * Perform the actual relocation + */ +EFI_STATUS +relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, + EFI_IMAGE_SECTION_HEADER *Section, + void *orig, void *data) +{ + EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; + UINT64 Adjust; + UINT16 *Reloc, *RelocEnd; + char *Fixup, *FixupBase; + UINT16 *Fixup16; + UINT32 *Fixup32; + UINT64 *Fixup64; + int size = context->ImageSize; + void *ImageEnd = (char *)orig + size; + int n = 0; + + /* Alright, so here's how this works: + * + * context->RelocDir gives us two things: + * - the VA the table of base relocation blocks are (maybe) to be + * mapped at (RelocDir->VirtualAddress) + * - the virtual size (RelocDir->Size) + * + * The .reloc section (Section here) gives us some other things: + * - the name! kind of. (Section->Name) + * - the virtual size (Section->VirtualSize), which should be the same + * as RelocDir->Size + * - the virtual address (Section->VirtualAddress) + * - the file section size (Section->SizeOfRawData), which is + * a multiple of OptHdr->FileAlignment. Only useful for image + * validation, not really useful for iteration bounds. + * - the file address (Section->PointerToRawData) + * - a bunch of stuff we don't use that's 0 in our binaries usually + * - Flags (Section->Characteristics) + * + * and then the thing that's actually at the file address is an array + * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind + * them. The SizeOfBlock field of this structure includes the + * structure itself, and adding it to that structure's address will + * yield the next entry in the array. + */ + RelocBase = ImageAddress(orig, size, Section->PointerToRawData); + /* RelocBaseEnd here is the address of the first entry /past/ the + * table. */ + RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData + + context->RelocDir->Size); + + if (!RelocBase && !RelocBaseEnd) + return EFI_SUCCESS; + + if (!RelocBase || !RelocBaseEnd) { + perror(L"Reloc table overflows binary\n"); + return EFI_UNSUPPORTED; + } + + Adjust = (UINTN)data - context->ImageAddress; + + if (Adjust == 0) + return EFI_SUCCESS; + + while (RelocBase < RelocBaseEnd) { + Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); + + if (RelocBase->SizeOfBlock == 0) { + perror(L"Reloc %d block size 0 is invalid\n", n); + return EFI_UNSUPPORTED; + } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) { + perror(L"Reloc %d block size %d greater than reloc dir" + "size %d, which is invalid\n", n, + RelocBase->SizeOfBlock, + context->RelocDir->Size); + return EFI_UNSUPPORTED; + } + + RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); + if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) { + perror(L"Reloc %d entry overflows binary\n", n); + return EFI_UNSUPPORTED; + } + + FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); + if (!FixupBase) { + perror(L"Reloc %d Invalid fixupbase\n", n); + return EFI_UNSUPPORTED; + } + + while (Reloc < RelocEnd) { + Fixup = FixupBase + (*Reloc & 0xFFF); + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; + + case EFI_IMAGE_REL_BASED_HIGH: + Fixup16 = (UINT16 *) Fixup; + *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); + break; + + case EFI_IMAGE_REL_BASED_LOW: + Fixup16 = (UINT16 *) Fixup; + *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + Fixup32 = (UINT32 *) Fixup; + *Fixup32 = *Fixup32 + (UINT32) Adjust; + break; + + case EFI_IMAGE_REL_BASED_DIR64: + Fixup64 = (UINT64 *) Fixup; + *Fixup64 = *Fixup64 + (UINT64) Adjust; + break; + + default: + perror(L"Reloc %d Unknown relocation\n", n); + return EFI_UNSUPPORTED; + } + Reloc += 1; + } + RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; + n++; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +get_section_vma (UINTN section_num, + char *buffer, size_t bufsz UNUSED, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + char **basep, size_t *sizep, + EFI_IMAGE_SECTION_HEADER **sectionp) +{ + EFI_IMAGE_SECTION_HEADER *sections = context->FirstSection; + EFI_IMAGE_SECTION_HEADER *section; + char *base = NULL, *end = NULL; + + if (section_num >= context->NumberOfSections) + return EFI_NOT_FOUND; + + if (context->FirstSection == NULL) { + perror(L"Invalid section %d requested\n", section_num); + return EFI_UNSUPPORTED; + } + + section = §ions[section_num]; + + base = ImageAddress (buffer, context->ImageSize, section->VirtualAddress); + end = ImageAddress (buffer, context->ImageSize, + section->VirtualAddress + section->Misc.VirtualSize - 1); + + if (!(section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE)) { + if (!base) { + perror(L"Section %d has invalid base address\n", section_num); + return EFI_UNSUPPORTED; + } + if (!end) { + perror(L"Section %d has zero size\n", section_num); + return EFI_UNSUPPORTED; + } + } + + if (!(section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) && + (section->VirtualAddress < context->SizeOfHeaders || + section->PointerToRawData < context->SizeOfHeaders)) { + perror(L"Section %d is inside image headers\n", section_num); + return EFI_UNSUPPORTED; + } + + if (end < base) { + perror(L"Section %d has negative size\n", section_num); + return EFI_UNSUPPORTED; + } + + *basep = base; + *sizep = end - base; + *sectionp = section; + return EFI_SUCCESS; +} + +EFI_STATUS +get_section_vma_by_name (char *name, size_t namesz, + char *buffer, size_t bufsz, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + char **basep, size_t *sizep, + EFI_IMAGE_SECTION_HEADER **sectionp) +{ + UINTN i; + char namebuf[9]; + + if (!name || namesz == 0 || !buffer || bufsz < namesz || !context + || !basep || !sizep || !sectionp) + return EFI_INVALID_PARAMETER; + + /* + * This code currently is only used for ".reloc\0\0" and + * ".sbat\0\0\0", and it doesn't know how to look up longer section + * names. + */ + if (namesz > 8) + return EFI_UNSUPPORTED; + + SetMem(namebuf, sizeof(namebuf), 0); + CopyMem(namebuf, name, MIN(namesz, 8)); + + /* + * Copy the executable's sections to their desired offsets + */ + for (i = 0; i < context->NumberOfSections; i++) { + EFI_STATUS status; + EFI_IMAGE_SECTION_HEADER *section = NULL; + char *base = NULL; + size_t size = 0; + + status = get_section_vma(i, buffer, bufsz, context, &base, &size, §ion); + if (!EFI_ERROR(status)) { + if (CompareMem(section->Name, namebuf, 8) == 0) { + *basep = base; + *sizep = size; + *sectionp = section; + return EFI_SUCCESS; + } + continue; + } + + switch(status) { + case EFI_NOT_FOUND: + break; + } + } + + return EFI_NOT_FOUND; +} + +/* here's a chart: + * i686 x86_64 aarch64 + * 64-on-64: nyet yes yes + * 64-on-32: nyet yes nyet + * 32-on-32: yes yes no + */ +static int +allow_64_bit(void) +{ +#if defined(__x86_64__) || defined(__aarch64__) + return 1; +#elif defined(__i386__) || defined(__i686__) + /* Right now blindly assuming the kernel will correctly detect this + * and /halt the system/ if you're not really on a 64-bit cpu */ + if (in_protocol) + return 1; + return 0; +#else /* assuming everything else is 32-bit... */ + return 0; +#endif +} + +static int +allow_32_bit(void) +{ +#if defined(__x86_64__) +#if defined(ALLOW_32BIT_KERNEL_ON_X64) + if (in_protocol) + return 1; + return 0; +#else + return 0; +#endif +#elif defined(__i386__) || defined(__i686__) + return 1; +#elif defined(__aarch64__) + return 0; +#else /* assuming everything else is 32-bit... */ + return 1; +#endif +} + +static int +image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) +{ + /* .Magic is the same offset in all cases */ + if (PEHdr->Pe32Plus.OptionalHeader.Magic + == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) + return 1; + return 0; +} + +static const UINT16 machine_type = +#if defined(__x86_64__) + IMAGE_FILE_MACHINE_X64; +#elif defined(__aarch64__) + IMAGE_FILE_MACHINE_ARM64; +#elif defined(__arm__) + IMAGE_FILE_MACHINE_ARMTHUMB_MIXED; +#elif defined(__i386__) || defined(__i486__) || defined(__i686__) + IMAGE_FILE_MACHINE_I386; +#elif defined(__ia64__) + IMAGE_FILE_MACHINE_IA64; +#else +#error this architecture is not supported by shim +#endif + +static int +image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) +{ + /* If the machine type doesn't match the binary, bail, unless + * we're in an allowed 64-on-32 scenario */ + if (PEHdr->Pe32.FileHeader.Machine != machine_type) { + if (!(machine_type == IMAGE_FILE_MACHINE_I386 && + PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 && + allow_64_bit())) { + return 0; + } + } + + /* If it's not a header type we recognize at all, bail */ + switch (PEHdr->Pe32Plus.OptionalHeader.Magic) { + case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: + case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: + break; + default: + return 0; + } + + /* and now just check for general 64-vs-32 compatibility */ + if (image_is_64_bit(PEHdr)) { + if (allow_64_bit()) + return 1; + } else { + if (allow_32_bit()) + return 1; + } + return 0; +} + +/* + * Read the binary header and grab appropriate information from it + */ +EFI_STATUS +read_header(void *data, unsigned int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + EFI_IMAGE_DOS_HEADER *DosHdr = data; + EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; + unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; + unsigned long FileAlignment = 0; + UINT16 DllFlags; + + if (datasize < sizeof (PEHdr->Pe32)) { + perror(L"Invalid image\n"); + return EFI_UNSUPPORTED; + } + + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) + PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); + + if (!image_is_loadable(PEHdr)) { + perror(L"Platform does not support this image\n"); + return EFI_UNSUPPORTED; + } + + if (image_is_64_bit(PEHdr)) { + context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; + context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; + context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; + context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; + FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; + OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); + } else { + context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes; + context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders; + context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; + context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; + FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; + OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); + } + + if (FileAlignment % 2 != 0) { + perror(L"File Alignment is invalid (%d)\n", FileAlignment); + return EFI_UNSUPPORTED; + } + if (FileAlignment == 0) + FileAlignment = 0x200; + if (context->SectionAlignment == 0) + context->SectionAlignment = PAGE_SIZE; + if (context->SectionAlignment < FileAlignment) + context->SectionAlignment = FileAlignment; + + context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; + + if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) { + perror(L"Image header too large\n"); + return EFI_UNSUPPORTED; + } + + HeaderWithoutDataDir = OptHeaderSize + - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; + if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) != + context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { + perror(L"Image header overflows data directory\n"); + return EFI_UNSUPPORTED; + } + + SectionHeaderOffset = DosHdr->e_lfanew + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; + if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER + <= context->NumberOfSections) { + perror(L"Image sections overflow image size\n"); + return EFI_UNSUPPORTED; + } + + if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER + < (UINT32)context->NumberOfSections) { + perror(L"Image sections overflow section headers\n"); + return EFI_UNSUPPORTED; + } + + if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { + perror(L"Invalid image\n"); + return EFI_UNSUPPORTED; + } + + if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { + perror(L"Unsupported image type\n"); + return EFI_UNSUPPORTED; + } + + if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { + perror(L"Unsupported image - Relocations have been stripped\n"); + return EFI_UNSUPPORTED; + } + + context->PEHdr = PEHdr; + + if (image_is_64_bit(PEHdr)) { + context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; + context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; + context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; + } else { + context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; + context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; + context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; + } + + if ((mok_policy & MOK_POLICY_REQUIRE_NX) && + !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + perror(L"Policy requires NX, but image does not support NX\n"); + return EFI_UNSUPPORTED; + } + + context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); + + if (context->ImageSize < context->SizeOfHeaders) { + perror(L"Invalid image\n"); + return EFI_UNSUPPORTED; + } + + if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) > + (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) { + perror(L"Invalid image\n"); + return EFI_UNSUPPORTED; + } + + if (context->SecDir->VirtualAddress > datasize || + (context->SecDir->VirtualAddress == datasize && + context->SecDir->Size > 0)) { + perror(L"Malformed security header\n"); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/pe.c b/pe.c index 18f3e8fc..37753d23 100644 --- a/pe.c +++ b/pe.c @@ -21,152 +21,6 @@ #include -/* - * Perform basic bounds checking of the intra-image pointers - */ -void * -ImageAddress (void *image, uint64_t size, uint64_t address) -{ - /* ensure our local pointer isn't bigger than our size */ - if (address > size) - return NULL; - - /* Insure our math won't overflow */ - if (UINT64_MAX - address < (uint64_t)(intptr_t)image) - return NULL; - - /* return the absolute pointer */ - return image + address; -} - -/* - * Perform the actual relocation - */ -EFI_STATUS -relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, - EFI_IMAGE_SECTION_HEADER *Section, - void *orig, void *data) -{ - EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; - UINT64 Adjust; - UINT16 *Reloc, *RelocEnd; - char *Fixup, *FixupBase; - UINT16 *Fixup16; - UINT32 *Fixup32; - UINT64 *Fixup64; - int size = context->ImageSize; - void *ImageEnd = (char *)orig + size; - int n = 0; - - /* Alright, so here's how this works: - * - * context->RelocDir gives us two things: - * - the VA the table of base relocation blocks are (maybe) to be - * mapped at (RelocDir->VirtualAddress) - * - the virtual size (RelocDir->Size) - * - * The .reloc section (Section here) gives us some other things: - * - the name! kind of. (Section->Name) - * - the virtual size (Section->VirtualSize), which should be the same - * as RelocDir->Size - * - the virtual address (Section->VirtualAddress) - * - the file section size (Section->SizeOfRawData), which is - * a multiple of OptHdr->FileAlignment. Only useful for image - * validation, not really useful for iteration bounds. - * - the file address (Section->PointerToRawData) - * - a bunch of stuff we don't use that's 0 in our binaries usually - * - Flags (Section->Characteristics) - * - * and then the thing that's actually at the file address is an array - * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind - * them. The SizeOfBlock field of this structure includes the - * structure itself, and adding it to that structure's address will - * yield the next entry in the array. - */ - RelocBase = ImageAddress(orig, size, Section->PointerToRawData); - /* RelocBaseEnd here is the address of the first entry /past/ the - * table. */ - RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData + - context->RelocDir->Size); - - if (!RelocBase && !RelocBaseEnd) - return EFI_SUCCESS; - - if (!RelocBase || !RelocBaseEnd) { - perror(L"Reloc table overflows binary\n"); - return EFI_UNSUPPORTED; - } - - Adjust = (UINTN)data - context->ImageAddress; - - if (Adjust == 0) - return EFI_SUCCESS; - - while (RelocBase < RelocBaseEnd) { - Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); - - if (RelocBase->SizeOfBlock == 0) { - perror(L"Reloc %d block size 0 is invalid\n", n); - return EFI_UNSUPPORTED; - } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) { - perror(L"Reloc %d block size %d greater than reloc dir" - "size %d, which is invalid\n", n, - RelocBase->SizeOfBlock, - context->RelocDir->Size); - return EFI_UNSUPPORTED; - } - - RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); - if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) { - perror(L"Reloc %d entry overflows binary\n", n); - return EFI_UNSUPPORTED; - } - - FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); - if (!FixupBase) { - perror(L"Reloc %d Invalid fixupbase\n", n); - return EFI_UNSUPPORTED; - } - - while (Reloc < RelocEnd) { - Fixup = FixupBase + (*Reloc & 0xFFF); - switch ((*Reloc) >> 12) { - case EFI_IMAGE_REL_BASED_ABSOLUTE: - break; - - case EFI_IMAGE_REL_BASED_HIGH: - Fixup16 = (UINT16 *) Fixup; - *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); - break; - - case EFI_IMAGE_REL_BASED_LOW: - Fixup16 = (UINT16 *) Fixup; - *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); - break; - - case EFI_IMAGE_REL_BASED_HIGHLOW: - Fixup32 = (UINT32 *) Fixup; - *Fixup32 = *Fixup32 + (UINT32) Adjust; - break; - - case EFI_IMAGE_REL_BASED_DIR64: - Fixup64 = (UINT64 *) Fixup; - *Fixup64 = *Fixup64 + (UINT64) Adjust; - break; - - default: - perror(L"Reloc %d Unknown relocation\n", n); - return EFI_UNSUPPORTED; - } - Reloc += 1; - } - RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; - n++; - } - - return EFI_SUCCESS; -} - #define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \ if ((unsigned long)hashbase > \ (unsigned long)data + datasize_in) { \ @@ -185,113 +39,6 @@ relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, }) #define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__) -EFI_STATUS -get_section_vma (UINTN section_num, - char *buffer, size_t bufsz UNUSED, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - char **basep, size_t *sizep, - EFI_IMAGE_SECTION_HEADER **sectionp) -{ - EFI_IMAGE_SECTION_HEADER *sections = context->FirstSection; - EFI_IMAGE_SECTION_HEADER *section; - char *base = NULL, *end = NULL; - - if (section_num >= context->NumberOfSections) - return EFI_NOT_FOUND; - - if (context->FirstSection == NULL) { - perror(L"Invalid section %d requested\n", section_num); - return EFI_UNSUPPORTED; - } - - section = §ions[section_num]; - - base = ImageAddress (buffer, context->ImageSize, section->VirtualAddress); - end = ImageAddress (buffer, context->ImageSize, - section->VirtualAddress + section->Misc.VirtualSize - 1); - - if (!(section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE)) { - if (!base) { - perror(L"Section %d has invalid base address\n", section_num); - return EFI_UNSUPPORTED; - } - if (!end) { - perror(L"Section %d has zero size\n", section_num); - return EFI_UNSUPPORTED; - } - } - - if (!(section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) && - (section->VirtualAddress < context->SizeOfHeaders || - section->PointerToRawData < context->SizeOfHeaders)) { - perror(L"Section %d is inside image headers\n", section_num); - return EFI_UNSUPPORTED; - } - - if (end < base) { - perror(L"Section %d has negative size\n", section_num); - return EFI_UNSUPPORTED; - } - - *basep = base; - *sizep = end - base; - *sectionp = section; - return EFI_SUCCESS; -} - -EFI_STATUS -get_section_vma_by_name (char *name, size_t namesz, - char *buffer, size_t bufsz, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - char **basep, size_t *sizep, - EFI_IMAGE_SECTION_HEADER **sectionp) -{ - UINTN i; - char namebuf[9]; - - if (!name || namesz == 0 || !buffer || bufsz < namesz || !context - || !basep || !sizep || !sectionp) - return EFI_INVALID_PARAMETER; - - /* - * This code currently is only used for ".reloc\0\0" and - * ".sbat\0\0\0", and it doesn't know how to look up longer section - * names. - */ - if (namesz > 8) - return EFI_UNSUPPORTED; - - SetMem(namebuf, sizeof(namebuf), 0); - CopyMem(namebuf, name, MIN(namesz, 8)); - - /* - * Copy the executable's sections to their desired offsets - */ - for (i = 0; i < context->NumberOfSections; i++) { - EFI_STATUS status; - EFI_IMAGE_SECTION_HEADER *section = NULL; - char *base = NULL; - size_t size = 0; - - status = get_section_vma(i, buffer, bufsz, context, &base, &size, §ion); - if (!EFI_ERROR(status)) { - if (CompareMem(section->Name, namebuf, 8) == 0) { - *basep = base; - *sizep = size; - *sectionp = section; - return EFI_SUCCESS; - } - continue; - } - - switch(status) { - case EFI_NOT_FOUND: - break; - } - } - - return EFI_NOT_FOUND; -} /* * Calculate the SHA1 and SHA256 hashes of a binary @@ -585,249 +332,6 @@ done: return efi_status; } -/* here's a chart: - * i686 x86_64 aarch64 - * 64-on-64: nyet yes yes - * 64-on-32: nyet yes nyet - * 32-on-32: yes yes no - */ -static int -allow_64_bit(void) -{ -#if defined(__x86_64__) || defined(__aarch64__) - return 1; -#elif defined(__i386__) || defined(__i686__) - /* Right now blindly assuming the kernel will correctly detect this - * and /halt the system/ if you're not really on a 64-bit cpu */ - if (in_protocol) - return 1; - return 0; -#else /* assuming everything else is 32-bit... */ - return 0; -#endif -} - -static int -allow_32_bit(void) -{ -#if defined(__x86_64__) -#if defined(ALLOW_32BIT_KERNEL_ON_X64) - if (in_protocol) - return 1; - return 0; -#else - return 0; -#endif -#elif defined(__i386__) || defined(__i686__) - return 1; -#elif defined(__aarch64__) - return 0; -#else /* assuming everything else is 32-bit... */ - return 1; -#endif -} - -static int -image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) -{ - /* .Magic is the same offset in all cases */ - if (PEHdr->Pe32Plus.OptionalHeader.Magic - == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) - return 1; - return 0; -} - -static const UINT16 machine_type = -#if defined(__x86_64__) - IMAGE_FILE_MACHINE_X64; -#elif defined(__aarch64__) - IMAGE_FILE_MACHINE_ARM64; -#elif defined(__arm__) - IMAGE_FILE_MACHINE_ARMTHUMB_MIXED; -#elif defined(__i386__) || defined(__i486__) || defined(__i686__) - IMAGE_FILE_MACHINE_I386; -#elif defined(__ia64__) - IMAGE_FILE_MACHINE_IA64; -#else -#error this architecture is not supported by shim -#endif - -static int -image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) -{ - /* If the machine type doesn't match the binary, bail, unless - * we're in an allowed 64-on-32 scenario */ - if (PEHdr->Pe32.FileHeader.Machine != machine_type) { - if (!(machine_type == IMAGE_FILE_MACHINE_I386 && - PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 && - allow_64_bit())) { - return 0; - } - } - - /* If it's not a header type we recognize at all, bail */ - switch (PEHdr->Pe32Plus.OptionalHeader.Magic) { - case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: - case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: - break; - default: - return 0; - } - - /* and now just check for general 64-vs-32 compatibility */ - if (image_is_64_bit(PEHdr)) { - if (allow_64_bit()) - return 1; - } else { - if (allow_32_bit()) - return 1; - } - return 0; -} - -/* - * Read the binary header and grab appropriate information from it - */ -EFI_STATUS -read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) -{ - EFI_IMAGE_DOS_HEADER *DosHdr = data; - EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; - unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; - unsigned long FileAlignment = 0; - UINT16 DllFlags; - - if (datasize < sizeof (PEHdr->Pe32)) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) - PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); - - if (!image_is_loadable(PEHdr)) { - perror(L"Platform does not support this image\n"); - return EFI_UNSUPPORTED; - } - - if (image_is_64_bit(PEHdr)) { - context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; - context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; - context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; - context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; - FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; - OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); - } else { - context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes; - context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders; - context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; - context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; - FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; - OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); - } - - if (FileAlignment % 2 != 0) { - perror(L"File Alignment is invalid (%d)\n", FileAlignment); - return EFI_UNSUPPORTED; - } - if (FileAlignment == 0) - FileAlignment = 0x200; - if (context->SectionAlignment == 0) - context->SectionAlignment = PAGE_SIZE; - if (context->SectionAlignment < FileAlignment) - context->SectionAlignment = FileAlignment; - - context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; - - if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) { - perror(L"Image header too large\n"); - return EFI_UNSUPPORTED; - } - - HeaderWithoutDataDir = OptHeaderSize - - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; - if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) != - context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { - perror(L"Image header overflows data directory\n"); - return EFI_UNSUPPORTED; - } - - SectionHeaderOffset = DosHdr->e_lfanew - + sizeof (UINT32) - + sizeof (EFI_IMAGE_FILE_HEADER) - + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; - if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER - <= context->NumberOfSections) { - perror(L"Image sections overflow image size\n"); - return EFI_UNSUPPORTED; - } - - if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER - < (UINT32)context->NumberOfSections) { - perror(L"Image sections overflow section headers\n"); - return EFI_UNSUPPORTED; - } - - if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { - perror(L"Unsupported image type\n"); - return EFI_UNSUPPORTED; - } - - if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { - perror(L"Unsupported image - Relocations have been stripped\n"); - return EFI_UNSUPPORTED; - } - - context->PEHdr = PEHdr; - - if (image_is_64_bit(PEHdr)) { - context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; - context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; - context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; - context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; - } else { - context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; - context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; - context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; - context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; - } - - if ((mok_policy & MOK_POLICY_REQUIRE_NX) && - !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { - perror(L"Policy requires NX, but image does not support NX\n"); - return EFI_UNSUPPORTED; - } - - context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); - - if (context->ImageSize < context->SizeOfHeaders) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) > - (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) { - perror(L"Invalid image\n"); - return EFI_UNSUPPORTED; - } - - if (context->SecDir->VirtualAddress > datasize || - (context->SecDir->VirtualAddress == datasize && - context->SecDir->Size > 0)) { - perror(L"Malformed security header\n"); - return EFI_INVALID_PARAMETER; - } - return EFI_SUCCESS; -} - EFI_STATUS verify_sbat_section(char *SBATBase, size_t SBATSize) { -- cgit v1.2.3 From 569270d8603d68308ad8bf8ef4cad4b09101d35e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 28 Apr 2023 11:55:10 -0400 Subject: Test (and fix) ImageAddress() This adds a test case for our address sanitation checking function ImageAddresS(). In doing so it addresses two issues: - previously we allowed the address after the last byte of the image to be computed (may need to revert this or fix some callers, we'll see...) - bespoke overflow checking and using + directly instead of using __builtin_add_overflow() Signed-off-by: Peter Jones --- include/test.mk | 3 +++ pe-relocate.c | 9 ++++++--- test-data/.gitignore | 1 + test-data/grubx64.0.76.el7.1.efi | Bin 0 -> 1168848 bytes test-data/grubx64.0.76.el7.efi | Bin 0 -> 1091024 bytes test-data/grubx64.0.80.el7.efi | Bin 0 -> 1168848 bytes test-pe-relocate.c | 39 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 test-data/.gitignore create mode 100755 test-data/grubx64.0.76.el7.1.efi create mode 100755 test-data/grubx64.0.76.el7.efi create mode 100755 test-data/grubx64.0.80.el7.efi create mode 100644 test-pe-relocate.c (limited to 'include') diff --git a/include/test.mk b/include/test.mk index c37b8446..e6d46594 100644 --- a/include/test.mk +++ b/include/test.mk @@ -95,6 +95,9 @@ test-mok-mirror: CFLAGS+=-DHAVE_START_IMAGE -DHAVE_SHIM_LOCK_GUID test-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S mock-variables.c test-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID +test-pe-relocate_FILES = globals.c +test-pe-relocate :: CFLAGS+=-DHAVE_SHIM_LOCK_GUID + test-str_FILES = lib/string.c tests := $(patsubst %.c,%,$(wildcard test-*.c)) diff --git a/pe-relocate.c b/pe-relocate.c index ee5a6153..64011146 100644 --- a/pe-relocate.c +++ b/pe-relocate.c @@ -12,16 +12,19 @@ void * ImageAddress (void *image, uint64_t size, uint64_t address) { + uintptr_t img_addr; + /* ensure our local pointer isn't bigger than our size */ - if (address > size) + if (address >= size) return NULL; /* Insure our math won't overflow */ - if (UINT64_MAX - address < (uint64_t)(intptr_t)image) + img_addr = (uintptr_t)image; + if (__builtin_add_overflow(img_addr, address, &img_addr)) return NULL; /* return the absolute pointer */ - return image + address; + return (void *)img_addr; } /* diff --git a/test-data/.gitignore b/test-data/.gitignore new file mode 100644 index 00000000..bbde87b1 --- /dev/null +++ b/test-data/.gitignore @@ -0,0 +1 @@ +!/*.efi diff --git a/test-data/grubx64.0.76.el7.1.efi b/test-data/grubx64.0.76.el7.1.efi new file mode 100755 index 00000000..29f6812a Binary files /dev/null and b/test-data/grubx64.0.76.el7.1.efi differ diff --git a/test-data/grubx64.0.76.el7.efi b/test-data/grubx64.0.76.el7.efi new file mode 100755 index 00000000..5a198f6e Binary files /dev/null and b/test-data/grubx64.0.76.el7.efi differ diff --git a/test-data/grubx64.0.80.el7.efi b/test-data/grubx64.0.80.el7.efi new file mode 100755 index 00000000..c5f28d90 Binary files /dev/null and b/test-data/grubx64.0.80.el7.efi differ diff --git a/test-pe-relocate.c b/test-pe-relocate.c new file mode 100644 index 00000000..55577791 --- /dev/null +++ b/test-pe-relocate.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-pe-reloc.c - attempt to test relocate_coff() + * Copyright Peter Jones + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +static int +test_image_address(void) +{ + char image[4]; + void *ret; + + assert_equal_return(ImageAddress(image, sizeof(image), 0), &image[0], -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress(image, sizeof(image), 4), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)1, 2, 3), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)-1ull, UINT64_MAX, UINT64_MAX), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)0, UINT64_MAX, UINT64_MAX), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)1, UINT64_MAX, UINT64_MAX), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)2, UINT64_MAX, UINT64_MAX), NULL, -1, "got %p expected %p\n"); + assert_equal_return(ImageAddress((void *)3, UINT64_MAX, UINT64_MAX), NULL, -1, "got %p expected %p\n"); + + return 0; +} + +int +main(void) +{ + int status = 0; + test(test_image_address); + + return status; +} + +// vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 1578b55a70bd2204cb0ad6db18ae4c003494df81 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 28 Jun 2023 15:15:14 -0400 Subject: Add libFuzzer support for csv.c shim takes several forms of input from several sources that are not necessarily trustworthy. As such, we need to take measures to validate that we don't have unacceptable results from bad inputs. One such measure is "fuzzing" the inputs which parse untrusted data by running them with randomized or partially randomized input. This change adds such testing using clang's "libFuzzer" to our CSV parser. I've run this on 24-cores at 4GHz for half an hour, and so far each fuzzer has converged on 79% coverage. I expect the 21% that's not getting covered are the EFI API mock interfaces we're building in from test.c and similar. So far no errors have been found, which is what was expected since this particular API is being manually fuzzed with ~8kB of /dev/urandom on every build since 2021-02-23. Signed-off-by: Peter Jones --- .gitignore | 4 +++ Makefile | 20 ++++++++++-- fuzz-csv.c | 71 ++++++++++++++++++++++++++++++++++++++++++ include/fuzz.mk | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 fuzz-csv.c create mode 100644 include/fuzz.mk (limited to 'include') diff --git a/.gitignore b/.gitignore index 594090e1..9085e5a7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,10 @@ Make.local /compile_commands.json /compile_commands.events.json /cov-int/ +/crash-* +/fuzz-* +!/fuzz-*.c +/leak-* /post-process-pe /random.bin /sbat.*.csv diff --git a/Makefile b/Makefile index ee8d37e2..8a33fa7e 100644 --- a/Makefile +++ b/Makefile @@ -290,6 +290,15 @@ else $(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f endif +fuzz fuzz-clean fuzz-coverage fuzz-lto : + @make -f $(TOPDIR)/include/fuzz.mk \ + COMPILER="$(COMPILER)" \ + CROSS_COMPILE="$(CROSS_COMPILE)" \ + CLANG_WARNINGS="$(CLANG_WARNINGS)" \ + ARCH_DEFINES="$(ARCH_DEFINES)" \ + EFI_INCLUDES="$(EFI_INCLUDES)" \ + fuzz-clean $@ + test test-clean test-coverage test-lto : @make -f $(TOPDIR)/include/test.mk \ COMPILER="$(COMPILER)" \ @@ -299,14 +308,21 @@ test test-clean test-coverage test-lto : EFI_INCLUDES="$(EFI_INCLUDES)" \ test-clean $@ +$(patsubst %.c,%,$(wildcard fuzz-*.c)) : + @make -f $(TOPDIR)/include/fuzz.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@ + $(patsubst %.c,%,$(wildcard test-*.c)) : @make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@ -.PHONY : $(patsubst %.c,%,$(wildcard test-*.c)) test +clean-fuzz-objs: + @make -f $(TOPDIR)/include/fuzz.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" clean clean-test-objs: @make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" clean +.PHONY : $(patsubst %.c,%,$(wildcard fuzz-*.c)) fuzz +.PHONY : $(patsubst %.c,%,$(wildcard test-*.c)) test + clean-gnu-efi: @if [ -d gnu-efi ] ; then \ $(MAKE) -C gnu-efi \ @@ -340,7 +356,7 @@ clean-cryptlib-objs: $(MAKE) -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile clean ; \ fi -clean: clean-shim-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs +clean: clean-shim-objs clean-fuzz-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs GITTAG = $(VERSION) diff --git a/fuzz-csv.c b/fuzz-csv.c new file mode 100644 index 00000000..bc701df0 --- /dev/null +++ b/fuzz-csv.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * test-csv.c - test our csv parser + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +#include + +int +test_csv_simple_fuzz(char *random_bin, size_t random_bin_len) +{ + list_t entry_list; + size_t i; + char *current, *end; + list_t *pos = NULL; + EFI_STATUS efi_status; + + INIT_LIST_HEAD(&entry_list); + + current = &random_bin[0]; + current = current + 1 - 1; + end = current + random_bin_len - 1; + *end = '\0'; + + efi_status = parse_csv_data(current, end, 7, &entry_list); + if (efi_status != EFI_SUCCESS) + return 0; + if (list_size(&entry_list) <= 1) + goto fail; + + i = 0; + list_for_each(pos, &entry_list) { + struct csv_row *csv_row; + + csv_row = list_entry(pos, struct csv_row, list); + i++; + } + + free_csv_list(&entry_list); + + return 0; +fail: + free_csv_list(&entry_list); + return -1; +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int rc; + uint8_t *data_copy; + + if (size < 1) + return 0; + + data_copy = malloc(size); + if (!data_copy) + return -1; + + memcpy(data_copy, data, size); + rc = test_csv_simple_fuzz((char *)data_copy, size); + free(data_copy); + + return rc; // Values other than 0 and -1 are reserved for future use. +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/include/fuzz.mk b/include/fuzz.mk new file mode 100644 index 00000000..5289c6af --- /dev/null +++ b/include/fuzz.mk @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# fuzz.mk - makefile to fuzz local test programs +# + +.SUFFIXES: + +include Make.defaults + +CC = clang +VALGRIND ?= +DEBUG_PRINTS ?= 0 +OPTIMIZATIONS ?= -Og -ggdb +FUZZ_ARGS ?= +CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ + -isystem $(TOPDIR)/include/system \ + $(EFI_INCLUDES) \ + -Iinclude -iquote . \ + -isystem /usr/include \ + -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \ + $(ARCH_CFLAGS) \ + -fsanitize=fuzzer,address \ + -fshort-wchar \ + -fno-builtin \ + -rdynamic \ + -fno-inline \ + -fno-eliminate-unused-debug-types \ + -fno-eliminate-unused-debug-symbols \ + -gpubnames \ + -grecord-gcc-switches \ + $(if $(findstring clang,$(CC)),-Wno-unknown-warning-option) \ + $(DEFAULT_WARNFLAGS) \ + -Wsign-compare \ + -Wno-deprecated-declarations \ + $(if $(findstring gcc,$(CC)),-Wno-unused-but-set-variable) \ + -Wno-unused-but-set-variable \ + -Wno-unused-variable \ + -Wno-pointer-sign \ + $(DEFAULT_WERRFLAGS) \ + -Werror=nonnull \ + $(shell $(CC) -Werror=nonnull-compare -E -x c /dev/null >/dev/null 2>&1 && echo -Werror=nonnull-compare) \ + $(ARCH_DEFINES) \ + -DEFI_FUNCTION_WRAPPER \ + -DGNU_EFI_USE_MS_ABI -DPAGE_SIZE=4096 \ + -DSHIM_UNIT_TEST \ + -DSHIM_ENABLE_LIBFUZZER \ + "-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)" + +# On some systems (e.g. Arch Linux), limits.h is in the "include-fixed" instead +# of the "include" directory +CFLAGS += -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include-fixed) + +# And on Debian also check the multi-arch include path +CFLAGS += -isystem /usr/include/$(shell $(CC) $(ARCH_CFLAGS) -print-multiarch) + +libefi-test.a : + $(MAKE) -C gnu-efi \ + COMPILER="$(COMPILER)" \ + CC="$(CC)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ + -f $(TOPDIR)/gnu-efi/Makefile \ + clean lib + mv gnu-efi/$(ARCH)/lib/libefi.a $@ + $(MAKE) -C gnu-efi \ + COMPILER="$(COMPILER)" \ + ARCH=$(ARCH_GNUEFI) \ + TOPDIR=$(TOPDIR)/gnu-efi \ + -f $(TOPDIR)/gnu-efi/Makefile \ + clean + + +fuzzers := $(patsubst %.c,%,$(wildcard fuzz-*.c)) + +$(fuzzers) :: fuzz-% : | libefi-test.a + +$(fuzzers) :: fuzz-% : test.c fuzz-%.c $(fuzz-%_FILES) + $(CC) $(CFLAGS) -o $@ $(sort $^ $(wildcard $*.c) $(fuzz-$*_FILES)) libefi-test.a -lefivar + $(VALGRIND) ./$@ -max_len=4096 -jobs=24 $(FUZZ_ARGS) + +fuzz : $(fuzzers) + $(MAKE) -f include/fuzz.mk fuzz-clean + +fuzz-clean : + @rm -vf random.bin libefi-test.a + @rm -vf vgcore.* fuzz*.log + +clean : fuzz-clean + +all : fuzz-clean fuzz + +.PHONY: $(fuzzers) all fuzz clean +.SECONDARY: random.bin + +# vim:ft=make -- cgit v1.2.3 From e24681211ce8a71583b5233084bd1290c3c7e872 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 28 Jun 2023 16:52:22 -0400 Subject: Add libFuzzer support to the .sbat parser. shim takes several forms of input from several sources that are not necessarily trustworthy. As such, we need to take measures to validate that we don't have unacceptable results from bad inputs. One such measure is "fuzzing" the inputs which parse untrusted data by running them with randomized or partially randomized input. This change adds such testing using clang's "libFuzzer" to our parser for ".sbat" sections. I've run it for about half an hour and so far it found one memory leak, but no other errors. Signed-off-by: Peter Jones --- fuzz-sbat.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ include/fuzz.mk | 2 ++ 2 files changed, 48 insertions(+) create mode 100644 fuzz-sbat.c (limited to 'include') diff --git a/fuzz-sbat.c b/fuzz-sbat.c new file mode 100644 index 00000000..74d313bb --- /dev/null +++ b/fuzz-sbat.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * fuzz-sbat-section.c - fuzz our .sbat parsing code + * Copyright Peter Jones + */ + +#ifndef SHIM_UNIT_TEST +#define SHIM_UNIT_TEST +#endif +#include "shim.h" + +#include + +list_t sbat_var; + +BOOLEAN +secure_mode() { + return 1; +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint8_t *data_copy; + EFI_STATUS status = 0; + size_t n = 0; + struct sbat_section_entry **entries = NULL; + + if (size < 1) + return 0; + + data_copy = malloc(size+1); + if (!data_copy) + return -1; + + memcpy(data_copy, data, size); + data_copy[size] = 0; + status = parse_sbat_section(data_copy, size, &n, &entries); + cleanup_sbat_section_entries(n, entries); + + free(data_copy); + + return 0; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/include/fuzz.mk b/include/fuzz.mk index 5289c6af..f35df415 100644 --- a/include/fuzz.mk +++ b/include/fuzz.mk @@ -69,6 +69,8 @@ libefi-test.a : -f $(TOPDIR)/gnu-efi/Makefile \ clean +fuzz-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S mock-variables.c +fuzz-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID fuzzers := $(patsubst %.c,%,$(wildcard fuzz-*.c)) -- cgit v1.2.3 From 04111d41f03b6de060209d417f5d18cce3ab2ea2 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 22 Aug 2023 12:41:13 -0400 Subject: Make some of the static analysis tools a little easier to run With "gcc -fanalyzer" and "scan-build", it's convenient to be able to continue even though the compiler has returned error on one or more source files. This makes it so compiler errors are ignored in some of those cases. Signed-off-by: Peter Jones --- Make.rules | 2 +- Makefile | 8 ++++---- include/fanalyzer.mk | 1 + include/scan-build.mk | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Make.rules b/Make.rules index 7c6ec6c0..96a8649a 100644 --- a/Make.rules +++ b/Make.rules @@ -36,6 +36,6 @@ $(strip $(foreach x,$(DEFAULT_$(1)), endef %.o : %.S - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) -c -o $@ $< $(IGNORE_COMPILER_ERRORS) # vim:filetype=make diff --git a/Makefile b/Makefile index 8a33fa7e..aaa927d2 100644 --- a/Makefile +++ b/Makefile @@ -160,19 +160,19 @@ gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: ARCH=$(ARCH_GNUEFI) \ TOPDIR=$(TOPDIR)/gnu-efi \ -f $(TOPDIR)/gnu-efi/Makefile \ - lib gnuefi inc + lib gnuefi inc $(IGNORE_COMPILER_ERRORS) Cryptlib/libcryptlib.a: for i in Hash Hmac Cipher Rand Pk Pem SysCall; do mkdir -p Cryptlib/$$i; done - $(MAKE) TOPDIR=$(TOPDIR) VPATH=$(TOPDIR)/Cryptlib -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile + $(MAKE) TOPDIR=$(TOPDIR) VPATH=$(TOPDIR)/Cryptlib -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile $(IGNORE_COMPILER_ERRORS) Cryptlib/OpenSSL/libopenssl.a: for i in x509v3 x509 txt_db stack sha rsa rc4 rand pkcs7 pkcs12 pem ocsp objects modes md5 lhash kdf hmac evp err dso dh conf comp cmac buffer bn bio async/arch asn1 aes; do mkdir -p Cryptlib/OpenSSL/crypto/$$i; done - $(MAKE) TOPDIR=$(TOPDIR) VPATH=$(TOPDIR)/Cryptlib/OpenSSL -C Cryptlib/OpenSSL -f $(TOPDIR)/Cryptlib/OpenSSL/Makefile + $(MAKE) TOPDIR=$(TOPDIR) VPATH=$(TOPDIR)/Cryptlib/OpenSSL -C Cryptlib/OpenSSL -f $(TOPDIR)/Cryptlib/OpenSSL/Makefile $(IGNORE_COMPILER_ERRORS) lib/lib.a: | $(TOPDIR)/lib/Makefile $(wildcard $(TOPDIR)/include/*.[ch]) mkdir -p lib - $(MAKE) VPATH=$(TOPDIR)/lib TOPDIR=$(TOPDIR) -C lib -f $(TOPDIR)/lib/Makefile + $(MAKE) VPATH=$(TOPDIR)/lib TOPDIR=$(TOPDIR) -C lib -f $(TOPDIR)/lib/Makefile $(IGNORE_COMPILER_ERRORS) post-process-pe : $(TOPDIR)/post-process-pe.c $(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $< diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk index e0bf4d75..a0679e3e 100644 --- a/include/fanalyzer.mk +++ b/include/fanalyzer.mk @@ -21,6 +21,7 @@ fanalyzer-build-all : COMPILER=gcc fanalyzer-build-all : CCACHE_DISABLE=1 fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference +fanalyzer-build-all : IGNORE_COMPILER_ERRORS=" || :" fanalyzer-build-all : all fanalyzer-no-openssl : | fanalyzer-test diff --git a/include/scan-build.mk b/include/scan-build.mk index 3ed7660e..170ba836 100644 --- a/include/scan-build.mk +++ b/include/scan-build.mk @@ -22,6 +22,7 @@ scan-build-unchecked-openssl : Cryptlib/OpenSSL/libopenssl.a scan-build-all : CCACHE_DISABLE=1 scan-build-all : COMPILER=clang +scan-build-all : IGNORE_COMPILER_ERRORS=" || :" scan-build-all : | scan-test scan-build-all : +scan-build -o scan-results make $(MAKEARGS) $(DASHJ) CCACHE_DISABLE=1 all -- cgit v1.2.3 From f27182695d88350b48c8b9a6dce54bb513d7aa4e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 27 Jul 2023 15:13:08 -0400 Subject: Add primitives for overflow-checked arithmetic operations. We need to do arithmetic on untrusted values sometimes, so this patch adds the following primitives as macros that wrap the compiler builtins. bool checked_add(TYPE addend0, TYPE addend1, TYPE *sum) bool checked_sub(TYPE minuend, TYPE subtrahend, TYPE *difference) bool checked_mul(TYPE factor0, TYPE factor1, TYPE *product) And also the following primitive which returns True if divisor is 0 and False otherwise: bool checked_div(TYPE dividend, TYPE divisor, TYPE *quotient) Signed-off-by: Peter Jones --- include/compiler.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index b0d595f3..545a72e5 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -198,5 +198,21 @@ #error shim has no cache_invalidate() implementation for this compiler #endif /* __GNUC__ */ +#define checked_add(addend0, addend1, sum) \ + __builtin_add_overflow(addend0, addend1, sum) +#define checked_sub(minuend, subtrahend, difference) \ + __builtin_sub_overflow(minuend, subtrahend, difference) +#define checked_mul(factor0, factor1, product) \ + __builtin_mul_overflow(factor0, factor1, product) +#define checked_div(dividend, divisor, quotient) \ + ({ \ + bool _ret = True; \ + if ((divisor) != 0) { \ + _ret = False; \ + (quotient) = (dividend) / (divisor); \ + } \ + _ret; \ + }) + #endif /* !COMPILER_H_ */ // vim:fenc=utf-8:tw=75:et -- cgit v1.2.3 From ea0f9dfe8ae49ead3204be4c3166b08cc96fad7e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Wed, 9 Nov 2022 19:37:53 -0800 Subject: Allow SbatLevel data from external binary Ingest SBAT Levels from revocations binary thereby allowing level requirements to be updated independently from shipping a new shim. Do not automatically apply any revocations from a stock shim at this point. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 4 ++- include/sbat_var_defs.h | 17 ++++----- sbat.c | 90 ++++++++++++++++++++++++++-------------------- shim.c | 96 ++++++++++++++++++++++++++++++++++++++++++------- test-sbat.c | 3 +- 5 files changed, 148 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index c94c4fba..84f5ef01 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -34,6 +34,7 @@ #define SBAT_POLICY_LATEST 1 #define SBAT_POLICY_PREVIOUS 2 #define SBAT_POLICY_RESET 3 +#define SBAT_POLICY_NOTREAD 255 extern UINTN _sbat, _esbat; @@ -52,7 +53,8 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); -EFI_STATUS set_sbat_uefi_variable(void); +EFI_STATUS set_sbat_uefi_variable_internal(void); +EFI_STATUS set_sbat_uefi_variable(char *, char *); bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes, char *sbar_var); diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 5b1a764f..2ea98e4e 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -13,11 +13,9 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" #if defined(ENABLE_SHIM_DEVEL) -#define SBAT_VAR_PREVIOUS_DATE "2022020101" -#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" +#define SBAT_VAR_PREVIOUS_DATE "2021030218" #define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" @@ -26,14 +24,13 @@ SBAT_VAR_LATEST_REVOCATIONS #else /* !ENABLE_SHIM_DEVEL */ /* - * As of 2022-11-16, most folks (including Ubuntu, SUSE, openSUSE) don't have - * a "shim,2" yet, so adding that here would end up unbootable. + * At this point we do not want shim to automatically apply a + * previous revocation unless it is delivered by a separately + * installed signed revocations binary. */ -#define SBAT_VAR_PREVIOUS_DATE "2022052400" -#define SBAT_VAR_PREVIOUS_REVOCATIONS "grub,2\n" +#define SBAT_VAR_PREVIOUS_DATE "2021030218" #define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ - SBAT_VAR_PREVIOUS_REVOCATIONS + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" /* * Debian's grub.3 update was broken - some binaries included the SBAT diff --git a/sbat.c b/sbat.c index 4e51f2e8..aebc6074 100644 --- a/sbat.c +++ b/sbat.c @@ -10,6 +10,8 @@ extern struct { UINT32 latest_offset; } sbat_var_payload_header; +static UINT8 sbat_policy = SBAT_POLICY_NOTREAD; + EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, size_t *n_entries, @@ -407,64 +409,62 @@ clear_sbat_policy() } EFI_STATUS -set_sbat_uefi_variable(void) +set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) { EFI_STATUS efi_status = EFI_SUCCESS; UINT32 attributes = 0; - char *sbat_var_previous; - char *sbat_var_latest; - UINT8 *sbat = NULL; - UINT8 *sbat_policy = NULL; + UINT8 *sbat_policyp = NULL; UINTN sbatsize = 0; UINTN sbat_policysize = 0; char *sbat_var = NULL; bool reset_sbat = false; - sbat_var_previous = (char *)&sbat_var_payload_header + sbat_var_payload_header.previous_offset; - sbat_var_latest = (char *)&sbat_var_payload_header + sbat_var_payload_header.latest_offset; + if (sbat_policy == SBAT_POLICY_NOTREAD) { + efi_status = get_variable_attr(SBAT_POLICY, &sbat_policyp, + &sbat_policysize, SHIM_LOCK_GUID, + &attributes); + if (!EFI_ERROR(efi_status)) { + sbat_policy = *sbat_policyp; + clear_sbat_policy(); + } + } - efi_status = get_variable_attr(SBAT_POLICY, &sbat_policy, - &sbat_policysize, SHIM_LOCK_GUID, - &attributes); if (EFI_ERROR(efi_status)) { dprint("Default sbat policy: previous\n"); sbat_var = sbat_var_previous; } else { - switch (*sbat_policy) { - case SBAT_POLICY_LATEST: - dprint("Custom sbat policy: latest\n"); - sbat_var = sbat_var_latest; - clear_sbat_policy(); - break; - case SBAT_POLICY_PREVIOUS: - dprint("Custom sbat policy: previous\n"); - sbat_var = sbat_var_previous; - break; - case SBAT_POLICY_RESET: - if (secure_mode()) { - console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); - sbat_var = sbat_var_previous; - } else { - dprint(L"Custom SBAT policy: reset OK\n"); - reset_sbat = true; - sbat_var = SBAT_VAR_ORIGINAL; - } - clear_sbat_policy(); - break; - default: - console_error(L"SBAT policy state %llu is invalid", - EFI_INVALID_PARAMETER); + switch (sbat_policy) { + case SBAT_POLICY_LATEST: + dprint("Custom sbat policy: latest\n"); + sbat_var = sbat_var_latest; + break; + case SBAT_POLICY_PREVIOUS: + dprint("Custom sbat policy: previous\n"); + sbat_var = sbat_var_previous; + break; + case SBAT_POLICY_RESET: + if (secure_mode()) { + console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); sbat_var = sbat_var_previous; - clear_sbat_policy(); - break; + } else { + dprint(L"Custom SBAT policy: reset OK\n"); + reset_sbat = true; + sbat_var = SBAT_VAR_ORIGINAL; + } + break; + default: + console_error(L"SBAT policy state %llu is invalid", + EFI_INVALID_PARAMETER); + sbat_var = sbat_var_previous; + break; } } efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, - SHIM_LOCK_GUID, &attributes); + SHIM_LOCK_GUID, &attributes); /* * Always set the SbatLevel UEFI variable if it fails to read. * @@ -474,7 +474,7 @@ set_sbat_uefi_variable(void) if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, sbat_var) - && !reset_sbat) { + && !reset_sbat) { dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); @@ -526,4 +526,18 @@ set_sbat_uefi_variable(void) return efi_status; } +EFI_STATUS +set_sbat_uefi_variable_internal(void) +{ + char *sbat_var_previous; + char *sbat_var_latest; + + sbat_var_previous = (char *)&sbat_var_payload_header + + sbat_var_payload_header.previous_offset; + sbat_var_latest = (char *)&sbat_var_payload_header + + sbat_var_payload_header.latest_offset; + + return set_sbat_uefi_variable(sbat_var_previous, sbat_var_latest); +} + // vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 84a98cab..12ce10d6 100644 --- a/shim.c +++ b/shim.c @@ -1395,6 +1395,65 @@ uninstall_shim_protocols(void) #endif } +EFI_STATUS +load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + PE_COFF_LOADER_IMAGE_CONTEXT context; + EFI_IMAGE_SECTION_HEADER *Section; + void *pointer; + int datasize = 0; + void *data = NULL; + unsigned int i; + char *sbat_var_previous = NULL; + char *sbat_var_latest = NULL; + + efi_status = read_image(image_handle, L"revocations.efi", &PathName, + &data, &datasize); + if (EFI_ERROR(efi_status)) + return efi_status; + + efi_status = verify_image(data, datasize, shim_li, &context); + if (EFI_ERROR(efi_status)) { + dprint(L"revocations failed to verify\n"); + return efi_status; + } + dprint(L"verified revocations\n"); + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + dprint(L"checking section %a\n", (char *)Section->Name); + if (CompareMem(Section->Name, ".sbatl", 6) == 0) { + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + sbat_var_latest = (char *)pointer; + dprint(L"found sbatl\n"); + } + if (CompareMem(Section->Name, ".sbatp", 6) == 0) { + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + sbat_var_previous = (char *)pointer; + dprint(L"found sbatp\n"); + } + } + + if (sbat_var_latest && sbat_var_previous) { + dprint(L"attempting to update SBAT_LEVEL\n"); + efi_status = set_sbat_uefi_variable(sbat_var_previous, + sbat_var_latest); + } else { + dprint(L"no data for SBAT_LEVEL\n"); + } + FreePool(data); + return efi_status; +} + EFI_STATUS load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) { @@ -1441,9 +1500,12 @@ load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) return EFI_SUCCESS; } -/* Read additional certificates from files (after verifying signatures) */ +/* + * Read additional certificates and SBAT Level requirements from files + * (after verifying signatures) + */ EFI_STATUS -load_certs(EFI_HANDLE image_handle) +load_unbundled_trust(EFI_HANDLE image_handle) { EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; @@ -1486,8 +1548,20 @@ load_certs(EFI_HANDLE image_handle) goto done; } - while (1) { + if (!secure_mode()) + goto done; + + /* + * In the event that there are unprocessed revocation additions, they + * could be intended to ban any *new* trust anchors we find here. + * With that in mind, we always want to do a pass of loading + * revocations before we try to add anything new to our allowlist. + */ + load_revocations_file(image_handle, PathName); + + while (true) { UINTN old = buffersize; + efi_status = dir->Read(dir, &buffersize, buffer); if (efi_status == EFI_BUFFER_TOO_SMALL) { if (buffersize == old) { @@ -1499,7 +1573,7 @@ load_certs(EFI_HANDLE image_handle) * size, up to a certain point, until the call succeeds. */ perror(L"Error reading directory %s - non-compliant UEFI driver or firmware!\n", - PathName); + PathName); buffersize = (buffersize < 4) ? 4 : buffersize * 2; if (buffersize > 1024) goto done; @@ -1513,7 +1587,7 @@ load_certs(EFI_HANDLE image_handle) continue; } else if (EFI_ERROR(efi_status)) { perror(L"Failed to read directory %s - %r\n", PathName, - efi_status); + efi_status); goto done; } @@ -1521,7 +1595,7 @@ load_certs(EFI_HANDLE image_handle) if (buffersize == 0 || !info) goto done; - if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + if (StrCaseCmp(info->FileName, L"shim_certificate.efi") == 0) { load_cert_file(image_handle, info->FileName, PathName); } } @@ -1731,7 +1805,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); - efi_status = set_sbat_uefi_variable(); + efi_status = set_sbat_uefi_variable_internal(); if (EFI_ERROR(efi_status) && secure_mode()) { perror(L"%s variable initialization failed\n", SBAT_VAR_NAME); msg = SET_SBAT; @@ -1766,11 +1840,9 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) init_openssl(); - if (secure_mode()) { - efi_status = load_certs(global_image_handle); - if (EFI_ERROR(efi_status)) { - LogError(L"Failed to load addon certificates\n"); - } + efi_status = load_unbundled_trust(global_image_handle); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to load addon certificates / sbat level\n"); } /* diff --git a/test-sbat.c b/test-sbat.c index 0ee3d694..f59a1723 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -5,6 +5,7 @@ #ifndef SHIM_UNIT_TEST #define SHIM_UNIT_TEST +#include "sbat_var_defs.h" #endif #include "shim.h" @@ -1132,7 +1133,7 @@ test_sbat_var_asciz(void) UINTN size = sizeof(buf); char expected[] = SBAT_VAR_PREVIOUS; - status = set_sbat_uefi_variable(); + status = set_sbat_uefi_variable(SBAT_VAR_PREVIOUS, SBAT_VAR_PREVIOUS); if (status != EFI_SUCCESS) return -1; -- cgit v1.2.3 From 7dfb6871b8a54710d9e9d8d56146e7c083d2e6a8 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 28 Apr 2023 19:54:14 -0700 Subject: BS Variables for bootmgr revocations This adds support for applying SkuSiPolicy UEFI BS variables. These varaibles are needed for non-dbx based Windows revocations and are described here: https://support.microsoft.com/en-us/topic/kb5027455-guidance-for-blocking-vulnerable-windows-boot-managers-522bb851-0a61-44ad-aa94-ad11119c5e91 Signed-off-by: Jan Setje-Eilers --- include/guid.h | 1 + include/sbat.h | 10 ++- include/sbat_var_defs.h | 1 - include/ssp.h | 14 ++++ include/ssp_var_defs.h | 19 +++++ lib/guid.c | 1 + sbat.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++-- shim.c | 77 +++++++++++++----- shim.h | 1 + 9 files changed, 304 insertions(+), 31 deletions(-) create mode 100644 include/ssp.h create mode 100644 include/ssp_var_defs.h (limited to 'include') diff --git a/include/guid.h b/include/guid.h index dad63f0f..898c4fad 100644 --- a/include/guid.h +++ b/include/guid.h @@ -37,5 +37,6 @@ extern EFI_GUID SECURITY2_PROTOCOL_GUID; extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; extern EFI_GUID SHIM_LOCK_GUID; extern EFI_GUID MOK_VARIABLE_STORE; +extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID; #endif /* SHIM_GUID_H */ diff --git a/include/sbat.h b/include/sbat.h index 84f5ef01..af4c1a8f 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -30,11 +30,13 @@ #define SBAT_POLICY L"SbatPolicy" #define SBAT_POLICY8 "SbatPolicy" +#define SSP_POLICY L"SSPPolicy" +#define SSP_POLICY8 "SSPPolicy" -#define SBAT_POLICY_LATEST 1 -#define SBAT_POLICY_PREVIOUS 2 -#define SBAT_POLICY_RESET 3 -#define SBAT_POLICY_NOTREAD 255 +#define POLICY_LATEST 1 +#define POLICY_PREVIOUS 2 +#define POLICY_RESET 3 +#define POLICY_NOTREAD 255 extern UINTN _sbat, _esbat; diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 2ea98e4e..772df972 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -42,5 +42,4 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #endif /* ENABLE_SHIM_DEVEL */ - #endif /* !SBAT_VAR_DEFS_H_ */ diff --git a/include/ssp.h b/include/ssp.h new file mode 100644 index 00000000..f25590c6 --- /dev/null +++ b/include/ssp.h @@ -0,0 +1,14 @@ +#ifndef SSP_H_ +#define SSP_H_ + +#define SSPVER_VAR_NAME L"SkuSiPolicyVersion" +#define SSPSIG_VAR_NAME L"SkuSiPolicyUpdateSigners" +#define SSP_VAR_ATTRS UEFI_VAR_NV_BS + +#define SSPVER_SIZE 8 +#define SSPSIG_SIZE 131 + +EFI_STATUS set_ssp_uefi_variable_internal(void); +EFI_STATUS set_ssp_uefi_variable(uint8_t*, uint8_t*, uint8_t*, uint8_t*); + +#endif /* !SSP_H_ */ diff --git a/include/ssp_var_defs.h b/include/ssp_var_defs.h new file mode 100644 index 00000000..4bfad878 --- /dev/null +++ b/include/ssp_var_defs.h @@ -0,0 +1,19 @@ +/* + * variable definitions to enable bootmgr self revocation + */ +#ifndef SSP_VAR_DEFS_H_ +#define SSP_VAR_DEFS_H_ + +uint8_t SkuSiPolicyVersion[] = { 0x2,0x0,0x0,0x0,0x0,0x0,0x2,0x0 }; +uint8_t SkuSiPolicyUpdateSigners[] = { +0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0x0b,0x00,0x00,0x00,0xd0,0x91,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x54,0xa6,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x5c,0xa6,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x64,0xa6,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x0a,0x03,0x06,0x00, +0x00,0x00,0x00 }; + +#endif /* !SSP_VAR_DEFS_H_ */ diff --git a/lib/guid.c b/lib/guid.c index 904629eb..6e92cea3 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -36,3 +36,4 @@ EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} }; EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} }; +EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} }; diff --git a/sbat.c b/sbat.c index 60669ba1..b33fe85c 100644 --- a/sbat.c +++ b/sbat.c @@ -4,13 +4,16 @@ */ #include "shim.h" +#include "ssp.h" +#include "ssp_var_defs.h" extern struct { UINT32 previous_offset; UINT32 latest_offset; } sbat_var_payload_header; -static UINT8 sbat_policy = SBAT_POLICY_NOTREAD; +static UINT8 sbat_policy = POLICY_NOTREAD; +static UINT8 ssp_policy = POLICY_NOTREAD; EFI_STATUS parse_sbat_section(char *section_base, size_t section_size, @@ -398,6 +401,49 @@ preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes, return false; } +/* + * This looks kind of weird, but it comes directly from the MS + * documentation: + * https://support.microsoft.com/en-us/topic/kb5027455-guidance-for-blocking-vulnerable-windows-boot-managers-522bb851-0a61-44ad-aa94-ad11119c5e91 + */ +static UINT64 +ssp_ver_to_ull(UINT16 *ver) +{ + dprint("major: %u\n", ver[0]); + dprint("minor: %u\n", ver[1]); + dprint("rev: %u\n", ver[2]); + dprint("build: %u\n", ver[3]); + + return ((UINT64)ver[0] << 48) + + ((UINT64)ver[1] << 32) + + ((UINT64)ver[2] << 16) + + ver[3]; +} + +static bool +preserve_ssp_uefi_variable(UINT8 *ssp_applied, UINTN sspversize, UINT32 attributes, + uint8_t *ssp_candidate) +{ + UINT64 old, new; + + if (ssp_applied == NULL || ssp_candidate == NULL) + return false; + + if (sspversize != SSPVER_SIZE) + return false; + + if (!check_sbat_var_attributes(attributes)) + return false; + + old = ssp_ver_to_ull((UINT16 *)ssp_applied); + new = ssp_ver_to_ull((UINT16 *)ssp_candidate); + + if (new > old) + return false; + else + return true; +} + static void clear_sbat_policy() { @@ -422,7 +468,7 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) char *sbat_var = NULL; bool reset_sbat = false; - if (sbat_policy == SBAT_POLICY_NOTREAD) { + if (sbat_policy == POLICY_NOTREAD) { efi_status = get_variable_attr(SBAT_POLICY, &sbat_policyp, &sbat_policysize, SHIM_LOCK_GUID, &attributes); @@ -477,9 +523,6 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) SHIM_LOCK_GUID, &attributes); /* * Always set the SbatLevel UEFI variable if it fails to read. - * - * Don't try to set the SbatLevel UEFI variable if attributes match - * and the signature matches. */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); @@ -532,7 +575,6 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) } FreePool(sbat); - return efi_status; } @@ -550,4 +592,161 @@ set_sbat_uefi_variable_internal(void) return set_sbat_uefi_variable(sbat_var_previous, sbat_var_latest); } +static void +clear_ssp_policy(void) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + + efi_status = del_variable(SSP_POLICY, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) + console_error(L"Could not reset SSP Policy", efi_status); +} + +static EFI_STATUS +clear_ssp_uefi_variables(void) +{ + EFI_STATUS efi_status, rc = EFI_SUCCESS; + + /* delete previous variable */ + dprint("Deleting %s variable.\n", SSPVER_VAR_NAME); + efi_status = del_variable(SSPVER_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID); + if (EFI_ERROR(efi_status)) { + dprint(L"%s variable delete failed %r\n", SSPVER_VAR_NAME, + efi_status); + rc = efi_status; + } + dprint("Deleting %s variable.\n", SSPSIG_VAR_NAME); + efi_status = del_variable(SSPSIG_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID); + if (EFI_ERROR(efi_status)) { + dprint(L"%s variable delete failed %r\n", SSPSIG_VAR_NAME, + efi_status); + rc = efi_status; + } + + return rc; +} + +EFI_STATUS +set_ssp_uefi_variable(uint8_t *ssp_ver_previous, uint8_t *ssp_sig_previous, + uint8_t *ssp_ver_latest, uint8_t *ssp_sig_latest) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + UINT32 attributes = 0; + + UINT8 *sspver = NULL; + UINT8 *policyp = NULL; + UINTN sspversize = 0; + UINTN policysize = 0; + + uint8_t *ssp_ver = NULL; + uint8_t *ssp_sig = NULL; + bool reset_ssp = false; + + _Static_assert(sizeof(SkuSiPolicyVersion) == SSPVER_SIZE, + "SkuSiPolicyVersion has unexpected size"); + _Static_assert(sizeof(SkuSiPolicyUpdateSigners) == SSPSIG_SIZE, + "SkuSiPolicyUpdateSigners has unexpected size"); + + if (ssp_policy == POLICY_NOTREAD) { + efi_status = get_variable_attr(SSP_POLICY, &policyp, + &policysize, SHIM_LOCK_GUID, + &attributes); + if (!EFI_ERROR(efi_status)) { + ssp_policy = *policyp; + clear_ssp_policy(); + } + } + + if (EFI_ERROR(efi_status)) { + dprint("Default SSP policy: previous\n"); + ssp_ver = ssp_ver_previous; + ssp_sig = ssp_sig_previous; + } else { + switch (ssp_policy) { + case POLICY_LATEST: + dprint("Custom SSP policy: latest\n");\ + ssp_ver = ssp_ver_latest; + ssp_sig = ssp_sig_latest; + break; + case POLICY_PREVIOUS: + dprint("Custom SSP policy: previous\n"); + ssp_ver = ssp_ver_previous; + ssp_sig = ssp_sig_previous; + break; + case POLICY_RESET: + if (secure_mode()) { + console_print(L"Cannot reset SSP policy: Secure Boot is enabled.\n"); + ssp_ver = ssp_ver_previous; + ssp_sig = ssp_sig_previous; + } else { + dprint(L"Custom SSP policy: reset OK\n"); + reset_ssp = true; + } + break; + default: + console_error(L"SSP policy state %llu is invalid", + EFI_INVALID_PARAMETER); + ssp_ver = ssp_ver_previous; + ssp_sig = ssp_sig_previous; + break; + } + } + + if (!ssp_ver && !ssp_sig && !reset_ssp) { + dprint(L"No supplied SSP data, not setting variables\n"); + return EFI_SUCCESS; + } + + efi_status = get_variable_attr(SSPVER_VAR_NAME, &sspver, &sspversize, + SECUREBOOT_EFI_NAMESPACE_GUID, &attributes); + /* + * Since generally we want bootmgr to manage its own revocations, + * we are much less agressive trying to set those variables + */ + if (EFI_ERROR(efi_status)) { + dprint(L"SkuSiPolicyVersion read failed %r\n", efi_status); + } else if (preserve_ssp_uefi_variable(sspver, sspversize, attributes, ssp_ver) + && !reset_ssp) { + FreePool(sspver); + + dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", + SSPVER_VAR_NAME, sspversize, attributes); + return EFI_SUCCESS; + } else { + FreePool(sspver); + + efi_status = clear_ssp_uefi_variables(); + } + + if (reset_ssp) + return efi_status; + + /* set variable */ + efi_status = set_variable(SSPVER_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID, + SSP_VAR_ATTRS, SSPVER_SIZE, ssp_ver); + if (EFI_ERROR(efi_status)) { + dprint(L"%s variable writing failed %r\n", SSPVER_VAR_NAME, + efi_status); + return efi_status; + } + dprint("done setting %s variable.\n", SSPSIG_VAR_NAME); + + efi_status = set_variable(SSPSIG_VAR_NAME, SECUREBOOT_EFI_NAMESPACE_GUID, + SSP_VAR_ATTRS, SSPSIG_SIZE, ssp_sig); + if (EFI_ERROR(efi_status)) { + dprint(L"%s variable writing failed %r\n", SSPSIG_VAR_NAME, + efi_status); + return efi_status; + } + dprint("done setting %s variable.\n", SSPSIG_VAR_NAME); + + return efi_status; +} + +EFI_STATUS +set_ssp_uefi_variable_internal(void) +{ + return set_ssp_uefi_variable(NULL, NULL, SkuSiPolicyVersion, + SkuSiPolicyUpdateSigners); +} // vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 12ce10d6..2ad1c063 100644 --- a/shim.c +++ b/shim.c @@ -1395,18 +1395,45 @@ uninstall_shim_protocols(void) #endif } +static void +check_section_helper(char *section_name, int len, void **pointer, + EFI_IMAGE_SECTION_HEADER *Section, void *data, + int datasize, size_t minsize) +{ + if (CompareMem(Section->Name, section_name, len) == 0) { + *pointer = ImageAddress(data, datasize, Section->PointerToRawData); + if (Section->SizeOfRawData < minsize) { + dprint(L"found and rejected %.*a bad size\n", len, section_name); + dprint(L"minsize: %d\n", minsize); + dprint(L"rawsize: %d\n", Section->SizeOfRawData); + return ; + } + if (!*pointer) { + return ; + } + dprint(L"found %.*a\n", len, section_name); + } +} + +#define check_section(section_name, pointer, section, data, datasize, minsize) \ + check_section_helper(section_name, sizeof(section_name) - 1, pointer, \ + section, data, datasize, minsize) + EFI_STATUS load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) { EFI_STATUS efi_status = EFI_SUCCESS; PE_COFF_LOADER_IMAGE_CONTEXT context; EFI_IMAGE_SECTION_HEADER *Section; - void *pointer; int datasize = 0; void *data = NULL; unsigned int i; char *sbat_var_previous = NULL; char *sbat_var_latest = NULL; + uint8_t *ssps_previous = NULL; + uint8_t *sspv_previous = NULL; + uint8_t *ssps_latest = NULL; + uint8_t *sspv_latest = NULL; efi_status = read_image(image_handle, L"revocations.efi", &PathName, &data, &datasize); @@ -1422,25 +1449,19 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++, Section++) { - dprint(L"checking section %a\n", (char *)Section->Name); - if (CompareMem(Section->Name, ".sbatl", 6) == 0) { - pointer = ImageAddress(data, datasize, - Section->PointerToRawData); - if (!pointer) { - continue; - } - sbat_var_latest = (char *)pointer; - dprint(L"found sbatl\n"); - } - if (CompareMem(Section->Name, ".sbatp", 6) == 0) { - pointer = ImageAddress(data, datasize, - Section->PointerToRawData); - if (!pointer) { - continue; - } - sbat_var_previous = (char *)pointer; - dprint(L"found sbatp\n"); - } + dprint(L"checking section \"%c%c%c%c%c%c%c%c\"\n", (char *)Section->Name); + check_section(".sbatp", (void **)&sbat_var_previous, Section, + data, datasize, sizeof(SBAT_VAR_ORIGINAL)); + check_section(".sbatl", (void **)&sbat_var_latest, Section, + data, datasize, sizeof(SBAT_VAR_ORIGINAL)); + check_section(".sspvp", (void **)&sspv_previous, Section, + data, datasize, SSPVER_SIZE); + check_section(".sspsp", (void **)&ssps_previous, Section, + data, datasize, SSPSIG_SIZE); + check_section(".sspvl", (void **)&sspv_latest, Section, + data, datasize, SSPVER_SIZE); + check_section(".sspsl", (void **)&ssps_latest, Section, + data, datasize, SSPSIG_SIZE); } if (sbat_var_latest && sbat_var_previous) { @@ -1450,6 +1471,16 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) } else { dprint(L"no data for SBAT_LEVEL\n"); } + + if ((sspv_previous && ssps_previous) || (sspv_latest && ssps_latest)) { + dprint(L"attempting to update SkuSiPolicy\n"); + efi_status = set_ssp_uefi_variable(sspv_previous, ssps_previous, + sspv_latest, ssps_latest); + + } else { + dprint(L"no data for SkuSiPolicy\n"); + } + FreePool(data); return efi_status; } @@ -1814,6 +1845,12 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) dprint(L"%s variable initialization failed: %r\n", SBAT_VAR_NAME, efi_status); } + efi_status = set_ssp_uefi_variable_internal(); + if (EFI_ERROR(efi_status)) { + dprint(L"%s variable initialization failed: %r\n", + SSPVER_VAR_NAME, efi_status); + } + dprint(L"%s variable initialization done\n", SSPVER_VAR_NAME); if (secure_mode()) { char *sbat_start = (char *)&_sbat; diff --git a/shim.h b/shim.h index 652be45c..5791a031 100644 --- a/shim.h +++ b/shim.h @@ -180,6 +180,7 @@ #include "include/replacements.h" #include "include/sbat.h" #include "include/sbat_var_defs.h" +#include "include/ssp.h" #if defined(OVERRIDE_SECURITY_POLICY) #include "include/security_policy.h" #endif -- cgit v1.2.3 From a967c0e7a0a27a310958f5b64a4c4ef8dc1b546e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 7 Jul 2023 13:21:30 -0700 Subject: shim should not self revoke Before applying an updated SbatLevel shim should re-run introspection and never apply a revocation level that would prevent the currently running shim from booting. The proper way forward is to update shim first. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 2 +- sbat.c | 84 +++++++++++++++++++++++++++++++++++++++++----------------- shim.c | 2 +- test-sbat.c | 2 +- 4 files changed, 62 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index af4c1a8f..20009ada 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -53,7 +53,7 @@ extern list_t sbat_var; #define SBAT_VAR_COLUMNS ((sizeof (struct sbat_var_entry) - sizeof(list_t)) / sizeof(CHAR8 *)) #define SBAT_VAR_REQUIRED_COLUMNS (SBAT_VAR_COLUMNS - 1) -EFI_STATUS parse_sbat_var(list_t *entries); +EFI_STATUS parse_sbat_var(list_t *entries, char *sbat_var_candidate); void cleanup_sbat_var(list_t *entries); EFI_STATUS set_sbat_uefi_variable_internal(void); EFI_STATUS set_sbat_uefi_variable(char *, char *); diff --git a/sbat.c b/sbat.c index b33fe85c..391b59e6 100644 --- a/sbat.c +++ b/sbat.c @@ -302,7 +302,7 @@ err: } EFI_STATUS -parse_sbat_var(list_t *entries) +parse_sbat_var(list_t *entries, char *sbat_var_candidate) { UINT8 *data = 0; UINTN datasize; @@ -314,10 +314,17 @@ parse_sbat_var(list_t *entries) return EFI_INVALID_PARAMETER; } - efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID); - if (EFI_ERROR(efi_status)) { - LogError(L"Failed to read SBAT variable\n", efi_status); - return efi_status; + if (sbat_var_candidate == NULL) { + dprint(L"sbat_var_candidate is NULL, reading variable\n"); + efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to read SBAT variable\n", efi_status); + return efi_status; + } + } else { + datasize = strlen(sbat_var_candidate); + data = AllocatePool(datasize + 1); + memcpy(data, sbat_var_candidate, datasize); } /* @@ -325,8 +332,10 @@ parse_sbat_var(list_t *entries) * allocations, so use that here. */ efi_status = parse_sbat_var_data(entries, data, datasize+1); - if (EFI_ERROR(efi_status)) - return efi_status; + if (EFI_ERROR(efi_status)) { + dprint(L"parse_sbat_var_data() failed datasize: %d\n", datasize); + goto out; + } dprint(L"SBAT variable entries:\n"); list_for_each(pos, entries) { @@ -337,6 +346,8 @@ parse_sbat_var(list_t *entries) entry->component_generation, entry->sbat_datestamp); } +out: + FreePool(data); return efi_status; } @@ -465,7 +476,7 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) UINTN sbatsize = 0; UINTN sbat_policysize = 0; - char *sbat_var = NULL; + char *sbat_var_candidate = NULL; bool reset_sbat = false; if (sbat_policy == POLICY_NOTREAD) { @@ -481,39 +492,39 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) if (EFI_ERROR(efi_status)) { dprint("Default sbat policy: previous\n"); if (secure_mode()) { - sbat_var = sbat_var_previous; + sbat_var_candidate = sbat_var_previous; } else { reset_sbat = true; - sbat_var = SBAT_VAR_ORIGINAL; + sbat_var_candidate = SBAT_VAR_ORIGINAL; } } else { switch (sbat_policy) { - case SBAT_POLICY_LATEST: + case POLICY_LATEST: dprint("Custom sbat policy: latest\n"); - sbat_var = sbat_var_latest; + sbat_var_candidate = sbat_var_latest; break; - case SBAT_POLICY_PREVIOUS: + case POLICY_PREVIOUS: dprint("Custom sbat policy: previous\n"); - sbat_var = sbat_var_previous; + sbat_var_candidate = sbat_var_previous; break; - case SBAT_POLICY_RESET: + case POLICY_RESET: if (secure_mode()) { console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); - sbat_var = sbat_var_previous; + sbat_var_candidate = sbat_var_previous; } else { dprint(L"Custom SBAT policy: reset OK\n"); reset_sbat = true; - sbat_var = SBAT_VAR_ORIGINAL; + sbat_var_candidate = SBAT_VAR_ORIGINAL; } break; default: console_error(L"SBAT policy state %llu is invalid", EFI_INVALID_PARAMETER); if (secure_mode()) { - sbat_var = sbat_var_previous; + sbat_var_candidate = sbat_var_previous; } else { reset_sbat = true; - sbat_var = SBAT_VAR_ORIGINAL; + sbat_var_candidate = SBAT_VAR_ORIGINAL; } break; } @@ -526,8 +537,9 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, sbat_var) - && !reset_sbat) { + } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, + sbat_var_candidate) && + !reset_sbat) { dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); @@ -535,6 +547,27 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) } else { FreePool(sbat); + /* + * parse the candidate SbatLevel and check that shim will not + * self revoke before writing SbatLevel variable + */ + dprint(L"shim SBAT reparse before application\n"); + efi_status = parse_sbat_var(&sbat_var, sbat_var_candidate); + if (EFI_ERROR(efi_status)) { + dprint(L"proposed SbatLevel failed to parse\n"); + return efi_status; + } + +#ifndef SHIM_UNIT_TEST + char *sbat_start = (char *)&_sbat; + char *sbat_end = (char *)&_esbat; + efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1); + if (EFI_ERROR(efi_status)) { + dprint(L"shim SBAT self check fails for new SbatLevel, refusing to apply\n"); + return efi_status; + } +#endif /* SHIM_UNIT_TEST */ + /* delete previous variable */ dprint("%s variable is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); @@ -550,7 +583,7 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) /* set variable */ efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, - strlen(sbat_var), sbat_var); + strlen(sbat_var_candidate), sbat_var_candidate); if (EFI_ERROR(efi_status)) { dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME, efi_status); @@ -565,10 +598,11 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) return efi_status; } - if (sbatsize != strlen(sbat_var) || - strncmp((const char *)sbat, sbat_var, strlen(sbat_var)) != 0) { + if (sbatsize != strlen(sbat_var_candidate) || + strncmp((const char *)sbat, sbat_var_candidate, + strlen(sbat_var_candidate)) != 0) { dprint("new sbatsize is %d, expected %d\n", sbatsize, - strlen(sbat_var)); + strlen(sbat_var_candidate)); efi_status = EFI_INVALID_PARAMETER; } else { dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME); diff --git a/shim.c b/shim.c index 2ad1c063..cfdf2732 100644 --- a/shim.c +++ b/shim.c @@ -1857,7 +1857,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) char *sbat_end = (char *)&_esbat; INIT_LIST_HEAD(&sbat_var); - efi_status = parse_sbat_var(&sbat_var); + efi_status = parse_sbat_var(&sbat_var, NULL); if (EFI_ERROR(efi_status)) { perror(L"Parsing %s variable failed: %r\n", SBAT_VAR_NAME, efi_status); diff --git a/test-sbat.c b/test-sbat.c index f59a1723..980a8f86 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -379,7 +379,7 @@ test_parse_sbat_var_null_list(void) EFI_STATUS status; INIT_LIST_HEAD(&sbat_var); - status = parse_sbat_var(NULL); + status = parse_sbat_var(NULL, NULL); cleanup_sbat_var(&sbat_var); assert_equal_return(status, EFI_INVALID_PARAMETER, -1, "got %#hhx expected %#hhx\n"); -- cgit v1.2.3 From 57c0eedfa1ebf6e2132a9cb26a7b0fcdee82557f Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Wed, 20 Sep 2023 18:03:41 -0700 Subject: Updated Revocations for January 2024 CVEs Since shim is inherently updated by shipping a new shim, the latest built in revocations can include the most recent shim revocations. Since CVE-2023-40547 is high impact, this revocation should be available to everyone as soon as possible. GRUB2 CVE-2023-4692 and CVE-2023-4693 are in the ntfs module that only some vendors ship. Since some vendors did not ship an updated GRUB2 for these issues, the revocation for these CVEs is not included in the payload at this time. Signed-off-by: Jan Setje-Eilers --- SbatLevel_Variable.txt | 76 +++++++++++++++++++++++++++++++++++++++++++++---- data/sbat.csv | 2 +- include/sbat_var_defs.h | 7 ++--- 3 files changed, 75 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/SbatLevel_Variable.txt b/SbatLevel_Variable.txt index 8696304d..42a388e4 100644 --- a/SbatLevel_Variable.txt +++ b/SbatLevel_Variable.txt @@ -12,7 +12,7 @@ Initialized, no revocations: sbat,1,2021030218 -To Revoke GRUB binaries impacted by +To Revoke GRUB2 binaries impacted by * CVE-2021-3695 * CVE-2021-3696 @@ -21,22 +21,88 @@ To Revoke GRUB binaries impacted by * CVE-2022-28734 * CVE-2022-28735 * CVE-2022-28736 + +sbat,1,2022052400 +grub,2 + +and shim binaries impacted by + * CVE-2022-28737 sbat,1,2022052400 +shim,2 grub,2 -To revoke the above and also grub binaries impacted by +Shim delivered both versions of these revocations with +the same 2022052400 date stamp, once as an opt-in latest +revocation with shim,2 and then as an automatic revocation without +shim,2 + + +To revoke GRUB2 grub binaries impacted by * CVE-2022-2601 * CVE-2022-3775 sbat,1,2022111500 +shim,2 grub,3 -An additonal bug was fixed in shim that was not considered exploitable -and can be revoked by setting: +To revoke Debian's grub.3 which missed +the patches: -sbat,1,2022111500 +sbat,1,2023012900 +shim,2 +grub,3 +grub.debian,4 + + +An additonal bug was fixed in shim that was not considered exploitable, +can be revoked by setting: + +sbat,1,2023012950 +shim,3 +grub,3 +grub.debian,4 + +shim did not deliver this payload at the time + + +To Revoke GRUB2 binaries impacted by: + +* CVE-2023-4692 +* CVE-2023-4693 + +These CVEs are in the ntfs module and vendors that do and do not +ship this module as part of their signed binary are split. + +sbat,1,2023091900 shim,2 +grub,4 + +Since not everyone has shipped updated GRUB packages, shim did not +deliver this revocation at the time. + +To Revoke shim binaries impacted by: + +* CVE-2023-40547 +* CVE-2023-40546 +* CVE-2023-40548 +* CVE-2023-40549 +* CVE-2023-40550 +* CVE-2023-40551 + +sbat,1,2024010900 +shim,4 grub,3 +grub.debian,4 + +Since http boot shim CVE is considerably more serious than then GRUB +ntfs CVEs shim is delivering the shim revocation without the updated +GRUB revocation as a latest payload. + +To revoke both the impacted shim and impacted GRUB binaries: + +sbat,1,2024 +shim,4 +grub,4 diff --git a/data/sbat.csv b/data/sbat.csv index 2beec754..18293727 100755 --- a/data/sbat.csv +++ b/data/sbat.csv @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -shim,3,UEFI shim,shim,1,https://github.com/rhboot/shim +shim,4,UEFI shim,shim,1,https://github.com/rhboot/shim diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 772df972..8e643a4e 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -33,11 +33,10 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" /* - * Debian's grub.3 update was broken - some binaries included the SBAT - * data update but not the security patches :-( + * Revocations for January 2024 shim CVEs */ -#define SBAT_VAR_LATEST_DATE "2023012900" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" +#define SBAT_VAR_LATEST_DATE "2024010900" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From 13abd9f51b285db7eb46bf375cae623bf1153404 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 6 Dec 2023 17:07:01 -0500 Subject: pe-relocate: Avoid __builtin_add_overflow() on GCC < 5 GCC 4 doesn't have __builtin_add_overflow() and friends, so this results in a compiler error. On platforms using that version, do the arithmetic without it. Signed-off-by: Peter Jones --- include/compiler.h | 34 ++++++++++++++++++++++++++++++++++ pe-relocate.c | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index 545a72e5..8e8a658d 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -198,12 +198,46 @@ #error shim has no cache_invalidate() implementation for this compiler #endif /* __GNUC__ */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +#define GNUC_PREREQ(maj, min) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define CLANG_PREREQ(maj, min) \ + ((__clang_major__ > (maj)) || \ + (__clang_major__ == (maj) && __clang_minor__ >= (min))) +#else +#define CLANG_PREREQ(maj, min) 0 +#endif + +#if GNUC_PREREQ(5, 1) || CLANG_PREREQ(3, 8) #define checked_add(addend0, addend1, sum) \ __builtin_add_overflow(addend0, addend1, sum) #define checked_sub(minuend, subtrahend, difference) \ __builtin_sub_overflow(minuend, subtrahend, difference) #define checked_mul(factor0, factor1, product) \ __builtin_mul_overflow(factor0, factor1, product) +#else +#define checked_add(a0, a1, s) \ + ({ \ + (*s) = ((a0) + (a1)); \ + 0; \ + }) +#define checked_sub(s0, s1, d) \ + ({ \ + (*d) = ((s0) - (s1)); \ + 0; \ + }) +#define checked_mul(f0, f1, p) \ + ({ \ + (*p) = ((f0) * (f1)); \ + 0; \ + }) +#endif + #define checked_div(dividend, divisor, quotient) \ ({ \ bool _ret = True; \ diff --git a/pe-relocate.c b/pe-relocate.c index d399cdf1..bde71729 100644 --- a/pe-relocate.c +++ b/pe-relocate.c @@ -20,7 +20,7 @@ ImageAddress (void *image, uint64_t size, uint64_t address) /* Insure our math won't overflow */ img_addr = (uintptr_t)image; - if (__builtin_add_overflow(img_addr, address, &img_addr)) + if (checked_add(img_addr, address, &img_addr)) return NULL; /* return the absolute pointer */ -- cgit v1.2.3 From 30a4f3751a8da09ab0853f1a384b80096828cc34 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Wed, 13 Dec 2023 17:59:28 -0800 Subject: Rename "previous" revocations to "automatic" When the term previous was introduced for revocations to be automatically applied there was a hope that everytime a new revocation was built into shim, the previous revocation could be applied automatically. Further experience has shown the real world to be more complex than that. The automatic payload will realistically contain a set of revocations governed by both the cadence at which a distro's customer base updates as well as the severity of the issue being revoked. This is not a functional change. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 8 ++++---- include/sbat_var_defs.h | 16 ++++++++-------- sbat.c | 50 ++++++++++++++++++++++++------------------------- sbat_var.S | 6 +++--- shim.c | 20 ++++++++++---------- test-sbat.c | 4 ++-- 6 files changed, 52 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 20009ada..4be0cbb1 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -33,10 +33,10 @@ #define SSP_POLICY L"SSPPolicy" #define SSP_POLICY8 "SSPPolicy" -#define POLICY_LATEST 1 -#define POLICY_PREVIOUS 2 -#define POLICY_RESET 3 -#define POLICY_NOTREAD 255 +#define POLICY_LATEST 1 +#define POLICY_AUTOMATIC 2 +#define POLICY_RESET 3 +#define POLICY_NOTREAD 255 extern UINTN _sbat, _esbat; diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 8e643a4e..bd54cf5d 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -13,9 +13,9 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" #if defined(ENABLE_SHIM_DEVEL) -#define SBAT_VAR_PREVIOUS_DATE "2021030218" -#define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" +#define SBAT_VAR_AUTOMATIC_DATE "2021030218" +#define SBAT_VAR_AUTOMATIC \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" @@ -25,12 +25,12 @@ #else /* !ENABLE_SHIM_DEVEL */ /* * At this point we do not want shim to automatically apply a - * previous revocation unless it is delivered by a separately - * installed signed revocations binary. + * revocation unless it is delivered by a separately installed + * signed revocations binary. */ -#define SBAT_VAR_PREVIOUS_DATE "2021030218" -#define SBAT_VAR_PREVIOUS \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" +#define SBAT_VAR_AUTOMATIC_DATE "2021030218" +#define SBAT_VAR_AUTOMATIC \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" /* * Revocations for January 2024 shim CVEs diff --git a/sbat.c b/sbat.c index b9f228b4..06956122 100644 --- a/sbat.c +++ b/sbat.c @@ -8,7 +8,7 @@ #include "ssp_var_defs.h" extern struct { - UINT32 previous_offset; + UINT32 automatic_offset; UINT32 latest_offset; } sbat_var_payload_header; @@ -466,7 +466,7 @@ clear_sbat_policy() } EFI_STATUS -set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) +set_sbat_uefi_variable(char *sbat_var_automatic, char *sbat_var_latest) { EFI_STATUS efi_status = EFI_SUCCESS; UINT32 attributes = 0; @@ -490,9 +490,9 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) } if (EFI_ERROR(efi_status)) { - dprint("Default sbat policy: previous\n"); + dprint("Default sbat policy: automatic\n"); if (secure_mode()) { - sbat_var_candidate = sbat_var_previous; + sbat_var_candidate = sbat_var_automatic; } else { reset_sbat = true; sbat_var_candidate = SBAT_VAR_ORIGINAL; @@ -503,14 +503,14 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) dprint("Custom sbat policy: latest\n"); sbat_var_candidate = sbat_var_latest; break; - case POLICY_PREVIOUS: - dprint("Custom sbat policy: previous\n"); - sbat_var_candidate = sbat_var_previous; + case POLICY_AUTOMATIC: + dprint("Custom sbat policy: automatic\n"); + sbat_var_candidate = sbat_var_automatic; break; case POLICY_RESET: if (secure_mode()) { console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); - sbat_var_candidate = sbat_var_previous; + sbat_var_candidate = sbat_var_automatic; } else { dprint(L"Custom SBAT policy: reset OK\n"); reset_sbat = true; @@ -521,7 +521,7 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) console_error(L"SBAT policy state %llu is invalid", EFI_INVALID_PARAMETER); if (secure_mode()) { - sbat_var_candidate = sbat_var_previous; + sbat_var_candidate = sbat_var_automatic; } else { reset_sbat = true; sbat_var_candidate = SBAT_VAR_ORIGINAL; @@ -617,15 +617,15 @@ set_sbat_uefi_variable(char *sbat_var_previous, char *sbat_var_latest) EFI_STATUS set_sbat_uefi_variable_internal(void) { - char *sbat_var_previous; + char *sbat_var_automatic; char *sbat_var_latest; - sbat_var_previous = (char *)&sbat_var_payload_header + - sbat_var_payload_header.previous_offset; + sbat_var_automatic = (char *)&sbat_var_payload_header + + sbat_var_payload_header.automatic_offset; sbat_var_latest = (char *)&sbat_var_payload_header + sbat_var_payload_header.latest_offset; - return set_sbat_uefi_variable(sbat_var_previous, sbat_var_latest); + return set_sbat_uefi_variable(sbat_var_automatic, sbat_var_latest); } static void @@ -663,7 +663,7 @@ clear_ssp_uefi_variables(void) } EFI_STATUS -set_ssp_uefi_variable(uint8_t *ssp_ver_previous, uint8_t *ssp_sig_previous, +set_ssp_uefi_variable(uint8_t *ssp_ver_automatic, uint8_t *ssp_sig_automatic, uint8_t *ssp_ver_latest, uint8_t *ssp_sig_latest) { EFI_STATUS efi_status = EFI_SUCCESS; @@ -694,9 +694,9 @@ set_ssp_uefi_variable(uint8_t *ssp_ver_previous, uint8_t *ssp_sig_previous, } if (EFI_ERROR(efi_status)) { - dprint("Default SSP policy: previous\n"); - ssp_ver = ssp_ver_previous; - ssp_sig = ssp_sig_previous; + dprint("Default SSP policy: automatic\n"); + ssp_ver = ssp_ver_automatic; + ssp_sig = ssp_sig_automatic; } else { switch (ssp_policy) { case POLICY_LATEST: @@ -704,16 +704,16 @@ set_ssp_uefi_variable(uint8_t *ssp_ver_previous, uint8_t *ssp_sig_previous, ssp_ver = ssp_ver_latest; ssp_sig = ssp_sig_latest; break; - case POLICY_PREVIOUS: - dprint("Custom SSP policy: previous\n"); - ssp_ver = ssp_ver_previous; - ssp_sig = ssp_sig_previous; + case POLICY_AUTOMATIC: + dprint("Custom SSP policy: automatic\n"); + ssp_ver = ssp_ver_automatic; + ssp_sig = ssp_sig_automatic; break; case POLICY_RESET: if (secure_mode()) { console_print(L"Cannot reset SSP policy: Secure Boot is enabled.\n"); - ssp_ver = ssp_ver_previous; - ssp_sig = ssp_sig_previous; + ssp_ver = ssp_ver_automatic; + ssp_sig = ssp_sig_automatic; } else { dprint(L"Custom SSP policy: reset OK\n"); reset_ssp = true; @@ -722,8 +722,8 @@ set_ssp_uefi_variable(uint8_t *ssp_ver_previous, uint8_t *ssp_sig_previous, default: console_error(L"SSP policy state %llu is invalid", EFI_INVALID_PARAMETER); - ssp_ver = ssp_ver_previous; - ssp_sig = ssp_sig_previous; + ssp_ver = ssp_ver_automatic; + ssp_sig = ssp_sig_automatic; break; } } diff --git a/sbat_var.S b/sbat_var.S index 7854ade3..ed82a46c 100644 --- a/sbat_var.S +++ b/sbat_var.S @@ -9,12 +9,12 @@ .type sbat_var_payload_header, %object .size sbat_var_payload_header, .Lsbat_var_payload_header_end - sbat_var_payload_header sbat_var_payload_header: - .4byte .Lsbat_var_previous - sbat_var_payload_header + .4byte .Lsbat_var_automatic - sbat_var_payload_header .4byte .Lsbat_var_latest - sbat_var_payload_header .Lsbat_var_payload_header_end: .balign 1, 0 -.Lsbat_var_previous: - .ascii SBAT_VAR_PREVIOUS +.Lsbat_var_automatic: + .ascii SBAT_VAR_AUTOMATIC .byte 0 .balign 1, 0 .Lsbat_var_latest: diff --git a/shim.c b/shim.c index d800f675..3854fbde 100644 --- a/shim.c +++ b/shim.c @@ -1430,10 +1430,10 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) int datasize = 0; void *data = NULL; unsigned int i; - char *sbat_var_previous = NULL; + char *sbat_var_automatic = NULL; char *sbat_var_latest = NULL; - uint8_t *ssps_previous = NULL; - uint8_t *sspv_previous = NULL; + uint8_t *ssps_automatic = NULL; + uint8_t *sspv_automatic = NULL; uint8_t *ssps_latest = NULL; uint8_t *sspv_latest = NULL; @@ -1452,13 +1452,13 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++, Section++) { dprint(L"checking section \"%c%c%c%c%c%c%c%c\"\n", (char *)Section->Name); - check_section(".sbatp\0\0", (void **)&sbat_var_previous, Section, + check_section(".sbata\0\0", (void **)&sbat_var_automatic, Section, data, datasize, sizeof(SBAT_VAR_ORIGINAL)); check_section(".sbatl\0\0", (void **)&sbat_var_latest, Section, data, datasize, sizeof(SBAT_VAR_ORIGINAL)); - check_section(".sspvp\0\0", (void **)&sspv_previous, Section, + check_section(".sspva\0\0", (void **)&sspv_automatic, Section, data, datasize, SSPVER_SIZE); - check_section(".sspsp\0\0", (void **)&ssps_previous, Section, + check_section(".sspsa\0\0", (void **)&ssps_automatic, Section, data, datasize, SSPSIG_SIZE); check_section(".sspvl\0\0", (void **)&sspv_latest, Section, data, datasize, SSPVER_SIZE); @@ -1466,17 +1466,17 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) data, datasize, SSPSIG_SIZE); } - if (sbat_var_latest && sbat_var_previous) { + if (sbat_var_latest && sbat_var_automatic) { dprint(L"attempting to update SBAT_LEVEL\n"); - efi_status = set_sbat_uefi_variable(sbat_var_previous, + efi_status = set_sbat_uefi_variable(sbat_var_automatic, sbat_var_latest); } else { dprint(L"no data for SBAT_LEVEL\n"); } - if ((sspv_previous && ssps_previous) || (sspv_latest && ssps_latest)) { + if ((sspv_automatic && ssps_automatic) || (sspv_latest && ssps_latest)) { dprint(L"attempting to update SkuSiPolicy\n"); - efi_status = set_ssp_uefi_variable(sspv_previous, ssps_previous, + efi_status = set_ssp_uefi_variable(sspv_automatic, ssps_automatic, sspv_latest, ssps_latest); } else { diff --git a/test-sbat.c b/test-sbat.c index 980a8f86..b37efcdd 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -1131,9 +1131,9 @@ test_sbat_var_asciz(void) char buf[1024] = ""; UINT32 attrs = 0; UINTN size = sizeof(buf); - char expected[] = SBAT_VAR_PREVIOUS; + char expected[] = SBAT_VAR_AUTOMATIC; - status = set_sbat_uefi_variable(SBAT_VAR_PREVIOUS, SBAT_VAR_PREVIOUS); + status = set_sbat_uefi_variable(SBAT_VAR_AUTOMATIC, SBAT_VAR_AUTOMATIC); if (status != EFI_SUCCESS) return -1; -- cgit v1.2.3 From 6f395c23466a2bc08a28bbc216d6665ade0b117d Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Wed, 13 Dec 2023 20:32:10 -0800 Subject: Build time selectable automatic SBATLevel revocations The ability to automatically apply SBATLevel revocations varies from distro to distro. This allows distros that are able to automatically apply SBATLevel revocations when shim is updated to select a level by supplying SBAT_AUTOMATIC_DATE= on the make command line. Currently the following options are available: 2021030218 no revocations - useful for distros that need to rely on an externally delivered revocations.efi 2022052400 grub,2 2022111500 shim,2 grub,3 2023012900 shim,2 grub,3 grub.debian,4 If no datestamp is specified the build will default to the most recent 2023012900. Signed-off-by: Jan Setje-Eilers --- Make.defaults | 3 +++ include/sbat_var_defs.h | 33 ++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Make.defaults b/Make.defaults index 9af89f4e..e75cd3cd 100644 --- a/Make.defaults +++ b/Make.defaults @@ -188,6 +188,9 @@ endif ifneq ($(origin VENDOR_DBX_FILE), undefined) DEFINES += -DVENDOR_DBX_FILE=\"$(VENDOR_DBX_FILE)\" endif +ifneq ($(origin SBAT_AUTOMATIC_DATE), undefined) +DEFINES += -DSBAT_AUTOMATIC_DATE=$(SBAT_AUTOMATIC_DATE) +endif LDFLAGS = --hash-style=sysv -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(LOCAL_EFI_PATH) -L$(LIBDIR) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) --build-id=sha1 $(ARCH_LDFLAGS) --no-undefined diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index bd54cf5d..f8cba029 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -3,6 +3,9 @@ #ifndef SBAT_VAR_DEFS_H_ #define SBAT_VAR_DEFS_H_ +#define QUOTEVAL(s) QUOTE(s) +#define QUOTE(s) #s + /* * This is the entry for the sbat data format */ @@ -23,14 +26,34 @@ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #else /* !ENABLE_SHIM_DEVEL */ + /* - * At this point we do not want shim to automatically apply a - * revocation unless it is delivered by a separately installed - * signed revocations binary. + * Some distros may want to apply revocations from 2022052400 + * or 2022111500 automatically. They can be selected by setting + * SBAT_AUTOMATIC_DATE= at build time. Otherwise the + * default is to apply the second to most recent revocations + * automatically. Distros that need to manage automatic updates + * externally from shim can choose the epoch 2021030218 emtpy + * revocations. */ -#define SBAT_VAR_AUTOMATIC_DATE "2021030218" +#ifndef SBAT_AUTOMATIC_DATE +#define SBAT_AUTOMATIC_DATE 2023012900 +#endif /* SBAT_AUTOMATIC_DATE */ +#if SBAT_AUTOMATIC_DATE == 2021030218 +#define SBAT_VAR_AUTOMATIC_REVOCATIONS +#elif SBAT_AUTOMATIC_DATE == 2022052400 +#define SBAT_VAR_AUTOMATIC_REVOCATIONS "grub,2\n" +#elif SBAT_AUTOMATIC_DATE == 2022111500 +#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n" +#elif SBAT_AUTOMATIC_DATE == 2023012900 +#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" +#else +#error "Unknown SBAT_AUTOMATIC_DATE" +#endif /* SBAT_AUTOMATIC_DATE == */ +#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE) #define SBAT_VAR_AUTOMATIC \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" \ + SBAT_VAR_AUTOMATIC_REVOCATIONS /* * Revocations for January 2024 shim CVEs -- cgit v1.2.3 From a23e2f0de7a61b6e895a915676eba3a1fda2cd78 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 15 Dec 2023 21:31:48 -0800 Subject: netboot read_image() should not hardcode DEFAULT_LOADER The netboot path up until now hardcodes DEFAULT_LOADER as the only possible filename to load. This is pretty limiting and needs to be fixed. Signed-off-by: Jan Setje-Eilers --- httpboot.c | 8 +++++--- include/httpboot.h | 2 +- include/netboot.h | 2 +- netboot.c | 49 +++++++++++++++++++++++++++++++++---------------- shim.c | 33 ++++++++++++++++++++++++++++----- 5 files changed, 68 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/httpboot.c b/httpboot.c index b34dd49c..ac9ea25c 100644 --- a/httpboot.c +++ b/httpboot.c @@ -719,18 +719,20 @@ error: } EFI_STATUS -httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINT64 *buf_size) +httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINT64 *buf_size, + CHAR8 *name) { EFI_STATUS efi_status; EFI_HANDLE nic; - CHAR8 next_loader[sizeof DEFAULT_LOADER_CHAR]; + CHAR8 *next_loader; CHAR8 *next_uri = NULL; CHAR8 *hostname = NULL; if (!uri) return EFI_NOT_READY; - translate_slashes(next_loader, DEFAULT_LOADER_CHAR); + next_loader = (CHAR8 *)AllocatePool((strlen(name) + 1) * sizeof (CHAR8)); + translate_slashes(next_loader, name); /* Create the URI for the next loader based on the original URI */ efi_status = generate_next_uri(uri, next_loader, &next_uri); diff --git a/include/httpboot.h b/include/httpboot.h index ea9c57fe..119c546d 100644 --- a/include/httpboot.h +++ b/include/httpboot.h @@ -12,6 +12,6 @@ extern BOOLEAN find_httpboot(EFI_HANDLE device); extern EFI_STATUS httpboot_fetch_buffer(EFI_HANDLE image, VOID **buffer, - UINT64 *buf_size); + UINT64 *buf_size, CHAR8 *name); #endif /* SHIM_HTTPBOOT_H */ diff --git a/include/netboot.h b/include/netboot.h index 98b174a3..a7bf6cd8 100644 --- a/include/netboot.h +++ b/include/netboot.h @@ -5,7 +5,7 @@ extern BOOLEAN findNetboot(EFI_HANDLE image_handle); -extern EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle); +extern EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle, CHAR8 *name); extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINT64 *bufsiz); diff --git a/netboot.c b/netboot.c index 832deecd..d8b10935 100644 --- a/netboot.c +++ b/netboot.c @@ -160,25 +160,30 @@ static CHAR8 *str2ip6(CHAR8 *str) return (CHAR8 *)ip; } -static BOOLEAN extract_tftp_info(CHAR8 *url) +static BOOLEAN extract_tftp_info(CHAR8 *url, CHAR8 *name) { CHAR8 *start, *end; CHAR8 ip6str[40]; CHAR8 ip6inv[16]; - CHAR8 template[sizeof DEFAULT_LOADER_CHAR]; + int template_len = 0; + CHAR8 *template; - translate_slashes(template, DEFAULT_LOADER_CHAR); + while (name[template_len++] != '\0'); + template = (CHAR8 *)AllocatePool((template_len + 1) * sizeof (CHAR8)); + translate_slashes(template, name); // to check against str2ip6() errors memset(ip6inv, 0, sizeof(ip6inv)); if (strncmp((const char *)url, (const char *)"tftp://", 7)) { console_print(L"URLS MUST START WITH tftp://\n"); + FreePool(template); return FALSE; } start = url + 7; if (*start != '[') { console_print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n"); + FreePool(template); return FALSE; } @@ -188,22 +193,28 @@ static BOOLEAN extract_tftp_info(CHAR8 *url) end++; if (end - start >= (int)sizeof(ip6str)) { console_print(L"TFTP URL includes malformed IPv6 address\n"); + FreePool(template); return FALSE; } } if (*end == '\0') { console_print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n"); + FreePool(template); return FALSE; } memset(ip6str, 0, sizeof(ip6str)); memcpy(ip6str, start, end - start); end++; memcpy(&tftp_addr.v6, str2ip6(ip6str), 16); - if (memcmp(&tftp_addr.v6, ip6inv, sizeof(ip6inv)) == 0) + if (memcmp(&tftp_addr.v6, ip6inv, sizeof(ip6inv)) == 0) { + FreePool(template); return FALSE; + } full_path = AllocateZeroPool(strlen(end)+strlen(template)+1); - if (!full_path) + if (!full_path) { + FreePool(template); return FALSE; + } memcpy(full_path, end, strlen(end)); end = (CHAR8 *)strrchr((char *)full_path, '/'); if (!end) @@ -211,10 +222,11 @@ static BOOLEAN extract_tftp_info(CHAR8 *url) memcpy(end, template, strlen(template)); end[strlen(template)] = '\0'; + FreePool(template); return TRUE; } -static EFI_STATUS parseDhcp6() +static EFI_STATUS parseDhcp6(CHAR8 *name) { EFI_PXE_BASE_CODE_DHCPV6_PACKET *packet = (EFI_PXE_BASE_CODE_DHCPV6_PACKET *)&pxe->Mode->DhcpAck.Raw; CHAR8 *bootfile_url; @@ -222,7 +234,7 @@ static EFI_STATUS parseDhcp6() bootfile_url = get_v6_bootfile_url(packet); if (!bootfile_url) return EFI_NOT_FOUND; - if (extract_tftp_info(bootfile_url) == FALSE) { + if (extract_tftp_info(bootfile_url, name) == FALSE) { FreePool(bootfile_url); return EFI_NOT_FOUND; } @@ -230,14 +242,16 @@ static EFI_STATUS parseDhcp6() return EFI_SUCCESS; } -static EFI_STATUS parseDhcp4() +static EFI_STATUS parseDhcp4(CHAR8 *name) { - CHAR8 template[sizeof DEFAULT_LOADER_CHAR]; - INTN template_len; + CHAR8 *template; + INTN template_len = 0; UINTN template_ofs = 0; EFI_PXE_BASE_CODE_DHCPV4_PACKET* pkt_v4 = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)&pxe->Mode->DhcpAck.Dhcpv4; - translate_slashes(template, DEFAULT_LOADER_CHAR); + while (name[template_len++] != '\0'); + template = (CHAR8 *)AllocatePool((template_len + 1) * sizeof (CHAR8)); + translate_slashes(template, name); template_len = strlen(template) + 1; if(pxe->Mode->ProxyOfferReceived) { @@ -270,8 +284,10 @@ static EFI_STATUS parseDhcp4() full_path = AllocateZeroPool(dir_len + template_len); - if (!full_path) + if (!full_path) { + FreePool(template); return EFI_OUT_OF_RESOURCES; + } if (dir_len > 0) { strncpy(full_path, (CHAR8 *)dir, dir_len); @@ -292,10 +308,11 @@ static EFI_STATUS parseDhcp4() strcat(full_path, template + template_ofs); memcpy(&tftp_addr.v4, pkt_v4->BootpSiAddr, 4); + FreePool(template); return EFI_SUCCESS; } -EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED) +EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED, CHAR8 *netbootname) { EFI_STATUS efi_status; @@ -310,9 +327,9 @@ EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED) * if its ipv4 or ipv6 */ if (pxe->Mode->UsingIpv6){ - efi_status = parseDhcp6(); + efi_status = parseDhcp6(netbootname); } else - efi_status = parseDhcp4(); + efi_status = parseDhcp4(netbootname); return efi_status; } @@ -324,7 +341,7 @@ EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT BOOLEAN nobuffer = FALSE; UINTN blksz = 512; - console_print(L"Fetching Netboot Image\n"); + console_print(L"Fetching Netboot Image %a\n", full_path); if (*buffer == NULL) { *buffer = AllocatePool(4096 * 1024); if (!*buffer) diff --git a/shim.c b/shim.c index 3854fbde..fd196cd9 100644 --- a/shim.c +++ b/shim.c @@ -1070,6 +1070,23 @@ restore_loaded_image(VOID) CopyMem(shim_li, &shim_li_bak, sizeof(shim_li_bak)); } +/* If gets used on static data it probably needs boundary checking */ +void +str16_to_str8(CHAR16 *str16, CHAR8 **str8) +{ + int i = 0; + + while (str16[i++] != '\0'); + *str8 = (CHAR8 *)AllocatePool((i + 1) * sizeof (CHAR8)); + + i = 0; + while (str16[i] != '\0') { + (*str8)[i] = (CHAR8)str16[i]; + i++; + } + (*str8)[i] = '\0'; +} + /* * Load and run an EFI executable */ @@ -1079,6 +1096,7 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, EFI_STATUS efi_status; void *sourcebuffer = NULL; UINT64 sourcesize = 0; + CHAR8 *netbootname; /* * We need to refer to the loaded image protocol on the running @@ -1102,11 +1120,13 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, } if (findNetboot(shim_li->DeviceHandle)) { - efi_status = parseNetbootinfo(image_handle); + str16_to_str8(ImagePath, &netbootname); + efi_status = parseNetbootinfo(image_handle, netbootname); if (EFI_ERROR(efi_status)) { perror(L"Netboot parsing failed: %r\n", efi_status); return EFI_PROTOCOL_ERROR; } + FreePool(netbootname); efi_status = FetchNetbootimage(image_handle, &sourcebuffer, &sourcesize); if (EFI_ERROR(efi_status)) { @@ -1117,12 +1137,14 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, *data = sourcebuffer; *datasize = sourcesize; } else if (find_httpboot(shim_li->DeviceHandle)) { + str16_to_str8(ImagePath, &netbootname); efi_status = httpboot_fetch_buffer (image_handle, &sourcebuffer, - &sourcesize); + &sourcesize, + netbootname); if (EFI_ERROR(efi_status)) { - perror(L"Unable to fetch HTTP image: %r\n", - efi_status); + perror(L"Unable to fetch HTTP image %a: %r\n", + netbootname, efi_status); return efi_status; } *data = sourcebuffer; @@ -1566,7 +1588,8 @@ load_unbundled_trust(EFI_HANDLE image_handle) efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **)&drive); if (EFI_ERROR(efi_status)) { - perror(L"Failed to find fs: %r\n", efi_status); + dprint(L"Failed to find fs on local drive (netboot?): %r \n", + efi_status); goto done; } -- cgit v1.2.3 From 993a345dc3657d47f0e5e1c55cfddfd5f9866053 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 15 Dec 2023 14:49:04 -0800 Subject: Try to load revocations.efi even if directory read fails Network booting tends to expose things like a tfpt server as a filesystem that doesn't implement directory listing This will blindly try to ingest a revocations.efi file in those cases, even if that may result in some console noise when the file does not exist. Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 2 ++ shim.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index 4be0cbb1..bb523e7e 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -38,6 +38,8 @@ #define POLICY_RESET 3 #define POLICY_NOTREAD 255 +#define REVOCATIONFILE L"revocations.efi" + extern UINTN _sbat, _esbat; struct sbat_var_entry { diff --git a/shim.c b/shim.c index fd196cd9..633163a0 100644 --- a/shim.c +++ b/shim.c @@ -1590,6 +1590,13 @@ load_unbundled_trust(EFI_HANDLE image_handle) if (EFI_ERROR(efi_status)) { dprint(L"Failed to find fs on local drive (netboot?): %r \n", efi_status); + /* + * Network boot cases do not support reading a directory. Try + * to read revocations.efi to pull in any unbundled SBATLevel + * updates unconditionally in those cases. This may produce + * console noise when the file is not present. + */ + load_cert_file(image_handle, REVOCATIONFILE, PathName); goto done; } @@ -1668,7 +1675,7 @@ load_unbundled_trust(EFI_HANDLE image_handle) * revocations.efi file then to search for shim_certificate.efi */ if (search_revocations && - StrCaseCmp(info->FileName, L"revocations.efi") == 0) { + StrCaseCmp(info->FileName, REVOCATIONFILE) == 0) { load_revocations_file(image_handle, PathName); search_revocations = FALSE; efi_status = root->Open(root, &dir, PathName, -- cgit v1.2.3 From 63edf92f8ae11b884bc7d24aecb8229cbc4ae014 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 5 Apr 2024 21:57:07 +0200 Subject: sbat: Add grub.peimage,2 to latest (CVE-2024-2312) Add the previous latest level to the switch for automatic. Signed-off-by: Julian Andres Klode --- include/sbat_var_defs.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index f8cba029..04d708f2 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -47,6 +47,8 @@ #define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n" #elif SBAT_AUTOMATIC_DATE == 2023012900 #define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" +#elif SBAT_AUTOMATIC_DATE == 2024010900 +#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" #else #error "Unknown SBAT_AUTOMATIC_DATE" #endif /* SBAT_AUTOMATIC_DATE == */ @@ -56,10 +58,10 @@ SBAT_VAR_AUTOMATIC_REVOCATIONS /* - * Revocations for January 2024 shim CVEs + * Revocations for January 2024 shim CVEs + Debian/Ubuntu (peimage) CVE-2024-2312 */ -#define SBAT_VAR_LATEST_DATE "2024010900" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" +#define SBAT_VAR_LATEST_DATE "2024040500" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\ngrub.peimage,2\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From 3e1394e8e6fd0071a69196230f991612a960c154 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Tue, 9 Apr 2024 18:55:12 +0200 Subject: sbat: Also bump latest for grub,4 (and to todays date) Back in January we decided to bump the SBAT level for the shim CVE without bumping the grub level for the previous NTFS issues - CVE-2023-4692 CVE-2023-4693 - as not every vendor was signing the ntfs module. Catch up on this revocation to ensure it doesn't get lost. Doing so also allows us to remove the grub.debian,4 revocation as this happened before grub,4 and hence is obsolete. Also bump the date of the sbat variable to today's. Don't copy the April 5 one to a previous selection, as it wasn't shipped to anyone. Signed-off-by: Julian Andres Klode --- include/sbat_var_defs.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 04d708f2..5c7115b9 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -58,10 +58,13 @@ SBAT_VAR_AUTOMATIC_REVOCATIONS /* - * Revocations for January 2024 shim CVEs + Debian/Ubuntu (peimage) CVE-2024-2312 + * Revocations for: + * - January 2024 shim CVEs + * - October 2023 grub CVEs + * - Debian/Ubuntu (peimage) CVE-2024-2312 */ -#define SBAT_VAR_LATEST_DATE "2024040500" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\ngrub.peimage,2\n" +#define SBAT_VAR_LATEST_DATE "2024040900" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,4\ngrub.peimage,2\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS -- cgit v1.2.3 From 5f541823991d26f80f66f6e8e188c7cb246fe316 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 17 Dec 2024 11:00:07 -0500 Subject: includes: work around CLANG_PREREQ() double-definition Right now when doing test builds with clang, we wind up getting an error from two different definitions of CLANG_PREREQ() in the headers. It might be that we can just rip one of these out, but for now I'm just making one of them conditional. Signed-off-by: Peter Jones --- include/compiler.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index 8e8a658d..982bc235 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -205,6 +205,7 @@ #define GNUC_PREREQ(maj, min) 0 #endif +#if !defined(CLANG_PREREQ) #if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) #define CLANG_PREREQ(maj, min) \ ((__clang_major__ > (maj)) || \ @@ -212,6 +213,7 @@ #else #define CLANG_PREREQ(maj, min) 0 #endif +#endif /* CLANG_PREREQ */ #if GNUC_PREREQ(5, 1) || CLANG_PREREQ(3, 8) #define checked_add(addend0, addend1, sum) \ -- cgit v1.2.3 From d197220e834e7915326c6a99e17bafe0dcfb3f77 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 4 Oct 2024 11:00:32 -0600 Subject: Backport EFI_HTTP_ERROR status code The define can be dropped when gnu-efi is updated to include de6f9259e8476495c78babbc25250a59de7f3942. Signed-off-by: Dan Nicholson --- include/errors.h | 3 +++ lib/console.c | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/errors.h b/include/errors.h index 67d821e0..eab58453 100644 --- a/include/errors.h +++ b/include/errors.h @@ -9,5 +9,8 @@ #ifndef EFI_SECURITY_VIOLATION #define EFI_SECURITY_VIOLATION EFIERR(26) #endif +#ifndef EFI_HTTP_ERROR +#define EFI_HTTP_ERROR EFIERR(35) +#endif #endif /* SHIM_ERRORS_H */ diff --git a/lib/console.c b/lib/console.c index a751f79d..f6038320 100644 --- a/lib/console.c +++ b/lib/console.c @@ -651,6 +651,7 @@ static struct { { EFI_PROTOCOL_ERROR, L"Protocol Error"}, { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"}, { EFI_SECURITY_VIOLATION, L"Security Violation"}, + { EFI_HTTP_ERROR, L"HTTP Error"}, // warnings { EFI_WARN_UNKNOWN_GLYPH, L"Warning Unknown Glyph"}, -- cgit v1.2.3 From 1508ece179267943bad5851010eba8c00570c0ed Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 17 Jan 2025 14:13:46 -0500 Subject: Move is_removable_media_path() to a shared location. We need to use is_removable_media_path(), and potentially other helpers, from Mok as well as shim. This moves it to a file just for Device Path utility functions to make that simpler. Signed-off-by: Peter Jones --- Makefile | 6 +++--- dp.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/dp.h | 14 ++++++++++++++ shim.c | 33 --------------------------------- shim.h | 1 + 5 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 dp.c create mode 100644 include/dp.h (limited to 'include') diff --git a/Makefile b/Makefile index 3d9fd614..833bcd2a 100644 --- a/Makefile +++ b/Makefile @@ -38,10 +38,10 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S -MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o +ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S +MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o ORIG_FALLBACK_SRCS = fallback.c diff --git a/dp.c b/dp.c new file mode 100644 index 00000000..3fc46f8d --- /dev/null +++ b/dp.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.c - device path helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +int +is_removable_media_path(EFI_LOADED_IMAGE *li) +{ + unsigned int pathlen = 0; + CHAR16 *bootpath = NULL; + int ret = 0; + + bootpath = DevicePathToStr(li->FilePath); + + /* Check the beginning of the string and the end, to avoid + * caring about which arch this is. */ + /* I really don't know why, but sometimes bootpath gives us + * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... + */ + if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && + StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && + StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) + goto error; + + pathlen = StrLen(bootpath); + if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) + goto error; + + ret = 1; + +error: + if (bootpath) + FreePool(bootpath); + + return ret; +} + + +// vim:fenc=utf-8:tw=75:noet diff --git a/include/dp.h b/include/dp.h new file mode 100644 index 00000000..884c1460 --- /dev/null +++ b/include/dp.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * dp.h - device path helper functions + * Copyright Peter Jones + */ + +#ifndef DP_H_ +#define DP_H_ + +int +is_removable_media_path(EFI_LOADED_IMAGE *li); + +#endif /* !DP_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index bb54993f..c447c3d3 100644 --- a/shim.c +++ b/shim.c @@ -778,39 +778,6 @@ verify_buffer (char *data, int datasize, return verify_buffer_sbat(data, datasize, context); } -static int -is_removable_media_path(EFI_LOADED_IMAGE *li) -{ - unsigned int pathlen = 0; - CHAR16 *bootpath = NULL; - int ret = 0; - - bootpath = DevicePathToStr(li->FilePath); - - /* Check the beginning of the string and the end, to avoid - * caring about which arch this is. */ - /* I really don't know why, but sometimes bootpath gives us - * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... - */ - if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && - StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) && - StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14)) - goto error; - - pathlen = StrLen(bootpath); - if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) - goto error; - - ret = 1; - -error: - if (bootpath) - FreePool(bootpath); - - return ret; -} - static int should_use_fallback(EFI_HANDLE image_handle) { diff --git a/shim.h b/shim.h index 5791a031..c0d8aa95 100644 --- a/shim.h +++ b/shim.h @@ -163,6 +163,7 @@ #include "include/configtable.h" #include "include/console.h" #include "include/crypt_blowfish.h" +#include "include/dp.h" #include "include/efiauthenticated.h" #include "include/errors.h" #include "include/execute.h" -- cgit v1.2.3 From f7e1d7226de8b9fc005257b8fede70093c547ad5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 20 Sep 2021 17:09:18 -0400 Subject: tests: make it possible to use different limits for variable space This splits up the API for setting default usage limits, adding a test API function to set alternate usage limits. Signed-off-by: Peter Jones --- include/mock-variables.h | 2 ++ mock-variables.c | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index 9f276e63..3f5c5e60 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -115,6 +115,8 @@ void mock_uninstall_query_variable_info(void); void mock_reset_variables(void); void mock_reset_config_table(void); void mock_finalize_vars_and_configs(void); +void mock_set_usage_limits(list_t *limit_list, + struct mock_variable_limits *limits); typedef enum { NONE = 0, diff --git a/mock-variables.c b/mock-variables.c index 5dc2356d..656670d5 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -1001,6 +1001,20 @@ static struct mock_variable_limits default_limits[] = { {.attrs = 0, } }; +void +mock_set_usage_limits(list_t *limit_list, + struct mock_variable_limits *limits) +{ + INIT_LIST_HEAD(limit_list); + for (size_t i = 0; limits[i].attrs != 0; i++) { + INIT_LIST_HEAD(&limits[i].list); + list_add_tail(&limits[i].list, limit_list); + } + + mock_qvi_limits = limit_list; + mock_sv_limits = limit_list; +} + void mock_set_default_usage_limits(void) { @@ -1008,12 +1022,7 @@ mock_set_default_usage_limits(void) default_remaining_var_storage = 65536; default_max_var_size = 32768; - INIT_LIST_HEAD(&mock_default_variable_limits); - for (size_t i = 0; default_limits[i].attrs != 0; i++) { - INIT_LIST_HEAD(&default_limits[i].list); - list_add_tail(&default_limits[i].list, - &mock_default_variable_limits); - } + mock_set_usage_limits(&mock_default_variable_limits, &default_limits[0]); } void -- cgit v1.2.3 From 3caa75e5693368f44478a1da272095cebc0b4c78 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sat, 18 Jan 2025 13:37:02 -0500 Subject: test-mok-mirror: minor bug fix In 70366a286552760863bacb521fb00c654586b494, I introduced a test case for test-mok-mirror to test the behavior when SetVariable() gives EFI_OUT_OF_RESOURCES. Unfortunately this includes a memory error in its cleanup functions: ==1972634== Invalid read of size 8 ==1972634== at 0x4032F3: mock_sv_adjust_usage_data (mock-variables.c:468) ==1972634== by 0x40387B: mock_delete_variable (mock-variables.c:541) ==1972634== by 0x4014E8: mock_reset_variables (mock-variables.c:1353) ==1972634== by 0x401348: mock_finalize_vars_and_configs (mock-variables.c:1405) ==1972634== by 0x401731: main (test-mok-mirror.c:688) ==1972634== Address 0x1ffeffc4f8 is on thread 1's stack ==1972634== 6784 bytes below stack pointer This is caused because the test data uses a linked-list data structure in an array in the test data, and we try to iterate the list to free the data, but of course the list pointers aren't initialized because it's an array. Whoops. This patch makes it so we don't try to clean up that list, because we don't need to. Signed-off-by: Peter Jones --- include/mock-variables.h | 1 + test-mok-mirror.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/mock-variables.h b/include/mock-variables.h index 3f5c5e60..b7ee1cb4 100644 --- a/include/mock-variables.h +++ b/include/mock-variables.h @@ -117,6 +117,7 @@ void mock_reset_config_table(void); void mock_finalize_vars_and_configs(void); void mock_set_usage_limits(list_t *limit_list, struct mock_variable_limits *limits); +void mock_set_default_usage_limits(void); typedef enum { NONE = 0, diff --git a/test-mok-mirror.c b/test-mok-mirror.c index 97b92959..0c45cead 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -633,6 +633,8 @@ test_mok_mirror_setvar_out_of_resources(void) test_mok_config_table, EFI_OUT_OF_RESOURCES); + mock_set_default_usage_limits(); + mock_set_variable_post_hook = NULL; mock_get_variable_post_hook = NULL; return ret; -- cgit v1.2.3 From e42c31963a5a1ed2edf8a40df81ccfe697c32981 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 22 Jan 2025 16:33:11 -0500 Subject: test.mk: don't use a temporary random.bin Sometimes using a temp file to make test-random.h breaks builds because clean and test get made in parallel. Since debugging parallel make is anything but easy, it's better to just not use the intermediate file. This patch makes it use a pipe instead. Because we live in the worst possible world, we have to support RHEL 9, in which xxd does not support "-n prefix", and so we have to build the header variable definition lines in shell. Signed-off-by: Peter Jones --- include/test.mk | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/test.mk b/include/test.mk index e6d46594..ee2d2fde 100644 --- a/include/test.mk +++ b/include/test.mk @@ -76,8 +76,12 @@ libefi-test.a : clean test-random.h: - dd if=/dev/urandom bs=512 count=17 of=random.bin - xxd -i random.bin test-random.h + dd if=/dev/urandom bs=512 count=17 status=none | ( \ + echo "unsigned char random_bin[] = {" ; \ + xxd -i - ; \ + echo "};" ; \ + echo "unsigned int random_bin_len = 8704;" ; \ + ) > test-random.h $(wildcard test-*.c) :: %.c : test-random.h $(patsubst %.c,%,$(wildcard test-*.c)) :: | test-random.h @@ -119,7 +123,7 @@ test-coverage : CFLAGS_GCOV+=--coverage test-coverage : $(tests) test-clean : - @rm -vf test-random.h random.bin libefi-test.a + @rm -vf test-random.h libefi-test.a @rm -vf *.gcda *.gcno *.gcov vgcore.* clean : test-clean @@ -127,6 +131,5 @@ clean : test-clean all : test-clean test .PHONY: $(tests) all test clean -.SECONDARY: random.bin # vim:ft=make -- cgit v1.2.3 From 5ae408aede0a410f28de92a5fdc5ce406f2c4515 Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Mon, 3 Feb 2025 20:15:09 -0500 Subject: Generate and use generated_sbat_var_defs.h Build changes to generate include/generated_sbat_var_defs.h from SbatLevel_Variable.txt and use that header file. From here on forward SbatLevel_Variable.txt should be the only place a new revocation needs to be recorded. Signed-off-by: Jan Setje-Eilers --- .gitignore | 1 + Makefile | 15 +++++++++++---- include/sbat_var_defs.h | 47 ++++------------------------------------------- sbat_var.S | 1 + test-sbat.c | 1 + 5 files changed, 18 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/.gitignore b/.gitignore index d04b21aa..d8296699 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ Make.local /fuzz-* !/fuzz-*.c /generate_sbat_var_defs +/generated_sbat_var_defs.h /leak-* /post-process-pe /random.bin diff --git a/Makefile b/Makefile index 266e764c..97a35b7c 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,6 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -# This is temporary and will go away soon -TARGETS += generate_sbat_var_defs OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S @@ -102,6 +100,7 @@ shim.crt: shim.cer: shim.crt $(OPENSSL) x509 -outform der -in $< -out $@ + .NOTPARALLEL: shim_cert.h shim_cert.h: shim.cer echo "static UINT8 shim_cert[] __attribute__((__unused__)) = {" > $@ @@ -123,8 +122,12 @@ shim.o: $(SOURCES) ifneq ($(origin ENABLE_SHIM_CERT),undefined) shim.o: shim_cert.h endif +# Both of these need to be here so that when TOPDIR is unset, make isn't trying +# to match against ./sbat_var.S, which isn't a target it will ever try to build. +$(TOPDIR)/sbat_var.S sbat_var.S: generated_sbat_var_defs.h shim.o: $(wildcard $(TOPDIR)/*.h) + sbat.%.csv : data/sbat.%.csv $(DOS2UNIX) $(D2UFLAGS) $< $@ tail -c1 $@ | read -r _ || echo >> $@ # ensure a trailing newline @@ -193,6 +196,10 @@ post-process-pe : $(TOPDIR)/post-process-pe.c generate_sbat_var_defs: $(TOPDIR)/generate_sbat_var_defs.c $(HOSTCC) -std=gnu11 -Og -g3 -Wall -Wextra -Wno-missing-field-initializers -Werror -o $@ $< +.NOTPARALLEL: generated_sbat_var_defs.h +generated_sbat_var_defs.h: generate_sbat_var_defs + ./generate_sbat_var_defs $(TOPDIR) > $@ + buildid : $(TOPDIR)/buildid.c $(HOSTCC) -I/usr/include -Og -g3 -Wall -Werror -Wextra -o $@ $< -lelf @@ -317,7 +324,7 @@ fuzz fuzz-clean fuzz-coverage fuzz-lto : EFI_INCLUDES="$(EFI_INCLUDES)" \ fuzz-clean $@ -test test-clean test-coverage test-lto : +test test-clean test-coverage test-lto : generated_sbat_var_defs.h @make -f $(TOPDIR)/include/test.mk \ COMPILER="$(COMPILER)" \ CROSS_COMPILE="$(CROSS_COMPILE)" \ @@ -361,7 +368,7 @@ clean-lib-objs: clean-shim-objs: @rm -rvf $(TARGET) *.o $(SHIM_OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb $(BOOTCSVNAME) @rm -vf *.debug *.so *.efi *.efi.* *.tar.* version.c buildid post-process-pe compile_commands.json - @rm -vf generate_sbat_var_defs + @rm -vf generate_sbat_var_defs generated_sbat_var_defs.h @rm -vf Cryptlib/*.[oa] Cryptlib/*/*.[oa] @if [ -d .git ] ; then git clean -f -d -e 'Cryptlib/OpenSSL/*'; fi diff --git a/include/sbat_var_defs.h b/include/sbat_var_defs.h index 5c7115b9..f4f5a27b 100644 --- a/include/sbat_var_defs.h +++ b/include/sbat_var_defs.h @@ -7,7 +7,9 @@ #define QUOTE(s) #s /* - * This is the entry for the sbat data format + * SbatLevel Epoch and SHIM_DEVEL definitions are here + * Actual revocations are now soley defined in + * SbatLevel_Variable.txt */ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," @@ -22,51 +24,10 @@ #define SBAT_VAR_LATEST_DATE "2022050100" #define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" -#define SBAT_VAR_LATEST \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ - SBAT_VAR_LATEST_REVOCATIONS -#else /* !ENABLE_SHIM_DEVEL */ - -/* - * Some distros may want to apply revocations from 2022052400 - * or 2022111500 automatically. They can be selected by setting - * SBAT_AUTOMATIC_DATE= at build time. Otherwise the - * default is to apply the second to most recent revocations - * automatically. Distros that need to manage automatic updates - * externally from shim can choose the epoch 2021030218 emtpy - * revocations. - */ -#ifndef SBAT_AUTOMATIC_DATE -#define SBAT_AUTOMATIC_DATE 2023012900 -#endif /* SBAT_AUTOMATIC_DATE */ -#if SBAT_AUTOMATIC_DATE == 2021030218 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS -#elif SBAT_AUTOMATIC_DATE == 2022052400 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "grub,2\n" -#elif SBAT_AUTOMATIC_DATE == 2022111500 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\n" -#elif SBAT_AUTOMATIC_DATE == 2023012900 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,2\ngrub,3\ngrub.debian,4\n" -#elif SBAT_AUTOMATIC_DATE == 2024010900 -#define SBAT_VAR_AUTOMATIC_REVOCATIONS "shim,4\ngrub,3\ngrub.debian,4\n" -#else -#error "Unknown SBAT_AUTOMATIC_DATE" -#endif /* SBAT_AUTOMATIC_DATE == */ -#define SBAT_VAR_AUTOMATIC_DATE QUOTEVAL(SBAT_AUTOMATIC_DATE) -#define SBAT_VAR_AUTOMATIC \ - SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_AUTOMATIC_DATE "\n" \ - SBAT_VAR_AUTOMATIC_REVOCATIONS -/* - * Revocations for: - * - January 2024 shim CVEs - * - October 2023 grub CVEs - * - Debian/Ubuntu (peimage) CVE-2024-2312 - */ -#define SBAT_VAR_LATEST_DATE "2024040900" -#define SBAT_VAR_LATEST_REVOCATIONS "shim,4\ngrub,4\ngrub.peimage,2\n" #define SBAT_VAR_LATEST \ SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ SBAT_VAR_LATEST_REVOCATIONS #endif /* ENABLE_SHIM_DEVEL */ + #endif /* !SBAT_VAR_DEFS_H_ */ diff --git a/sbat_var.S b/sbat_var.S index ed82a46c..72924067 100644 --- a/sbat_var.S +++ b/sbat_var.S @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-2-Clause-Patent #include "include/sbat_var_defs.h" +#include "generated_sbat_var_defs.h" .section .sbatlevel, "a", %progbits .balignl 4, 0 diff --git a/test-sbat.c b/test-sbat.c index b37efcdd..21f2b24c 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -8,6 +8,7 @@ #include "sbat_var_defs.h" #endif #include "shim.h" +#include "generated_sbat_var_defs.h" #include -- cgit v1.2.3 From 38dfa37ec218bda0dd6bb5c4ce9e6ecb05fda851 Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Tue, 29 Aug 2023 13:33:15 -0400 Subject: Create utils file Move some functions currently used within fallback that will later also be used in shim. Signed-off-by: Eric Snowberg --- Makefile | 2 +- fallback.c | 83 ------------------------------------------------------- include/utils.h | 9 ++++++ shim.h | 1 + utils.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 84 deletions(-) create mode 100644 include/utils.h create mode 100644 utils.c (limited to 'include') diff --git a/Makefile b/Makefile index 97a35b7c..257f343b 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) -FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o +FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o utils.o ORIG_FALLBACK_SRCS = fallback.c SBATPATH = $(TOPDIR)/data/sbat.csv diff --git a/fallback.c b/fallback.c index c5e35e5a..5a4786d6 100644 --- a/fallback.c +++ b/fallback.c @@ -94,89 +94,6 @@ FindSubDevicePath(EFI_DEVICE_PATH *In, UINT8 Type, UINT8 SubType, return EFI_NOT_FOUND; } -static EFI_STATUS -get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) -{ - EFI_STATUS efi_status; - void *buffer = NULL; - UINTN bs = 0; - - /* The API here is "Call it once with bs=0, it fills in bs, - * then allocate a buffer and ask again to get it filled. */ - efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); - if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) - return efi_status; - if (bs == 0) - return EFI_SUCCESS; - - buffer = AllocateZeroPool(bs); - if (!buffer) { - console_print(L"Could not allocate memory\n"); - return EFI_OUT_OF_RESOURCES; - } - efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); - /* This checks *either* the error from the first GetInfo, if it isn't - * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo - * call in *any* case. */ - if (EFI_ERROR(efi_status)) { - console_print(L"Could not get file info: %r\n", efi_status); - if (buffer) - FreePool(buffer); - return efi_status; - } - EFI_FILE_INFO *fi = buffer; - *retsize = fi->FileSize; - FreePool(buffer); - return EFI_SUCCESS; -} - -EFI_STATUS -read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) -{ - EFI_FILE_HANDLE fh2; - EFI_STATUS efi_status; - - efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0); - if (EFI_ERROR(efi_status)) { - console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status); - return efi_status; - } - - UINTN len = 0; - CHAR16 *b = NULL; - efi_status = get_file_size(fh2, &len); - if (EFI_ERROR(efi_status)) { - console_print(L"Could not get file size for \"%s\": %r\n", - fullpath, efi_status); - fh2->Close(fh2); - return efi_status; - } - - if (len > 1024 * PAGE_SIZE) { - fh2->Close(fh2); - return EFI_BAD_BUFFER_SIZE; - } - - b = AllocateZeroPool(len + 2); - if (!b) { - console_print(L"Could not allocate memory\n"); - fh2->Close(fh2); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = fh->Read(fh, &len, b); - if (EFI_ERROR(efi_status)) { - FreePool(b); - fh2->Close(fh2); - console_print(L"Could not read file: %r\n", efi_status); - return efi_status; - } - *buffer = b; - *bs = len; - fh2->Close(fh2); - return EFI_SUCCESS; -} - EFI_STATUS make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) { diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 00000000..654f05d8 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +#ifndef UTILS_H_ +#define UTILS_H_ + +EFI_STATUS get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize); +EFI_STATUS +read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs); + +#endif /* UTILS_H_ */ diff --git a/shim.h b/shim.h index c0d8aa95..43fcb191 100644 --- a/shim.h +++ b/shim.h @@ -188,6 +188,7 @@ #include "include/simple_file.h" #include "include/str.h" #include "include/tpm.h" +#include "include/utils.h" #include "include/cc.h" #include "include/ucs2.h" #include "include/variables.h" diff --git a/utils.c b/utils.c new file mode 100644 index 00000000..02722294 --- /dev/null +++ b/utils.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +#include "shim.h" + +EFI_STATUS +get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) +{ + EFI_STATUS efi_status; + void *buffer = NULL; + UINTN bs = 0; + + /* The API here is "Call it once with bs=0, it fills in bs, + * then allocate a buffer and ask again to get it filled. */ + efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); + if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) + return efi_status; + if (bs == 0) + return EFI_SUCCESS; + + buffer = AllocateZeroPool(bs); + if (!buffer) { + console_print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); + /* This checks *either* the error from the first GetInfo, if it isn't + * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo + * call in *any* case. */ + if (EFI_ERROR(efi_status)) { + console_print(L"Could not get file info: %r\n", efi_status); + if (buffer) + FreePool(buffer); + return efi_status; + } + EFI_FILE_INFO *fi = buffer; + *retsize = fi->FileSize; + FreePool(buffer); + return EFI_SUCCESS; +} + +EFI_STATUS +read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) +{ + EFI_FILE_HANDLE fh2; + EFI_STATUS efi_status; + + efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(efi_status)) { + console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status); + return efi_status; + } + + UINTN len = 0; + CHAR16 *b = NULL; + efi_status = get_file_size(fh2, &len); + if (EFI_ERROR(efi_status)) { + console_print(L"Could not get file size for \"%s\": %r\n", + fullpath, efi_status); + fh2->Close(fh2); + return efi_status; + } + + if (len > 1024 * PAGE_SIZE) { + fh2->Close(fh2); + return EFI_BAD_BUFFER_SIZE; + } + + b = AllocateZeroPool(len + 2); + if (!b) { + console_print(L"Could not allocate memory\n"); + fh2->Close(fh2); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = fh->Read(fh, &len, b); + if (EFI_ERROR(efi_status)) { + FreePool(b); + fh2->Close(fh2); + console_print(L"Could not read file: %r\n", efi_status); + return efi_status; + } + *buffer = b; + *bs = len; + fh2->Close(fh2); + return EFI_SUCCESS; +} -- cgit v1.2.3 From 83850cd8df2db60a00b96e7757c6ff9c1d8cccec Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Tue, 29 Aug 2023 17:25:42 -0400 Subject: Add configuration option to boot an alternative 2nd stage Add the ability for shim to load an optional configuration file. This new file is called "options.csv". The configuration file is completely optional. If used, it is located in the same directory as the booted shim. The "options.csv" file currently allows a single entry. Other options could be added to it in the future. The first and only entry in the file is the name of the secondary boot loader shim will load. The "options.csv" file is in Unicode LE format. This allows a signed shim to directly load a UKI without the need to rename it to grub. Shim's transitive trust is maintained. If the alternative 2nd stage can not be verified, it will not boot. Signed-off-by: Eric Snowberg --- Makefile | 2 +- include/load-options.h | 1 + load-options.c | 1 + shim.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index 257f343b..1c3190b9 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o +OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o diff --git a/include/load-options.h b/include/load-options.h index d2bee3bb..78b1dcce 100644 --- a/include/load-options.h +++ b/include/load-options.h @@ -13,6 +13,7 @@ EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li, EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li); extern CHAR16 *second_stage; +extern CHAR16 *optional_second_stage; extern void *load_options; extern UINT32 load_options_size; diff --git a/load-options.c b/load-options.c index e7b64471..091cb18b 100644 --- a/load-options.c +++ b/load-options.c @@ -6,6 +6,7 @@ #include "shim.h" CHAR16 *second_stage; +CHAR16 *optional_second_stage = NULL; void *load_options; UINT32 load_options_size; diff --git a/shim.c b/shim.c index 42b5bbcb..14355d27 100644 --- a/shim.c +++ b/shim.c @@ -1256,7 +1256,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) EFI_STATUS efi_status; EFI_LOADED_IMAGE *li = NULL; - second_stage = DEFAULT_LOADER; + second_stage = (optional_second_stage) ? optional_second_stage : DEFAULT_LOADER; load_options = NULL; load_options_size = 0; @@ -1689,6 +1689,86 @@ done: return efi_status; } +/* Read optional options file */ +EFI_STATUS +load_shim_options(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_HANDLE device; + EFI_LOADED_IMAGE *li = NULL; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE *root; + EFI_FILE_HANDLE ofile; + CHAR16 *PathName = NULL; + CHAR16 *buffer; + UINTN comma0; + UINT64 bs; + + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to init protocol\n"); + return efi_status; + } + + efi_status = generate_path_from_image_path(li, L"options.csv", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + + efi_status = BS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **) &drive); + if (EFI_ERROR(efi_status)) + goto done; + + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open fs: %r\n", efi_status); + goto done; + } + + efi_status = root->Open(root, &ofile, PathName, EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(efi_status)) { + if (efi_status != EFI_NOT_FOUND) + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + dprint(L"Loading optional second stage info from options.csv\n"); + efi_status = read_file(ofile, PathName, &buffer, &bs); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to read file\n"); + goto done; + } + + /* + * This file may or may not start with the Unicode byte order marker. + * Since UEFI is defined as LE, this file must also be LE. + * If we find the LE byte order marker, just skip its. + */ + if (*buffer == 0xfeff) + buffer++; + + comma0 = StrCSpn(buffer, L","); + if (comma0 == 0) { + perror(L"Invalid csv file\n"); + goto done; + } + + /* + * Currently the options.csv file allows one entry for the optional + * secondary boot stage, anything afterwards is skipped. + */ + buffer[comma0] = L'\0'; + dprint(L"Optional 2nd stage:\"%s\"\n", buffer); + optional_second_stage=buffer; + +done: + FreePool(PathName); + return EFI_SUCCESS; +} + EFI_STATUS shim_init(void) { @@ -1973,6 +2053,8 @@ die: */ (void) del_variable(SHIM_RETAIN_PROTOCOL_VAR_NAME, SHIM_LOCK_GUID); + load_shim_options(image_handle); + efi_status = shim_init(); if (EFI_ERROR(efi_status)) { msg = SHIM_INIT; -- cgit v1.2.3 From bb114a3b92a96875dc71e5e4925bedba5c02f958 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 29 Jun 2023 17:58:18 +0200 Subject: Implement shim image load protocol Define a new protocol for loading and starting images, encapsulating shim's PE loading facilities and verification/authentication against the same set of certificates that shim_lock::verify() authenticates against. This removes the need for loaders like GRUB to implement their own PE loader in order to be able to invoke loaded images as PE applications, rather than implementing a bespoke OS dependent handover protocol (e.g., invoke Linux via its EFI stub) Signed-off-by: Ard Biesheuvel --- include/guid.h | 2 ++ lib/guid.c | 2 ++ replacements.c | 11 ++++++ shim.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- shim.h | 18 ++++++++++ 5 files changed, 134 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/guid.h b/include/guid.h index 898c4fad..e32dfc07 100644 --- a/include/guid.h +++ b/include/guid.h @@ -36,6 +36,8 @@ extern EFI_GUID SECURITY_PROTOCOL_GUID; extern EFI_GUID SECURITY2_PROTOCOL_GUID; extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; extern EFI_GUID SHIM_LOCK_GUID; +extern EFI_GUID SHIM_IMAGE_LOADER_GUID; +extern EFI_GUID SHIM_LOADED_IMAGE_GUID; extern EFI_GUID MOK_VARIABLE_STORE; extern EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID; diff --git a/lib/guid.c b/lib/guid.c index 6e92cea3..1dc90ca9 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -35,5 +35,7 @@ EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} }; EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; +EFI_GUID SHIM_IMAGE_LOADER_GUID = {0x1f492041, 0xfadb, 0x4e59, {0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } }; +EFI_GUID SHIM_LOADED_IMAGE_GUID = {0x6e6baeb8, 0x7108, 0x4179, {0x94, 0x9d, 0xa3, 0x49, 0x34, 0x15, 0xec, 0x97 } }; EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} }; EFI_GUID SECUREBOOT_EFI_NAMESPACE_GUID = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b} }; diff --git a/replacements.c b/replacements.c index 469e73aa..d840616f 100644 --- a/replacements.c +++ b/replacements.c @@ -148,6 +148,17 @@ do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, UINTN ExitDataSize, CHAR16 *ExitData) { EFI_STATUS efi_status; + SHIM_LOADED_IMAGE *image; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + if (!EFI_ERROR(efi_status)) { + image->exit_status = ExitStatus; + image->exit_data_size = ExitDataSize; + image->exit_data = ExitData; + + longjmp(image->longjmp_buf, 1); + } shim_fini(); diff --git a/shim.c b/shim.c index 14355d27..60b5e720 100644 --- a/shim.c +++ b/shim.c @@ -1314,6 +1314,7 @@ init_openssl(void) } static SHIM_LOCK shim_lock_interface; +static SHIM_IMAGE_LOADER shim_image_loader_interface; static EFI_HANDLE shim_lock_handle; EFI_STATUS @@ -1346,10 +1347,12 @@ install_shim_protocols(void) /* * Install the protocol */ - efi_status = BS->InstallProtocolInterface(&shim_lock_handle, - &SHIM_LOCK_GUID, - EFI_NATIVE_INTERFACE, - &shim_lock_interface); + efi_status = BS->InstallMultipleProtocolInterfaces(&shim_lock_handle, + &SHIM_LOCK_GUID, + &shim_lock_interface, + &SHIM_IMAGE_LOADER_GUID, + &shim_image_loader_interface, + NULL); if (EFI_ERROR(efi_status)) { console_error(L"Could not install security protocol", efi_status); @@ -1375,8 +1378,12 @@ uninstall_shim_protocols(void) /* * If we're back here then clean everything up before exiting */ - BS->UninstallProtocolInterface(shim_lock_handle, &SHIM_LOCK_GUID, - &shim_lock_interface); + BS->UninstallMultipleProtocolInterfaces(shim_lock_handle, + &SHIM_LOCK_GUID, + &shim_lock_interface, + &SHIM_IMAGE_LOADER_GUID, + &shim_image_loader_interface, + NULL); if (!secure_mode()) return; @@ -1908,6 +1915,91 @@ devel_egress(devel_egress_action action UNUSED) #endif } +static EFI_STATUS EFIAPI +shim_load_image(IN BOOLEAN BootPolicy, IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH *FilePath, IN VOID *SourceBuffer, + IN UINTN SourceSize, OUT EFI_HANDLE *ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + (void)FilePath; + + if (BootPolicy || !SourceBuffer || !SourceSize) + return EFI_UNSUPPORTED; + + image = AllocatePool(sizeof(*image)); + if (!image) + return EFI_OUT_OF_RESOURCES; + + SetMem(image, sizeof(*image), 0); + + image->li.Revision = 0x1000; + image->li.ParentHandle = ParentImageHandle; + image->li.SystemTable = systab; + + efi_status = handle_image(SourceBuffer, SourceSize, &image->li, + &image->entry_point, &image->alloc_address, + &image->alloc_pages); + if (EFI_ERROR(efi_status)) + goto free_image; + + *ImageHandle = NULL; + efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle, + &SHIM_LOADED_IMAGE_GUID, image, + &EFI_LOADED_IMAGE_GUID, &image->li, + NULL); + if (EFI_ERROR(efi_status)) + goto free_alloc; + + return EFI_SUCCESS; + +free_alloc: + BS->FreePages(image->alloc_address, image->alloc_pages); +free_image: + FreePool(image); + return efi_status; +} + +static EFI_STATUS EFIAPI +shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + if (EFI_ERROR(efi_status) || image->started) + return EFI_INVALID_PARAMETER; + + if (!setjmp(image->longjmp_buf)) { + image->started = true; + efi_status = + image->entry_point(ImageHandle, image->li.SystemTable); + } else { + if (ExitData) { + *ExitDataSize = image->exit_data_size; + *ExitData = (CHAR16 *)image->exit_data; + } + efi_status = image->exit_status; + } + + // + // We only support EFI applications, so we can unload and free the + // image unconditionally. + // + BS->UninstallMultipleProtocolInterfaces(ImageHandle, + &EFI_LOADED_IMAGE_GUID, image, + &SHIM_LOADED_IMAGE_GUID, &image->li, + NULL); + + BS->FreePages(image->alloc_address, image->alloc_pages); + FreePool(image); + + return efi_status; +} + EFI_STATUS efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) { @@ -1951,6 +2043,9 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) shim_lock_interface.Hash = shim_hash; shim_lock_interface.Context = shim_read_header; + shim_image_loader_interface.LoadImage = shim_load_image; + shim_image_loader_interface.StartImage = shim_start_image; + systab = passed_systab; image_handle = global_image_handle = passed_image_handle; diff --git a/shim.h b/shim.h index 43fcb191..704e34ea 100644 --- a/shim.h +++ b/shim.h @@ -55,6 +55,7 @@ #ifndef SHIM_UNIT_TEST #include #include +#include #undef uefi_call_wrapper #include #include @@ -237,6 +238,11 @@ typedef struct _SHIM_LOCK { EFI_SHIM_LOCK_CONTEXT Context; } SHIM_LOCK; +typedef struct _SHIM_IMAGE_LOADER { + EFI_IMAGE_LOAD LoadImage; + EFI_IMAGE_START StartImage; +} SHIM_IMAGE_LOADER; + extern EFI_STATUS shim_init(void); extern void shim_fini(void); extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, @@ -326,4 +332,16 @@ verify_buffer (char *data, int datasize, char *translate_slashes(char *out, const char *str); +typedef struct { + EFI_LOADED_IMAGE li; + EFI_IMAGE_ENTRY_POINT entry_point; + EFI_PHYSICAL_ADDRESS alloc_address; + UINTN alloc_pages; + EFI_STATUS exit_status; + CONST CHAR16 *exit_data; + UINTN exit_data_size; + jmp_buf longjmp_buf; + BOOLEAN started; +} SHIM_LOADED_IMAGE; + #endif /* SHIM_H_ */ -- cgit v1.2.3 From e7b3598311c4b002417ac6364093cfab218ced88 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 30 Jun 2023 13:24:57 -0400 Subject: Move some stuff around This moves some things around to help with loader protocol changes: - Move replacements.c to loader-proto.c - likewise with replacements.h - move the SHIM_IMAGE_LOADER decl to loader-proto.h - move the LoadImage / StartImage interface setup to an init function - move shim_load_image() / shim_start_image() to loader-proto.c Signed-off-by: Peter Jones --- Makefile | 4 +- globals.c | 2 + include/loader-proto.h | 35 ++++++ include/replacements.h | 30 ----- loader-proto.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++ replacements.c | 233 ----------------------------------- shim.c | 91 +------------- shim.h | 11 +- 8 files changed, 371 insertions(+), 360 deletions(-) create mode 100644 include/loader-proto.h delete mode 100644 include/replacements.h create mode 100644 loader-proto.c delete mode 100644 replacements.c (limited to 'include') diff --git a/Makefile b/Makefile index 1c3190b9..bf339fab 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o +OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o loader-proto.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c replacements.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S +ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c loader-proto.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o utils.o diff --git a/globals.c b/globals.c index b4e80dd3..91916e98 100644 --- a/globals.c +++ b/globals.c @@ -26,6 +26,8 @@ UINT8 *build_cert; verification_method_t verification_method; int loader_is_participating; +SHIM_IMAGE_LOADER shim_image_loader_interface; + UINT8 user_insecure_mode; UINT8 ignore_db; UINT8 trust_mok_list; diff --git a/include/loader-proto.h b/include/loader-proto.h new file mode 100644 index 00000000..d3afa2f5 --- /dev/null +++ b/include/loader-proto.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent + +/* + * Copyright Red Hat, Inc + * Copyright Peter Jones + */ +#ifndef SHIM_REPLACEMENTS_H +#define SHIM_REPLACEMENTS_H + +extern EFI_SYSTEM_TABLE *get_active_systab(void); + +typedef enum { + VERIFIED_BY_NOTHING, + VERIFIED_BY_CERT, + VERIFIED_BY_HASH +} verification_method_t; + +extern verification_method_t verification_method; +extern int loader_is_participating; + +extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_system_services(void); + +extern void hook_exit(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_exit(void); + +typedef struct _SHIM_IMAGE_LOADER { + EFI_IMAGE_LOAD LoadImage; + EFI_IMAGE_START StartImage; +} SHIM_IMAGE_LOADER; + +extern SHIM_IMAGE_LOADER shim_image_loader_interface; +extern void init_image_loader(void); + +#endif /* SHIM_REPLACEMENTS_H */ diff --git a/include/replacements.h b/include/replacements.h deleted file mode 100644 index 8b35c857..00000000 --- a/include/replacements.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause-Patent - -/* - * Copyright Red Hat, Inc - * Copyright Peter Jones - */ -#ifndef SHIM_REPLACEMENTS_H -#define SHIM_REPLACEMENTS_H - -extern EFI_SYSTEM_TABLE *get_active_systab(void); - -typedef enum { - VERIFIED_BY_NOTHING, - VERIFIED_BY_CERT, - VERIFIED_BY_HASH -} verification_method_t; - -extern verification_method_t verification_method; -extern int loader_is_participating; - -extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); -extern void unhook_system_services(void); - -extern void hook_exit(EFI_SYSTEM_TABLE *local_systab); -extern void unhook_exit(void); - -extern EFI_STATUS install_shim_protocols(void); -extern void uninstall_shim_protocols(void); - -#endif /* SHIM_REPLACEMENTS_H */ diff --git a/loader-proto.c b/loader-proto.c new file mode 100644 index 00000000..a61a91db --- /dev/null +++ b/loader-proto.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * loader-proto.c - shim's loader protocol + * + * Copyright Red Hat, Inc + */ + +/* Chemical agents lend themselves to covert use in sabotage against + * which it is exceedingly difficult to visualize any really effective + * defense... I will not dwell upon this use of CBW because, as one + * pursues the possibilities of such covert uses, one discovers that the + * scenarios resemble that in which the components of a nuclear weapon + * are smuggled into New York City and assembled in the basement of the + * Empire State Building. + * In other words, once the possibility is recognized to exist, about + * all that one can do is worry about it. + * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on + * National Security Policy and Scientific Developments, November 20, + * 1969. + */ +#include "shim.h" + +static EFI_SYSTEM_TABLE *systab; + +EFI_SYSTEM_TABLE * +get_active_systab(void) +{ + if (systab) + return systab; + return ST; +} + +static typeof(systab->BootServices->LoadImage) system_load_image; +static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->Exit) system_exit; +#if !defined(DISABLE_EBS_PROTECTION) +static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; +#endif /* !defined(DISABLE_EBS_PROTECTION) */ + +static EFI_HANDLE last_loaded_image; + +void +unhook_system_services(void) +{ + if (!systab) + return; + + systab->BootServices->LoadImage = system_load_image; + systab->BootServices->StartImage = system_start_image; +#if !defined(DISABLE_EBS_PROTECTION) + systab->BootServices->ExitBootServices = system_exit_boot_services; +#endif /* !defined(DISABLE_EBS_PROTECTION) */ + BS = systab->BootServices; +} + +static EFI_STATUS EFIAPI +shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *FilePath, VOID *SourceBuffer, + UINTN SourceSize, EFI_HANDLE *ImageHandle) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + (void)FilePath; + + if (BootPolicy || !SourceBuffer || !SourceSize) + return EFI_UNSUPPORTED; + + image = AllocatePool(sizeof(*image)); + if (!image) + return EFI_OUT_OF_RESOURCES; + + SetMem(image, sizeof(*image), 0); + + image->li.Revision = 0x1000; + image->li.ParentHandle = ParentImageHandle; + image->li.SystemTable = systab; + + efi_status = handle_image(SourceBuffer, SourceSize, &image->li, + &image->entry_point, &image->alloc_address, + &image->alloc_pages); + if (EFI_ERROR(efi_status)) + goto free_image; + + *ImageHandle = NULL; + efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle, + &SHIM_LOADED_IMAGE_GUID, image, + &EFI_LOADED_IMAGE_GUID, &image->li, + NULL); + if (EFI_ERROR(efi_status)) + goto free_alloc; + + return EFI_SUCCESS; + +free_alloc: + BS->FreePages(image->alloc_address, image->alloc_pages); +free_image: + FreePool(image); + return efi_status; +} + +static EFI_STATUS EFIAPI +shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL) +{ + SHIM_LOADED_IMAGE *image; + EFI_STATUS efi_status; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + if (EFI_ERROR(efi_status) || image->started) + return EFI_INVALID_PARAMETER; + + if (!setjmp(image->longjmp_buf)) { + image->started = true; + efi_status = + image->entry_point(ImageHandle, image->li.SystemTable); + } else { + if (ExitData) { + *ExitDataSize = image->exit_data_size; + *ExitData = (CHAR16 *)image->exit_data; + } + efi_status = image->exit_status; + } + + // + // We only support EFI applications, so we can unload and free the + // image unconditionally. + // + BS->UninstallMultipleProtocolInterfaces(ImageHandle, + &EFI_LOADED_IMAGE_GUID, image, + &SHIM_LOADED_IMAGE_GUID, &image->li, + NULL); + + BS->FreePages(image->alloc_address, image->alloc_pages); + FreePool(image); + + return efi_status; +} + +static EFI_STATUS EFIAPI +load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, + UINTN SourceSize, EFI_HANDLE *ImageHandle) +{ + EFI_STATUS efi_status; + + unhook_system_services(); + efi_status = BS->LoadImage(BootPolicy, ParentImageHandle, DevicePath, + SourceBuffer, SourceSize, ImageHandle); + hook_system_services(systab); + if (EFI_ERROR(efi_status)) + last_loaded_image = NULL; + else + last_loaded_image = *ImageHandle; + return efi_status; +} + +static EFI_STATUS EFIAPI +replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) +{ + EFI_STATUS efi_status; + unhook_system_services(); + + if (image_handle == last_loaded_image) { + UINT8 retain_protocol = 0; + UINTN retain_protocol_size = sizeof(retain_protocol); + UINT32 retain_protocol_attrs = 0; + + loader_is_participating = 1; + + /* If a boot component asks us, keep our protocol around - it will be used to + * validate further PE payloads (e.g.: by the UKI stub, before the kernel is booted). + * But also check that the variable was set by a boot component, to ensure that + * nobody at runtime can attempt to change shim's behaviour. */ + efi_status = RT->GetVariable(SHIM_RETAIN_PROTOCOL_VAR_NAME, + &SHIM_LOCK_GUID, + &retain_protocol_attrs, + &retain_protocol_size, + &retain_protocol); + if (EFI_ERROR(efi_status) || + (retain_protocol_attrs & EFI_VARIABLE_NON_VOLATILE) || + !(retain_protocol_attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS) || + retain_protocol_size != sizeof(retain_protocol) || + retain_protocol == 0) + uninstall_shim_protocols(); + } + efi_status = BS->StartImage(image_handle, exit_data_size, exit_data); + if (EFI_ERROR(efi_status)) { + if (image_handle == last_loaded_image) { + EFI_STATUS efi_status2 = install_shim_protocols(); + + if (EFI_ERROR(efi_status2)) { + console_print(L"Something has gone seriously wrong: %r\n", + efi_status2); + console_print(L"shim cannot continue, sorry.\n"); + usleep(5000000); + RT->ResetSystem(EfiResetShutdown, + EFI_SECURITY_VIOLATION, + 0, NULL); + } + } + hook_system_services(systab); + loader_is_participating = 0; + } + return efi_status; +} + +#if !defined(DISABLE_EBS_PROTECTION) +static EFI_STATUS EFIAPI +exit_boot_services(EFI_HANDLE image_key, UINTN map_key) +{ + if (loader_is_participating || + verification_method == VERIFIED_BY_HASH) { + unhook_system_services(); + EFI_STATUS efi_status; + efi_status = BS->ExitBootServices(image_key, map_key); + if (EFI_ERROR(efi_status)) + hook_system_services(systab); + return efi_status; + } + + console_print(L"Bootloader has not verified loaded image.\n"); + console_print(L"System is compromised. halting.\n"); + usleep(5000000); + RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); + return EFI_SECURITY_VIOLATION; +} +#endif /* !defined(DISABLE_EBS_PROTECTION) */ + +static EFI_STATUS EFIAPI +do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, + UINTN ExitDataSize, CHAR16 *ExitData) +{ + EFI_STATUS efi_status; + SHIM_LOADED_IMAGE *image; + + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); + if (!EFI_ERROR(efi_status)) { + image->exit_status = ExitStatus; + image->exit_data_size = ExitDataSize; + image->exit_data = ExitData; + + longjmp(image->longjmp_buf, 1); + } + + shim_fini(); + + restore_loaded_image(); + + efi_status = BS->Exit(ImageHandle, ExitStatus, + ExitDataSize, ExitData); + if (EFI_ERROR(efi_status)) { + EFI_STATUS efi_status2 = shim_init(); + + if (EFI_ERROR(efi_status2)) { + console_print(L"Something has gone seriously wrong: %r\n", + efi_status2); + console_print(L"shim cannot continue, sorry.\n"); + usleep(5000000); + RT->ResetSystem(EfiResetShutdown, + EFI_SECURITY_VIOLATION, 0, NULL); + } + } + return efi_status; +} + +void +init_image_loader(void) +{ + shim_image_loader_interface.LoadImage = shim_load_image; + shim_image_loader_interface.StartImage = shim_start_image; +} + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = systab->BootServices; + + /* We need to hook various calls to make this work... */ + + /* We need LoadImage() hooked so that fallback.c can load shim + * without having to fake LoadImage as well. This allows it + * to call the system LoadImage(), and have us track the output + * and mark loader_is_participating in replacement_start_image. This + * means anything added by fallback has to be verified by the system + * db, which we want to preserve anyway, since that's all launching + * through BDS gives us. */ + system_load_image = systab->BootServices->LoadImage; + systab->BootServices->LoadImage = load_image; + + /* we need StartImage() so that we can allow chain booting to an + * image trusted by the firmware */ + system_start_image = systab->BootServices->StartImage; + systab->BootServices->StartImage = replacement_start_image; + +#if !defined(DISABLE_EBS_PROTECTION) + /* we need to hook ExitBootServices() so a) we can enforce the policy + * and b) we can unwrap when we're done. */ + system_exit_boot_services = systab->BootServices->ExitBootServices; + systab->BootServices->ExitBootServices = exit_boot_services; +#endif /* defined(DISABLE_EBS_PROTECTION) */ +} + +void +unhook_exit(void) +{ + systab->BootServices->Exit = system_exit; + BS = systab->BootServices; +} + +void +hook_exit(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + BS = local_systab->BootServices; + + /* we need to hook Exit() so that we can allow users to quit the + * bootloader and still e.g. start a new one or run an internal + * shell. */ + system_exit = systab->BootServices->Exit; + systab->BootServices->Exit = do_exit; +} diff --git a/replacements.c b/replacements.c deleted file mode 100644 index d840616f..00000000 --- a/replacements.c +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause-Patent -/* - * shim - trivial UEFI first-stage bootloader - * - * Copyright Red Hat, Inc - */ - -/* Chemical agents lend themselves to covert use in sabotage against - * which it is exceedingly difficult to visualize any really effective - * defense... I will not dwell upon this use of CBW because, as one - * pursues the possibilities of such covert uses, one discovers that the - * scenarios resemble that in which the components of a nuclear weapon - * are smuggled into New York City and assembled in the basement of the - * Empire State Building. - * In other words, once the possibility is recognized to exist, about - * all that one can do is worry about it. - * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on - * National Security Policy and Scientific Developments, November 20, - * 1969. - */ -#include "shim.h" - -static EFI_SYSTEM_TABLE *systab; - -EFI_SYSTEM_TABLE * -get_active_systab(void) -{ - if (systab) - return systab; - return ST; -} - -static typeof(systab->BootServices->LoadImage) system_load_image; -static typeof(systab->BootServices->StartImage) system_start_image; -static typeof(systab->BootServices->Exit) system_exit; -#if !defined(DISABLE_EBS_PROTECTION) -static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - -static EFI_HANDLE last_loaded_image; - -void -unhook_system_services(void) -{ - if (!systab) - return; - - systab->BootServices->LoadImage = system_load_image; - systab->BootServices->StartImage = system_start_image; -#if !defined(DISABLE_EBS_PROTECTION) - systab->BootServices->ExitBootServices = system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - BS = systab->BootServices; -} - -static EFI_STATUS EFIAPI -load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, - EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, - UINTN SourceSize, EFI_HANDLE *ImageHandle) -{ - EFI_STATUS efi_status; - - unhook_system_services(); - efi_status = BS->LoadImage(BootPolicy, ParentImageHandle, DevicePath, - SourceBuffer, SourceSize, ImageHandle); - hook_system_services(systab); - if (EFI_ERROR(efi_status)) - last_loaded_image = NULL; - else - last_loaded_image = *ImageHandle; - return efi_status; -} - -static EFI_STATUS EFIAPI -replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) -{ - EFI_STATUS efi_status; - unhook_system_services(); - - if (image_handle == last_loaded_image) { - UINT8 retain_protocol = 0; - UINTN retain_protocol_size = sizeof(retain_protocol); - UINT32 retain_protocol_attrs = 0; - - loader_is_participating = 1; - - /* If a boot component asks us, keep our protocol around - it will be used to - * validate further PE payloads (e.g.: by the UKI stub, before the kernel is booted). - * But also check that the variable was set by a boot component, to ensure that - * nobody at runtime can attempt to change shim's behaviour. */ - efi_status = RT->GetVariable(SHIM_RETAIN_PROTOCOL_VAR_NAME, - &SHIM_LOCK_GUID, - &retain_protocol_attrs, - &retain_protocol_size, - &retain_protocol); - if (EFI_ERROR(efi_status) || - (retain_protocol_attrs & EFI_VARIABLE_NON_VOLATILE) || - !(retain_protocol_attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS) || - retain_protocol_size != sizeof(retain_protocol) || - retain_protocol == 0) - uninstall_shim_protocols(); - } - efi_status = BS->StartImage(image_handle, exit_data_size, exit_data); - if (EFI_ERROR(efi_status)) { - if (image_handle == last_loaded_image) { - EFI_STATUS efi_status2 = install_shim_protocols(); - - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, - 0, NULL); - } - } - hook_system_services(systab); - loader_is_participating = 0; - } - return efi_status; -} - -#if !defined(DISABLE_EBS_PROTECTION) -static EFI_STATUS EFIAPI -exit_boot_services(EFI_HANDLE image_key, UINTN map_key) -{ - if (loader_is_participating || - verification_method == VERIFIED_BY_HASH) { - unhook_system_services(); - EFI_STATUS efi_status; - efi_status = BS->ExitBootServices(image_key, map_key); - if (EFI_ERROR(efi_status)) - hook_system_services(systab); - return efi_status; - } - - console_print(L"Bootloader has not verified loaded image.\n"); - console_print(L"System is compromised. halting.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); - return EFI_SECURITY_VIOLATION; -} -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - -static EFI_STATUS EFIAPI -do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, - UINTN ExitDataSize, CHAR16 *ExitData) -{ - EFI_STATUS efi_status; - SHIM_LOADED_IMAGE *image; - - efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, - (void **)&image); - if (!EFI_ERROR(efi_status)) { - image->exit_status = ExitStatus; - image->exit_data_size = ExitDataSize; - image->exit_data = ExitData; - - longjmp(image->longjmp_buf, 1); - } - - shim_fini(); - - restore_loaded_image(); - - efi_status = BS->Exit(ImageHandle, ExitStatus, - ExitDataSize, ExitData); - if (EFI_ERROR(efi_status)) { - EFI_STATUS efi_status2 = shim_init(); - - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, 0, NULL); - } - } - return efi_status; -} - -void -hook_system_services(EFI_SYSTEM_TABLE *local_systab) -{ - systab = local_systab; - BS = systab->BootServices; - - /* We need to hook various calls to make this work... */ - - /* We need LoadImage() hooked so that fallback.c can load shim - * without having to fake LoadImage as well. This allows it - * to call the system LoadImage(), and have us track the output - * and mark loader_is_participating in replacement_start_image. This - * means anything added by fallback has to be verified by the system - * db, which we want to preserve anyway, since that's all launching - * through BDS gives us. */ - system_load_image = systab->BootServices->LoadImage; - systab->BootServices->LoadImage = load_image; - - /* we need StartImage() so that we can allow chain booting to an - * image trusted by the firmware */ - system_start_image = systab->BootServices->StartImage; - systab->BootServices->StartImage = replacement_start_image; - -#if !defined(DISABLE_EBS_PROTECTION) - /* we need to hook ExitBootServices() so a) we can enforce the policy - * and b) we can unwrap when we're done. */ - system_exit_boot_services = systab->BootServices->ExitBootServices; - systab->BootServices->ExitBootServices = exit_boot_services; -#endif /* defined(DISABLE_EBS_PROTECTION) */ -} - -void -unhook_exit(void) -{ - systab->BootServices->Exit = system_exit; - BS = systab->BootServices; -} - -void -hook_exit(EFI_SYSTEM_TABLE *local_systab) -{ - systab = local_systab; - BS = local_systab->BootServices; - - /* we need to hook Exit() so that we can allow users to quit the - * bootloader and still e.g. start a new one or run an internal - * shell. */ - system_exit = systab->BootServices->Exit; - systab->BootServices->Exit = do_exit; -} diff --git a/shim.c b/shim.c index 60b5e720..98462aa0 100644 --- a/shim.c +++ b/shim.c @@ -1314,7 +1314,6 @@ init_openssl(void) } static SHIM_LOCK shim_lock_interface; -static SHIM_IMAGE_LOADER shim_image_loader_interface; static EFI_HANDLE shim_lock_handle; EFI_STATUS @@ -1344,6 +1343,8 @@ install_shim_protocols(void) if (!EFI_ERROR(efi_status)) uninstall_shim_protocols(); + init_image_loader(); + /* * Install the protocol */ @@ -1915,91 +1916,6 @@ devel_egress(devel_egress_action action UNUSED) #endif } -static EFI_STATUS EFIAPI -shim_load_image(IN BOOLEAN BootPolicy, IN EFI_HANDLE ParentImageHandle, - IN EFI_DEVICE_PATH *FilePath, IN VOID *SourceBuffer, - IN UINTN SourceSize, OUT EFI_HANDLE *ImageHandle) -{ - SHIM_LOADED_IMAGE *image; - EFI_STATUS efi_status; - - (void)FilePath; - - if (BootPolicy || !SourceBuffer || !SourceSize) - return EFI_UNSUPPORTED; - - image = AllocatePool(sizeof(*image)); - if (!image) - return EFI_OUT_OF_RESOURCES; - - SetMem(image, sizeof(*image), 0); - - image->li.Revision = 0x1000; - image->li.ParentHandle = ParentImageHandle; - image->li.SystemTable = systab; - - efi_status = handle_image(SourceBuffer, SourceSize, &image->li, - &image->entry_point, &image->alloc_address, - &image->alloc_pages); - if (EFI_ERROR(efi_status)) - goto free_image; - - *ImageHandle = NULL; - efi_status = BS->InstallMultipleProtocolInterfaces(ImageHandle, - &SHIM_LOADED_IMAGE_GUID, image, - &EFI_LOADED_IMAGE_GUID, &image->li, - NULL); - if (EFI_ERROR(efi_status)) - goto free_alloc; - - return EFI_SUCCESS; - -free_alloc: - BS->FreePages(image->alloc_address, image->alloc_pages); -free_image: - FreePool(image); - return efi_status; -} - -static EFI_STATUS EFIAPI -shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, - OUT CHAR16 **ExitData OPTIONAL) -{ - SHIM_LOADED_IMAGE *image; - EFI_STATUS efi_status; - - efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, - (void **)&image); - if (EFI_ERROR(efi_status) || image->started) - return EFI_INVALID_PARAMETER; - - if (!setjmp(image->longjmp_buf)) { - image->started = true; - efi_status = - image->entry_point(ImageHandle, image->li.SystemTable); - } else { - if (ExitData) { - *ExitDataSize = image->exit_data_size; - *ExitData = (CHAR16 *)image->exit_data; - } - efi_status = image->exit_status; - } - - // - // We only support EFI applications, so we can unload and free the - // image unconditionally. - // - BS->UninstallMultipleProtocolInterfaces(ImageHandle, - &EFI_LOADED_IMAGE_GUID, image, - &SHIM_LOADED_IMAGE_GUID, &image->li, - NULL); - - BS->FreePages(image->alloc_address, image->alloc_pages); - FreePool(image); - - return efi_status; -} - EFI_STATUS efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) { @@ -2043,9 +1959,6 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) shim_lock_interface.Hash = shim_hash; shim_lock_interface.Context = shim_read_header; - shim_image_loader_interface.LoadImage = shim_load_image; - shim_image_loader_interface.StartImage = shim_start_image; - systab = passed_systab; image_handle = global_image_handle = passed_image_handle; diff --git a/shim.h b/shim.h index 704e34ea..a3f8a505 100644 --- a/shim.h +++ b/shim.h @@ -174,12 +174,12 @@ #include "include/ip4config2.h" #include "include/ip6config.h" #include "include/load-options.h" +#include "include/loader-proto.h" #include "include/mok.h" #include "include/netboot.h" #include "include/passwordcrypt.h" #include "include/peimage.h" #include "include/pe.h" -#include "include/replacements.h" #include "include/sbat.h" #include "include/sbat_var_defs.h" #include "include/ssp.h" @@ -238,11 +238,6 @@ typedef struct _SHIM_LOCK { EFI_SHIM_LOCK_CONTEXT Context; } SHIM_LOCK; -typedef struct _SHIM_IMAGE_LOADER { - EFI_IMAGE_LOAD LoadImage; - EFI_IMAGE_START StartImage; -} SHIM_IMAGE_LOADER; - extern EFI_STATUS shim_init(void); extern void shim_fini(void); extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, @@ -256,6 +251,8 @@ extern VOID ClearErrors(VOID); extern VOID restore_loaded_image(VOID); extern EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath); extern EFI_STATUS import_mok_state(EFI_HANDLE image_handle); +extern EFI_STATUS install_shim_protocols(void); +extern void uninstall_shim_protocols(void); extern UINT32 vendor_authorized_size; extern UINT8 *vendor_authorized; @@ -332,6 +329,8 @@ verify_buffer (char *data, int datasize, char *translate_slashes(char *out, const char *str); +#include + typedef struct { EFI_LOADED_IMAGE li; EFI_IMAGE_ENTRY_POINT entry_point; -- cgit v1.2.3 From 0322e10ecc0eb6a4acbea3f83f71b19a559aaec6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 30 Jun 2023 14:48:17 -0400 Subject: Implement the rest of the loader protocol functions This adds an implementation of Exit() and UnloadImage(), removes the whole "loader_is_participating" mechanism and its supporting code, and removes DISABLE_EBS_PROTECTION. Signed-off-by: Peter Jones --- BUILDING | 9 --- Make.defaults | 4 - globals.c | 1 - include/loader-proto.h | 3 +- loader-proto.c | 202 +++++++++++++++++-------------------------------- shim.c | 4 - 6 files changed, 70 insertions(+), 153 deletions(-) (limited to 'include') diff --git a/BUILDING b/BUILDING index bdd98dd4..5005868a 100644 --- a/BUILDING +++ b/BUILDING @@ -37,15 +37,6 @@ Variables you could set to customize the build: debugger only on the development branch and not the OS you need to boot to scp in a new development build. Likewise, we look for SHIM_DEVEL_VERBOSE rather than SHIM_VERBOSE. -- DISABLE_EBS_PROTECTION - On systems where a second stage bootloader is not used, and the Linux - Kernel is embedded in the same EFI image as shim and booted directly - from shim, shim's ExitBootServices() hook can cause problems as the - kernel never calls the shim's verification protocol. In this case - calling the shim verification protocol is unnecessary and redundant as - shim has already verified the kernel when shim loaded the kernel as the - second stage loader. In such a case, and only in this case, you should - use DISABLE_EBS_PROTECTION=y to build. - DISABLE_REMOVABLE_LOAD_OPTIONS Do not parse load options when invoked as boot*.efi. This prevents boot failures because of unexpected data in boot entries automatically generated diff --git a/Make.defaults b/Make.defaults index ab11e838..c5fa32be 100644 --- a/Make.defaults +++ b/Make.defaults @@ -149,10 +149,6 @@ ifneq ($(origin REQUIRE_TPM), undefined) DEFINES += -DREQUIRE_TPM endif -ifneq ($(origin DISABLE_EBS_PROTECTION), undefined) - DEFINES += -DDISABLE_EBS_PROTECTION -endif - ifneq ($(origin DISABLE_REMOVABLE_LOAD_OPTIONS), undefined) DEFINES += -DDISABLE_REMOVABLE_LOAD_OPTIONS endif diff --git a/globals.c b/globals.c index 91916e98..d574df16 100644 --- a/globals.c +++ b/globals.c @@ -24,7 +24,6 @@ UINT8 *build_cert; * indicator of how an image has been verified */ verification_method_t verification_method; -int loader_is_participating; SHIM_IMAGE_LOADER shim_image_loader_interface; diff --git a/include/loader-proto.h b/include/loader-proto.h index d3afa2f5..db8e670e 100644 --- a/include/loader-proto.h +++ b/include/loader-proto.h @@ -16,7 +16,6 @@ typedef enum { } verification_method_t; extern verification_method_t verification_method; -extern int loader_is_participating; extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); extern void unhook_system_services(void); @@ -27,6 +26,8 @@ extern void unhook_exit(void); typedef struct _SHIM_IMAGE_LOADER { EFI_IMAGE_LOAD LoadImage; EFI_IMAGE_START StartImage; + EFI_EXIT Exit; + EFI_IMAGE_UNLOAD UnloadImage; } SHIM_IMAGE_LOADER; extern SHIM_IMAGE_LOADER shim_image_loader_interface; diff --git a/loader-proto.c b/loader-proto.c index a61a91db..f0df122c 100644 --- a/loader-proto.c +++ b/loader-proto.c @@ -32,12 +32,8 @@ get_active_systab(void) static typeof(systab->BootServices->LoadImage) system_load_image; static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->UnloadImage) system_unload_image; static typeof(systab->BootServices->Exit) system_exit; -#if !defined(DISABLE_EBS_PROTECTION) -static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ - -static EFI_HANDLE last_loaded_image; void unhook_system_services(void) @@ -47,9 +43,8 @@ unhook_system_services(void) systab->BootServices->LoadImage = system_load_image; systab->BootServices->StartImage = system_start_image; -#if !defined(DISABLE_EBS_PROTECTION) - systab->BootServices->ExitBootServices = system_exit_boot_services; -#endif /* !defined(DISABLE_EBS_PROTECTION) */ + systab->BootServices->Exit = system_exit; + systab->BootServices->UnloadImage = system_unload_image; BS = systab->BootServices; } @@ -108,6 +103,14 @@ shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, (void **)&image); + + /* + * This image didn't come from shim_load_image(), so it must have come + * from something before shim was involved. + */ + if (efi_status == EFI_UNSUPPORTED) + return system_start_image(ImageHandle, ExitDataSize, ExitData); + if (EFI_ERROR(efi_status) || image->started) return EFI_INVALID_PARAMETER; @@ -139,131 +142,54 @@ shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, } static EFI_STATUS EFIAPI -load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, - EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, - UINTN SourceSize, EFI_HANDLE *ImageHandle) +shim_unload_image(EFI_HANDLE ImageHandle) { + SHIM_LOADED_IMAGE *image; EFI_STATUS efi_status; - unhook_system_services(); - efi_status = BS->LoadImage(BootPolicy, ParentImageHandle, DevicePath, - SourceBuffer, SourceSize, ImageHandle); - hook_system_services(systab); - if (EFI_ERROR(efi_status)) - last_loaded_image = NULL; - else - last_loaded_image = *ImageHandle; - return efi_status; -} + efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, + (void **)&image); -static EFI_STATUS EFIAPI -replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) -{ - EFI_STATUS efi_status; - unhook_system_services(); - - if (image_handle == last_loaded_image) { - UINT8 retain_protocol = 0; - UINTN retain_protocol_size = sizeof(retain_protocol); - UINT32 retain_protocol_attrs = 0; - - loader_is_participating = 1; - - /* If a boot component asks us, keep our protocol around - it will be used to - * validate further PE payloads (e.g.: by the UKI stub, before the kernel is booted). - * But also check that the variable was set by a boot component, to ensure that - * nobody at runtime can attempt to change shim's behaviour. */ - efi_status = RT->GetVariable(SHIM_RETAIN_PROTOCOL_VAR_NAME, - &SHIM_LOCK_GUID, - &retain_protocol_attrs, - &retain_protocol_size, - &retain_protocol); - if (EFI_ERROR(efi_status) || - (retain_protocol_attrs & EFI_VARIABLE_NON_VOLATILE) || - !(retain_protocol_attrs & EFI_VARIABLE_BOOTSERVICE_ACCESS) || - retain_protocol_size != sizeof(retain_protocol) || - retain_protocol == 0) - uninstall_shim_protocols(); - } - efi_status = BS->StartImage(image_handle, exit_data_size, exit_data); - if (EFI_ERROR(efi_status)) { - if (image_handle == last_loaded_image) { - EFI_STATUS efi_status2 = install_shim_protocols(); - - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, - 0, NULL); - } - } - hook_system_services(systab); - loader_is_participating = 0; - } - return efi_status; -} + if (efi_status == EFI_UNSUPPORTED) + return system_unload_image(ImageHandle); -#if !defined(DISABLE_EBS_PROTECTION) -static EFI_STATUS EFIAPI -exit_boot_services(EFI_HANDLE image_key, UINTN map_key) -{ - if (loader_is_participating || - verification_method == VERIFIED_BY_HASH) { - unhook_system_services(); - EFI_STATUS efi_status; - efi_status = BS->ExitBootServices(image_key, map_key); - if (EFI_ERROR(efi_status)) - hook_system_services(systab); - return efi_status; - } + BS->FreePages(image->alloc_address, image->alloc_pages); + FreePool(image); - console_print(L"Bootloader has not verified loaded image.\n"); - console_print(L"System is compromised. halting.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); - return EFI_SECURITY_VIOLATION; + return EFI_SUCCESS; } -#endif /* !defined(DISABLE_EBS_PROTECTION) */ static EFI_STATUS EFIAPI -do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, - UINTN ExitDataSize, CHAR16 *ExitData) +shim_exit(EFI_HANDLE ImageHandle, + EFI_STATUS ExitStatus, + UINTN ExitDataSize, + CHAR16 *ExitData) { EFI_STATUS efi_status; SHIM_LOADED_IMAGE *image; efi_status = BS->HandleProtocol(ImageHandle, &SHIM_LOADED_IMAGE_GUID, (void **)&image); - if (!EFI_ERROR(efi_status)) { - image->exit_status = ExitStatus; - image->exit_data_size = ExitDataSize; - image->exit_data = ExitData; - longjmp(image->longjmp_buf, 1); + /* + * If this happens, something above us on the stack of running + * applications called Exit(), and we're getting aborted along with + * it. + */ + if (efi_status == EFI_UNSUPPORTED) { + shim_fini(); + return system_exit(ImageHandle, ExitStatus, ExitDataSize, + ExitData); } - shim_fini(); - - restore_loaded_image(); + if (EFI_ERROR(efi_status)) + return efi_status; - efi_status = BS->Exit(ImageHandle, ExitStatus, - ExitDataSize, ExitData); - if (EFI_ERROR(efi_status)) { - EFI_STATUS efi_status2 = shim_init(); + image->exit_status = ExitStatus; + image->exit_data_size = ExitDataSize; + image->exit_data = ExitData; - if (EFI_ERROR(efi_status2)) { - console_print(L"Something has gone seriously wrong: %r\n", - efi_status2); - console_print(L"shim cannot continue, sorry.\n"); - usleep(5000000); - RT->ResetSystem(EfiResetShutdown, - EFI_SECURITY_VIOLATION, 0, NULL); - } - } - return efi_status; + longjmp(image->longjmp_buf, 1); } void @@ -271,6 +197,8 @@ init_image_loader(void) { shim_image_loader_interface.LoadImage = shim_load_image; shim_image_loader_interface.StartImage = shim_start_image; + shim_image_loader_interface.Exit = shim_exit; + shim_image_loader_interface.UnloadImage = shim_unload_image; } void @@ -281,27 +209,31 @@ hook_system_services(EFI_SYSTEM_TABLE *local_systab) /* We need to hook various calls to make this work... */ - /* We need LoadImage() hooked so that fallback.c can load shim - * without having to fake LoadImage as well. This allows it - * to call the system LoadImage(), and have us track the output - * and mark loader_is_participating in replacement_start_image. This - * means anything added by fallback has to be verified by the system - * db, which we want to preserve anyway, since that's all launching - * through BDS gives us. */ + /* + * We need LoadImage() hooked so that we can guarantee everything is + * verified. + */ system_load_image = systab->BootServices->LoadImage; - systab->BootServices->LoadImage = load_image; + systab->BootServices->LoadImage = shim_load_image; - /* we need StartImage() so that we can allow chain booting to an - * image trusted by the firmware */ + /* + * We need StartImage() hooked because the system's StartImage() + * doesn't know about our structure layout. + */ system_start_image = systab->BootServices->StartImage; - systab->BootServices->StartImage = replacement_start_image; - -#if !defined(DISABLE_EBS_PROTECTION) - /* we need to hook ExitBootServices() so a) we can enforce the policy - * and b) we can unwrap when we're done. */ - system_exit_boot_services = systab->BootServices->ExitBootServices; - systab->BootServices->ExitBootServices = exit_boot_services; -#endif /* defined(DISABLE_EBS_PROTECTION) */ + systab->BootServices->StartImage = shim_start_image; + + /* + * We need Exit() hooked so that we make sure to use the right jmp_buf + * when an application calls Exit(), but that happens in a separate + * function. + */ + + /* + * We need UnloadImage() to match our LoadImage() + */ + system_unload_image = systab->BootServices->UnloadImage; + systab->BootServices->UnloadImage = shim_unload_image; } void @@ -317,9 +249,11 @@ hook_exit(EFI_SYSTEM_TABLE *local_systab) systab = local_systab; BS = local_systab->BootServices; - /* we need to hook Exit() so that we can allow users to quit the + /* + * We need to hook Exit() so that we can allow users to quit the * bootloader and still e.g. start a new one or run an internal - * shell. */ + * shell. + */ system_exit = systab->BootServices->Exit; - systab->BootServices->Exit = do_exit; + systab->BootServices->Exit = shim_exit; } diff --git a/shim.c b/shim.c index 98462aa0..bf0c9e6c 100644 --- a/shim.c +++ b/shim.c @@ -959,7 +959,6 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) if ((INT32)size < 0) return EFI_INVALID_PARAMETER; - loader_is_participating = 1; in_protocol = 1; efi_status = read_header(buffer, size, &context); @@ -1180,8 +1179,6 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto restore; } - loader_is_participating = 0; - /* * The binary is trusted and relocated. Run it */ @@ -1799,7 +1796,6 @@ shim_init(void) * validation of the next image. */ hook_system_services(systab); - loader_is_participating = 0; } } -- cgit v1.2.3 From 2b49dc13aed3723c7c841c2788217ff7b0e821df Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 12 Jul 2024 17:07:45 -0700 Subject: Suppress file open failures for some netboot cases Reading files during a netboot comes with the caveat that fetching files from a network does not support anything like listing a directory. In the past this has meant that we do not try to open optional files during a netboot. However at least the revocation.efi file is now tested during a netboot, which will print an error when it is not found. Since that error is spurious we should allow for those errors to be suppressed. This is also desirable since we will likely go looking for additional files in the near future. Signed-off-by: Jan Setje-Eilers --- include/netboot.h | 5 ++++- netboot.c | 13 +++++++++---- shim.c | 23 ++++++++++++++--------- 3 files changed, 27 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/netboot.h b/include/netboot.h index a7bf6cd8..296f10f0 100644 --- a/include/netboot.h +++ b/include/netboot.h @@ -3,10 +3,13 @@ #ifndef SHIM_NETBOOT_H #define SHIM_NETBOOT_H +#define SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE 1 + extern BOOLEAN findNetboot(EFI_HANDLE image_handle); extern EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle, CHAR8 *name); -extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINT64 *bufsiz); +extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, + UINT64 *bufsiz, int flags); #endif /* SHIM_NETBOOT_H */ diff --git a/netboot.c b/netboot.c index a4cc7acd..46765427 100644 --- a/netboot.c +++ b/netboot.c @@ -368,7 +368,8 @@ status_from_error(UINT8 error_code) } } -EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT64 *bufsiz) +EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, + UINT64 *bufsiz, int flags) { EFI_STATUS efi_status; EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE; @@ -376,7 +377,8 @@ EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT BOOLEAN nobuffer = FALSE; UINTN blksz = 512; - console_print(L"Fetching Netboot Image %a\n", full_path); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"Fetching Netboot Image %a\n", full_path); if (*buffer == NULL) { *buffer = AllocatePool(4096 * 1024); if (!*buffer) @@ -399,7 +401,8 @@ try_again: if (EFI_ERROR(efi_status)) { if (pxe->Mode->TftpErrorReceived) { - console_print(L"TFTP error %u: %a\n", + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"TFTP error %u: %a\n", pxe->Mode->TftpError.ErrorCode, pxe->Mode->TftpError.ErrorString); @@ -412,7 +415,9 @@ try_again: * * https://github.com/tianocore/edk2/pull/6287 */ - console_print(L"Unknown TFTP error, treating as file not found\n"); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + console_print(L"Unknown TFTP error, treating " + "as file not found\n"); efi_status = EFI_NOT_FOUND; } diff --git a/shim.c b/shim.c index bf0c9e6c..d040992d 100644 --- a/shim.c +++ b/shim.c @@ -1055,7 +1055,8 @@ str16_to_str8(CHAR16 *str16, CHAR8 **str8) * Load and run an EFI executable */ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, - CHAR16 **PathName, void **data, int *datasize) + CHAR16 **PathName, void **data, int *datasize, + int flags) { EFI_STATUS efi_status; void *sourcebuffer = NULL; @@ -1092,10 +1093,11 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, } FreePool(netbootname); efi_status = FetchNetbootimage(image_handle, &sourcebuffer, - &sourcesize); + &sourcesize, flags); if (EFI_ERROR(efi_status)) { - perror(L"Unable to fetch TFTP image: %r\n", - efi_status); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + perror(L"Unable to fetch TFTP image: %r\n", + efi_status); return efi_status; } *data = sourcebuffer; @@ -1107,8 +1109,9 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, &sourcesize, netbootname); if (EFI_ERROR(efi_status)) { - perror(L"Unable to fetch HTTP image %a: %r\n", - netbootname, efi_status); + if (~flags & SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE) + perror(L"Unable to fetch HTTP image %a: %r\n", + netbootname, efi_status); return efi_status; } *data = sourcebuffer; @@ -1147,7 +1150,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) int datasize = 0; efi_status = read_image(image_handle, ImagePath, &PathName, &data, - &datasize); + &datasize, 0); if (EFI_ERROR(efi_status)) goto done; @@ -1435,7 +1438,8 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) uint8_t *sspv_latest = NULL; efi_status = read_image(image_handle, L"revocations.efi", &PathName, - &data, &datasize); + &data, &datasize, + SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE); if (EFI_ERROR(efi_status)) return efi_status; @@ -1499,7 +1503,8 @@ load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) int i; efi_status = read_image(image_handle, filename, &PathName, - &data, &datasize); + &data, &datasize, + SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE); if (EFI_ERROR(efi_status)) return efi_status; -- cgit v1.2.3 From c66ce2a7e4f9c76057ceff8a3168582ebc1d4c4e Mon Sep 17 00:00:00 2001 From: Jan Setje-Eilers Date: Fri, 16 Aug 2024 15:06:43 -0700 Subject: Allow indepdent SkuSi and SBAT revocation updates While a revocations.efi binary can contain either SBAT revocations, SkuSi revocations, or both, it is desirable to package them separately so that higher level tools such as fwupd can decide which ones to put in place at a given moment. This changes revocations.efi to revocations_sbat.efi and revocations_sku.efi Signed-off-by: Jan Setje-Eilers --- include/sbat.h | 3 ++- shim.c | 31 ++++++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sbat.h b/include/sbat.h index bb523e7e..093bb64a 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -38,7 +38,8 @@ #define POLICY_RESET 3 #define POLICY_NOTREAD 255 -#define REVOCATIONFILE L"revocations.efi" +#define SBATREVOCATIONFILE L"revocations_sbat.efi" +#define SKUSIREVOCATIONFILE L"revocations_sku.efi" extern UINTN _sbat, _esbat; diff --git a/shim.c b/shim.c index cf30b331..32b6a30f 100644 --- a/shim.c +++ b/shim.c @@ -1422,7 +1422,7 @@ check_section_helper(char *section_name, int len, void **pointer, section, data, datasize, minsize) EFI_STATUS -load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) +load_revocations_file(EFI_HANDLE image_handle, CHAR16 *FileName, CHAR16 *PathName) { EFI_STATUS efi_status = EFI_SUCCESS; PE_COFF_LOADER_IMAGE_CONTEXT context; @@ -1437,13 +1437,12 @@ load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName) uint8_t *ssps_latest = NULL; uint8_t *sspv_latest = NULL; - efi_status = read_image(image_handle, L"revocations.efi", &PathName, + efi_status = read_image(image_handle, FileName, &PathName, &data, &datasize, SUPPRESS_NETBOOT_OPEN_FAILURE_NOISE); - if (EFI_ERROR(efi_status)) - return efi_status; + if (!EFI_ERROR(efi_status)) + efi_status = verify_image(data, datasize, shim_li, &context); - efi_status = verify_image(data, datasize, shim_li, &context); if (EFI_ERROR(efi_status)) { dprint(L"revocations failed to verify\n"); return efi_status; @@ -1597,7 +1596,8 @@ load_unbundled_trust(EFI_HANDLE image_handle) * updates unconditionally in those cases. This may produce * console noise when the file is not present. */ - load_revocations_file(image_handle, REVOCATIONFILE, PathName); + load_revocations_file(image_handle, SKUSIREVOCATIONFILE, PathName); + load_revocations_file(image_handle, SBATREVOCATIONFILE, PathName); goto done; } @@ -1667,17 +1667,17 @@ load_unbundled_trust(EFI_HANDLE image_handle) } /* - * In the event that there are unprocessed revocation + * In the event that there are unprocessed sbat revocation * additions, they could be intended to ban any *new* trust * anchors we find here. With that in mind, we always want to * do a pass of loading revocations before we try to add * anything new to our allowlist. This is done by making two * passes over the directory, first to search for the - * revocations.efi file then to search for shim_certificate*.efi + * revocations_sbat.efi file then to search for shim_certificate*.efi */ if (search_revocations && - StrCaseCmp(info->FileName, REVOCATIONFILE) == 0) { - load_revocations_file(image_handle, PathName); + StrCaseCmp(info->FileName, SBATREVOCATIONFILE) == 0) { + load_revocations_file(image_handle, SBATREVOCATIONFILE, PathName); search_revocations = FALSE; efi_status = root->Open(root, &dir, PathName, EFI_FILE_MODE_READ, 0); @@ -1688,9 +1688,14 @@ load_unbundled_trust(EFI_HANDLE image_handle) } } - if (!search_revocations && - StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { - load_cert_file(image_handle, info->FileName, PathName); + if (!search_revocations) { + if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + load_cert_file(image_handle, info->FileName, PathName, 0); + } + if (StrCaseCmp(info->FileName, SKUSIREVOCATIONFILE) == 0) { + load_revocations_file(image_handle, + SKUSIREVOCATIONFILE, PathName); + } } } done: -- cgit v1.2.3 From 7cde2cc52f19f733de7855419d1c43a13a8d6c5f Mon Sep 17 00:00:00 2001 From: Dennis Tseng Date: Fri, 28 Apr 2023 10:47:33 +0800 Subject: post-process-pe: add tests to validate NX compliance This changes post-process-pe to give warnings, and optionally errors, if a shim binary is built with Section Alignment or characteristics are not compatible with NX, or if the EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag is not set and require_nx_compat is true. Co-authored-by: Peter Jones Co-authored-by: Kamil Aronowski Signed-off-by: Dennis Tseng --- include/peimage.h | 1 + post-process-pe.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/peimage.h b/include/peimage.h index 6eef1051..4182a17b 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -824,6 +824,7 @@ typedef struct { EFI_IMAGE_DATA_DIRECTORY *RelocDir; EFI_IMAGE_DATA_DIRECTORY *SecDir; UINT64 NumberOfRvaAndSizes; + UINT16 DllCharacteristics; EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr; } PE_COFF_LOADER_IMAGE_CONTEXT; diff --git a/post-process-pe.c b/post-process-pe.c index de8f4a38..008da93b 100644 --- a/post-process-pe.c +++ b/post-process-pe.c @@ -43,6 +43,7 @@ static int verbosity; }) static bool set_nx_compat = false; +static bool require_nx_compat = false; typedef uint8_t UINT8; typedef uint16_t UINT16; @@ -162,6 +163,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, ctx->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; ctx->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; + ctx->DllCharacteristics = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); } else { @@ -172,6 +174,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, ctx->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; ctx->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; + ctx->DllCharacteristics = PEHdr->Pe32.OptionalHeader.DllCharacteristics; FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); } @@ -358,6 +361,50 @@ set_dll_characteristics(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) } else { ctx->PEHdr->Pe32.OptionalHeader.DllCharacteristics = newflags; } + ctx->DllCharacteristics = newflags; +} + +static int +validate_nx_compat(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) +{ + EFI_IMAGE_SECTION_HEADER *Section; + int i; + bool nx_compat_flag; + const int level = require_nx_compat ? ERROR : WARNING; + int ret = 0; + + nx_compat_flag = EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT + & ctx->DllCharacteristics; + debug(NOISE, "NX-Compat-Flag: %s\n", nx_compat_flag ? "set" : "unset"); + if (!nx_compat_flag) { + debug(level, "NX Compatibility flag is not set\n"); + if (require_nx_compat) + ret = -1; + } + + debug(NOISE, "Section alignment is 0x%x, page size is 0x%x\n", + ctx->SectionAlignment, PAGE_SIZE); + if (ctx->SectionAlignment != PAGE_SIZE) { + debug(level, "Section alignment is not page aligned\n"); + if (require_nx_compat) + ret = -1; + } + + Section = ctx->FirstSection; + for (i=0, Section = ctx->FirstSection; i < ctx->NumberOfSections; i++, Section++) { + debug(NOISE, "Section %d has WRITE=%d and EXECUTE=%d\n", i, + (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) ? 1 : 0, + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) ? 1 : 0); + + if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE)) { + debug(level, "Section %d is writable and executable\n", i); + if (require_nx_compat) + ret = -1; + } + } + + return ret; } static void @@ -449,6 +496,10 @@ handle_one(char *f) set_dll_characteristics(&ctx); + rc = validate_nx_compat(&ctx); + if (rc < 0) + err(2, "NX compatibility check failed\n"); + fix_timestamp(&ctx); fix_checksum(&ctx, map, sz); @@ -483,6 +534,7 @@ static void __attribute__((__noreturn__)) usage(int status) fprintf(out, " -v Be more verbose\n"); fprintf(out, " -N Disable the NX compatibility flag\n"); fprintf(out, " -n Enable the NX compatibility flag\n"); + fprintf(out, " -x Error on NX incompatibility\n"); fprintf(out, " -h Print this help text and exit\n"); exit(status); @@ -510,11 +562,14 @@ int main(int argc, char **argv) {.name = "verbose", .val = 'v', }, + {.name = "error-nx-compat", + .val = 'x', + }, {.name = ""} }; int longindex = -1; - while ((i = getopt_long(argc, argv, "hNnqv", options, &longindex)) != -1) { + while ((i = getopt_long(argc, argv, "hNnqvx", options, &longindex)) != -1) { switch (i) { case 'h': case '?': @@ -532,6 +587,9 @@ int main(int argc, char **argv) case 'v': verbosity = MIN(verbosity + 1, MAX_VERBOSITY); break; + case 'x': + require_nx_compat = true; + break; } } -- cgit v1.2.3 From 765f2944fb392be2e8f76f3503ae7ceab301fea4 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 27 Jun 2024 15:16:18 -0400 Subject: compiler.h: minor ALIGN_... fixes This fixes some minor errors with the testing of how ALIGN() and similar are defined, and makes an explicit "ALIGN_UP()" macro to complement the existing ALIGN_DOWN() macro. Signed-off-by: Peter Jones --- include/compiler.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/compiler.h b/include/compiler.h index 982bc235..6a19217c 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -175,14 +175,19 @@ #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) #endif -#ifndef ALIGN +#ifndef __ALIGN #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define __ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) +#endif +#ifndef ALIGN #define ALIGN(x, a) __ALIGN((x), (a)) #endif #ifndef ALIGN_DOWN #define ALIGN_DOWN(x, a) __ALIGN((x) - ((a) - 1), (a)) #endif +#ifndef ALIGN_UP +#define ALIGN_UP(addr, align) (((addr) + (typeof (addr)) (align) - 1) & ~((typeof (addr)) (align) - 1)) +#endif #define MIN(a, b) ({(a) < (b) ? (a) : (b);}) #define MAX(a, b) ({(a) <= (b) ? (b) : (a);}) -- cgit v1.2.3 From 5c1e6e45a0f54eaabea0f9405ed661b5028de30c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 19 Feb 2025 11:03:08 -0500 Subject: Move error logging decls out of shim.h This moves decls for errlog.c into errlog.h Signed-off-by: Peter Jones --- include/errlog.h | 20 ++++++++++++++++++++ shim.h | 9 +-------- 2 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 include/errlog.h (limited to 'include') diff --git a/include/errlog.h b/include/errlog.h new file mode 100644 index 00000000..bf59b2f9 --- /dev/null +++ b/include/errlog.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * errlog.h - error logging utilities + * Copyright Peter Jones + */ + +#ifndef ERRLOG_H_ +#define ERRLOG_H_ + +extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, + const CHAR16 *fmt, ...); +extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, + const CHAR16 *fmt, ms_va_list args); +extern VOID LogHexdump_(const char *file, int line, const char *func, + const void *data, size_t sz); +extern VOID PrintErrors(VOID); +extern VOID ClearErrors(VOID); + +#endif /* !ERRLOG_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/shim.h b/shim.h index 09d23780..f2246c90 100644 --- a/shim.h +++ b/shim.h @@ -166,6 +166,7 @@ #include "include/crypt_blowfish.h" #include "include/dp.h" #include "include/efiauthenticated.h" +#include "include/errlog.h" #include "include/errors.h" #include "include/execute.h" #include "include/guid.h" @@ -240,14 +241,6 @@ typedef struct _SHIM_LOCK { extern EFI_STATUS shim_init(void); extern void shim_fini(void); -extern EFI_STATUS EFIAPI LogError_(const char *file, int line, const char *func, - const CHAR16 *fmt, ...); -extern EFI_STATUS EFIAPI VLogError(const char *file, int line, const char *func, - const CHAR16 *fmt, ms_va_list args); -extern VOID LogHexdump_(const char *file, int line, const char *func, - const void *data, size_t sz); -extern VOID PrintErrors(VOID); -extern VOID ClearErrors(VOID); extern VOID restore_loaded_image(VOID); extern EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath); extern EFI_STATUS import_mok_state(EFI_HANDLE image_handle); -- cgit v1.2.3 From d972515e608e4898c4a34a5895648135d4ac5aec Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 19 Feb 2025 11:04:34 -0500 Subject: Save the debug and error logs in mok-variables This changes our debug and error logging to save the entire logs into mok-variables as "shim-dbg.txt" and "shim-log.txt". Signed-off-by: Peter Jones --- errlog.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/console.h | 1 + include/errlog.h | 2 + shim.c | 2 + 4 files changed, 154 insertions(+) (limited to 'include') diff --git a/errlog.c b/errlog.c index 3c5e0af8..fc89ae0a 100644 --- a/errlog.c +++ b/errlog.c @@ -99,4 +99,153 @@ ClearErrors(VOID) errs = NULL; } +static size_t +format_error_log(UINT8 *dest, size_t dest_sz) +{ + size_t err_log_sz = 0; + size_t pos = 0; + + for (UINTN i = 0; i < nerrs; i++) + err_log_sz += StrSize(errs[i]); + + if (!dest || dest_sz < err_log_sz) + return err_log_sz; + + ZeroMem(dest, err_log_sz); + for (UINTN i = 0; i < nerrs; i++) { + UINTN sz = StrSize(errs[i]); + CopyMem(&dest[pos], errs[i], sz); + pos += sz; + } + + return err_log_sz; +} + +static UINT8 *debug_log = NULL; +static size_t debug_log_sz = 0; +static size_t debug_log_alloc = 0; + +UINTN EFIAPI +log_debug_print(const CHAR16 *fmt, ...) +{ + ms_va_list args; + CHAR16 *buf; + size_t buf_sz; + UINTN ret = 0; + + ms_va_start(args, fmt); + buf = VPoolPrint(fmt, args); + if (!buf) + return 0; + ms_va_end(args); + + ret = StrLen(buf); + buf_sz = StrSize(buf); + if (debug_log_sz + buf_sz > debug_log_alloc) { + size_t new_alloc_sz = debug_log_alloc; + CHAR16 *new_debug_log; + + new_alloc_sz += buf_sz; + new_alloc_sz = ALIGN_UP(new_alloc_sz, EFI_PAGE_SIZE); + + new_debug_log = ReallocatePool(debug_log, debug_log_alloc, new_alloc_sz); + if (!new_debug_log) + return 0; + debug_log = (UINT8 *)new_debug_log; + debug_log_alloc = new_alloc_sz; + } + + CopyMem(&debug_log[debug_log_sz], buf, buf_sz); + debug_log_sz += buf_sz; + FreePool(buf); + return ret; +} + +static size_t +format_debug_log(UINT8 *dest, size_t dest_sz) +{ + if (!dest || dest_sz < debug_log_sz) + return debug_log_sz; + + ZeroMem(dest, debug_log_sz); + CopyMem(dest, debug_log, debug_log_sz); + return debug_log_sz; +} + +void +save_logs(void) +{ + struct mok_variable_config_entry *cfg_table = NULL; + struct mok_variable_config_entry *new_table = NULL; + struct mok_variable_config_entry *entry = NULL; + size_t new_table_sz; + UINTN pos = 0; + EFI_STATUS efi_status; + EFI_CONFIGURATION_TABLE *CT; + EFI_GUID bogus_guid = { 0x29f2f0db, 0xd025, 0x4aa6, { 0x99, 0x58, 0xa0, 0x21, 0x8b, 0x1d, 0xec, 0x0e }}; + size_t errlog_sz, dbglog_sz; + + errlog_sz = format_error_log(NULL, 0); + dbglog_sz = format_debug_log(NULL, 0); + + if (errlog_sz == 0 && dbglog_sz == 0) { + console_print(L"No console or debug log?!?!?\n"); + return; + } + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + CT = &ST->ConfigurationTable[i]; + + if (CompareGuid(&MOK_VARIABLE_STORE, &CT->VendorGuid) == 0) { + cfg_table = CT->VendorTable; + break; + } + CT = NULL; + } + + entry = cfg_table; + while (entry && entry->name[0] != 0) { + size_t entry_sz; + entry = (struct mok_variable_config_entry *)((uintptr_t)cfg_table + pos); + + entry_sz = sizeof(*entry); + entry_sz += entry->data_size; + + if (entry->name[0] != 0) + pos += entry_sz; + } + + new_table_sz = pos + 3 * sizeof(*entry) + errlog_sz + dbglog_sz; + new_table = AllocateZeroPool(new_table_sz); + if (!new_table) + return; + + CopyMem(new_table, cfg_table, pos); + + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + if (errlog_sz) { + strcpy(entry->name, "shim-err.txt"); + entry->data_size = errlog_sz; + format_error_log(&entry->data[0], errlog_sz); + + pos += sizeof(*entry) + errlog_sz; + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + } + if (dbglog_sz) { + strcpy(entry->name, "shim-dbg.txt"); + entry->data_size = dbglog_sz; + format_debug_log(&entry->data[0], dbglog_sz); + + pos += sizeof(*entry) + dbglog_sz; + // entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); + } + + if (CT) { + CopyMem(&CT->VendorGuid, &bogus_guid, sizeof(bogus_guid)); + } + efi_status = BS->InstallConfigurationTable(&MOK_VARIABLE_STORE, new_table); + if (EFI_ERROR(efi_status)) + console_print(L"Could not re-install MoK configuration table: %r\n", efi_status); +} + // vim:fenc=utf-8:tw=75 diff --git a/include/console.h b/include/console.h index 7ac4e113..c90e3e71 100644 --- a/include/console.h +++ b/include/console.h @@ -98,6 +98,7 @@ extern UINT32 verbose; #ifndef SHIM_UNIT_TEST #define dprint_(fmt, ...) ({ \ UINTN __dprint_ret = 0; \ + log_debug_print((fmt), ##__VA_ARGS__); \ if (verbose) \ __dprint_ret = console_print((fmt), ##__VA_ARGS__); \ __dprint_ret; \ diff --git a/include/errlog.h b/include/errlog.h index bf59b2f9..b9f089b8 100644 --- a/include/errlog.h +++ b/include/errlog.h @@ -15,6 +15,8 @@ extern VOID LogHexdump_(const char *file, int line, const char *func, const void *data, size_t sz); extern VOID PrintErrors(VOID); extern VOID ClearErrors(VOID); +extern void save_logs(void); +extern UINTN EFIAPI log_debug_print(const CHAR16 *fmt, ...); #endif /* !ERRLOG_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 083ea9e8..fb86589b 100644 --- a/shim.c +++ b/shim.c @@ -1182,6 +1182,8 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto restore; } + save_logs(); + /* * The binary is trusted and relocated. Run it */ -- cgit v1.2.3 From 49db3de08ef2c55f6dbc3c2b8e6ab7b2f22e5309 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2024 16:13:40 -0400 Subject: mok: add MOK_VARIABLE_CONFIG_ONLY This adds a mok variable flag "MOK_VARIABLE_CONFIG_ONLY" to specify that the data should be added to our UEFI config table, but shim should not create a legacy UEFI variable. Signed-off-by: Peter Jones --- include/mok.h | 2 ++ mok.c | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/mok.h b/include/mok.h index fb19423b..fe92cf03 100644 --- a/include/mok.h +++ b/include/mok.h @@ -81,6 +81,8 @@ struct mok_state_variable { * MOK_MIRROR_DELETE_FIRST delete any existing variable first * MOK_VARIABLE_MEASURE extend PCR 7 and log the hash change * MOK_VARIABLE_LOG measure into whatever .pcr says and log + * MOK_VARIABLE_CONFIG_ONLY don't create a UEFI variable, only add + * it to the config space variables. */ UINTN pcr; /* PCR to measure and hash to */ diff --git a/mok.c b/mok.c index 5ae91244..2778cd05 100644 --- a/mok.c +++ b/mok.c @@ -106,11 +106,12 @@ categorize_deauthorized(struct mok_state_variable *v) return VENDOR_ADDEND_DB; } -#define MOK_MIRROR_KEYDB 0x01 -#define MOK_MIRROR_DELETE_FIRST 0x02 -#define MOK_VARIABLE_MEASURE 0x04 -#define MOK_VARIABLE_LOG 0x08 -#define MOK_VARIABLE_INVERSE 0x10 +#define MOK_MIRROR_KEYDB 0x01 +#define MOK_MIRROR_DELETE_FIRST 0x02 +#define MOK_VARIABLE_MEASURE 0x04 +#define MOK_VARIABLE_LOG 0x08 +#define MOK_VARIABLE_INVERSE 0x10 +#define MOK_VARIABLE_CONFIG_ONLY 0x20 struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", @@ -834,7 +835,8 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); - if (FullDataSize && v->flags & MOK_MIRROR_KEYDB) { + if (FullDataSize && v->flags & MOK_MIRROR_KEYDB && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { dprint(L"calling mirror_mok_db(\"%s\", datasz=%lu)\n", v->rtname, FullDataSize); efi_status = mirror_mok_db(v->rtname, (CHAR8 *)v->rtname8, v->guid, @@ -842,7 +844,8 @@ mirror_one_mok_variable(struct mok_state_variable *v, only_first); dprint(L"mirror_mok_db(\"%s\", datasz=%lu) returned %r\n", v->rtname, FullDataSize, efi_status); - } else if (FullDataSize && only_first) { + } else if (FullDataSize && only_first && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { efi_status = SetVariable(v->rtname, v->guid, attrs, FullDataSize, FullData); } @@ -938,7 +941,8 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, dprint(L"importing mok state for \"%s\"\n", v->name); - if (!v->data && !v->data_size) { + if (!v->data && !v->data_size && + !(v->flags & MOK_VARIABLE_CONFIG_ONLY)) { efi_status = get_variable_attr(v->name, &v->data, &v->data_size, *v->guid, &attrs); @@ -980,6 +984,22 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, } } } + + if (!v->data && !v->data_size && + (v->flags & MOK_VARIABLE_CONFIG_ONLY)) { + efi_status = get_variable_attr(v->name, + &v->data, &v->data_size, + *v->guid, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Couldn't get variable \"%s\" for mirroring: %r\n", + v->name, efi_status); + if (efi_status != EFI_NOT_FOUND) + return efi_status; + v->data = NULL; + v->data_size = 0; + } + } + if (delete == TRUE) { perror(L"Deleting bad variable %s\n", v->name); efi_status = LibDeleteVariable(v->name, v->guid); -- cgit v1.2.3 From 887c0edab93c52e2558047897847166b73dc8c3a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2024 16:01:34 -0400 Subject: mok variables: add a format callback This adds a member to the mok_state_variable struct to provide a callback function for formatting external data. It basically has snprintf()-like semantics for filling the buffer, but without the actual printf-like formatting bits. Signed-off-by: Peter Jones --- include/mok.h | 18 ++++++++++++++++++ mok.c | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/mok.h b/include/mok.h index fe92cf03..c37ccba5 100644 --- a/include/mok.h +++ b/include/mok.h @@ -17,6 +17,7 @@ typedef enum { struct mok_state_variable; typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *); +typedef UINTN (mok_variable_format_helper_t)(UINT8 *buf, size_t sz, struct mok_state_variable *); /* * MoK variables that need to have their storage validated. @@ -91,6 +92,23 @@ struct mok_state_variable { * mirrored. */ UINT8 *state; + + /* + * If this is non-NULL, this function will be called during the + * "import" phase to format the variable data. It'll get called + * twice, once as: + * + * sz = format(NULL, 0, ptr); + * + * a buffer of size sz will then be allocated, and it'll be called + * again to fill the buffer: + * + * format(buf, sz, ptr); + * + * Note that as an implementation detail data and data_size must be + * NULL and 0 respectively for this entry. + */ + mok_variable_format_helper_t *format; }; extern size_t n_mok_state_variables; diff --git a/mok.c b/mok.c index 2778cd05..67a798a3 100644 --- a/mok.c +++ b/mok.c @@ -985,8 +985,22 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, } } + if (v->format) { + v->data_size = v->format(NULL, 0, v); + if (v->data_size > 0) { + v->data = AllocatePool(v->data_size); + if (!v->data) { + perror(L"Could not allocate %lu bytes for %s\n", + v->data_size, v->name); + return EFI_OUT_OF_RESOURCES; + } + } + v->format(v->data, v->data_size, v); + } + if (!v->data && !v->data_size && - (v->flags & MOK_VARIABLE_CONFIG_ONLY)) { + (v->flags & MOK_VARIABLE_CONFIG_ONLY) && + !v->format) { efi_status = get_variable_attr(v->name, &v->data, &v->data_size, *v->guid, &attrs); -- cgit v1.2.3 From 589c3f289e05454be23507767439cb9769a2264a Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 20 Feb 2025 13:51:29 -0500 Subject: Move memory attribute support to its own file. This moves the EFI Memory Attribute Protocol helper functions to their own file, since they're not related to PE things. Signed-off-by: Peter Jones --- Makefile | 4 +- include/memattrs.h | 15 +++++ memattrs.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++ pe.c | 152 ------------------------------------------------ shim.h | 1 + 5 files changed, 185 insertions(+), 154 deletions(-) create mode 100644 include/memattrs.h create mode 100644 memattrs.c (limited to 'include') diff --git a/Makefile b/Makefile index bf339fab..fe42db40 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ CFLAGS += -DENABLE_SHIM_CERT else TARGETS += $(MMNAME) $(FBNAME) endif -OBJS = shim.o globals.o mok.o netboot.o cert.o dp.o loader-proto.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o +OBJS = shim.o globals.o memattrs.o mok.o netboot.o cert.o dp.o loader-proto.o tpm.o version.o errlog.o sbat.o sbat_data.o sbat_var.o pe.o pe-relocate.o httpboot.o csv.o load-options.o utils.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c dp.c loader-proto.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S +ORIG_SOURCES = shim.c globals.c memattrs.c mok.c netboot.c dp.c loader-proto.c tpm.c errlog.c sbat.c pe.c pe-relocate.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S sbat_var.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o dp.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o utils.o diff --git a/include/memattrs.h b/include/memattrs.h new file mode 100644 index 00000000..8fefef22 --- /dev/null +++ b/include/memattrs.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.h - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#ifndef SHIM_MEMATTRS_H_ +#define SHIM_MEMATTRS_H_ + +extern EFI_STATUS get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs); +extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs); + +#endif /* !SHIM_MEMATTRS_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/memattrs.c b/memattrs.c new file mode 100644 index 00000000..99268cd1 --- /dev/null +++ b/memattrs.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: BSD-2-Clause-Patent +/* + * memattrs.c - EFI and DXE memory attribute helpers + * Copyright Peter Jones + */ + +#include "shim.h" + +static inline uint64_t +shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) +{ + uint64_t ret = EFI_MEMORY_RP | + EFI_MEMORY_RO | + EFI_MEMORY_XP; + + if (attrs & MEM_ATTR_R) + ret &= ~EFI_MEMORY_RP; + + if (attrs & MEM_ATTR_W) + ret &= ~EFI_MEMORY_RO; + + if (attrs & MEM_ATTR_X) + ret &= ~EFI_MEMORY_XP; + + return ret; +} + +static inline uint64_t +uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) +{ + uint64_t ret = MEM_ATTR_R | + MEM_ATTR_W | + MEM_ATTR_X; + + if (attrs & EFI_MEMORY_RP) + ret &= ~MEM_ATTR_R; + + if (attrs & EFI_MEMORY_RO) + ret &= ~MEM_ATTR_W; + + if (attrs & EFI_MEMORY_XP) + ret &= ~MEM_ATTR_X; + + return ret; +} + +EFI_STATUS +get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) { + dprint(L"No memory attribute protocol found: %r\n", efi_status); + if (!EFI_ERROR(efi_status)) + efi_status = EFI_UNSUPPORTED; + return efi_status; + } + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"GetMemoryAttributes(..., 0x%llx, 0x%x, 0x%x): %r\n", + physaddr, size, attrs, efi_status); + } else { + *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); + } + + return efi_status; +} + +EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status, ret; + uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) + return efi_status; + + efi_status = get_mem_attrs (addr, size, &before); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &before, efi_status); + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(physaddr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return 0; + } + + uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + efi_status = EFI_SUCCESS; + if (uefi_set_attrs) { + efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_set_attrs, physaddr, size, efi_status); + } + } + if (!EFI_ERROR(efi_status) && uefi_clear_attrs) { + efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", + uefi_clear_attrs, physaddr, size, efi_status); + } + } + ret = efi_status; + + efi_status = get_mem_attrs (addr, size, &after); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &after, efi_status); + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); + + return ret; +} + +// vim:fenc=utf-8:tw=75:noet diff --git a/pe.c b/pe.c index cd80b07d..40812bb7 100644 --- a/pe.c +++ b/pe.c @@ -395,158 +395,6 @@ err: return efi_status; } -static inline uint64_t -shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) -{ - uint64_t ret = EFI_MEMORY_RP | - EFI_MEMORY_RO | - EFI_MEMORY_XP; - - if (attrs & MEM_ATTR_R) - ret &= ~EFI_MEMORY_RP; - - if (attrs & MEM_ATTR_W) - ret &= ~EFI_MEMORY_RO; - - if (attrs & MEM_ATTR_X) - ret &= ~EFI_MEMORY_XP; - - return ret; -} - -static inline uint64_t -uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) -{ - uint64_t ret = MEM_ATTR_R | - MEM_ATTR_W | - MEM_ATTR_X; - - if (attrs & EFI_MEMORY_RP) - ret &= ~MEM_ATTR_R; - - if (attrs & EFI_MEMORY_RO) - ret &= ~MEM_ATTR_W; - - if (attrs & EFI_MEMORY_XP) - ret &= ~MEM_ATTR_X; - - return ret; -} - -static EFI_STATUS -get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) -{ - EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; - EFI_PHYSICAL_ADDRESS physaddr = addr; - EFI_STATUS efi_status; - - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) { - if (!EFI_ERROR(efi_status)) - efi_status = EFI_UNSUPPORTED; - return efi_status; - } - - if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { - dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", - __func__, (unsigned long long)physaddr, - (unsigned long long)(physaddr+size-1), - attrs); - return EFI_SUCCESS; - } - - efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); - *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); - - return efi_status; -} - -static EFI_STATUS -update_mem_attrs(uintptr_t addr, uint64_t size, - uint64_t set_attrs, uint64_t clear_attrs) -{ - EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; - EFI_PHYSICAL_ADDRESS physaddr = addr; - EFI_STATUS efi_status, ret; - uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; - - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) - return efi_status; - - efi_status = get_mem_attrs (addr, size, &before); - if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", - (unsigned long long)addr, (unsigned long long)size, - &before, efi_status); - - if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0) { - perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", - __func__, (unsigned long long)physaddr, - (unsigned long long)(physaddr + size - 1), - (unsigned long long)size, - (set_attrs & MEM_ATTR_R) ? "r" : "", - (set_attrs & MEM_ATTR_W) ? "w" : "", - (set_attrs & MEM_ATTR_X) ? "x" : "", - (clear_attrs & MEM_ATTR_R) ? "r" : "", - (clear_attrs & MEM_ATTR_W) ? "w" : "", - (clear_attrs & MEM_ATTR_X) ? "x" : ""); - if (!IS_PAGE_ALIGNED(physaddr)) - perror(L" addr is not page aligned\n"); - if (!IS_PAGE_ALIGNED(size)) - perror(L" size is not page aligned\n"); - if (size == 0) - perror(L" size is 0\n"); - return 0; - } - - uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); - dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); - uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); - dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); - efi_status = EFI_SUCCESS; - if (uefi_set_attrs) { - efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); - if (EFI_ERROR(efi_status)) { - dprint(L"Failed to set memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", - uefi_set_attrs, physaddr, size, efi_status); - } - } - if (!EFI_ERROR(efi_status) && uefi_clear_attrs) { - efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); - if (EFI_ERROR(efi_status)) { - dprint(L"Failed to clear memory attrs:0x%0x physaddr:0x%llx size:0x%0lx status:%r\n", - uefi_clear_attrs, physaddr, size, efi_status); - } - } - ret = efi_status; - - efi_status = get_mem_attrs (addr, size, &after); - if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", - (unsigned long long)addr, (unsigned long long)size, - &after, efi_status); - - dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", - (set_attrs & MEM_ATTR_R) ? "r" : "", - (set_attrs & MEM_ATTR_W) ? "w" : "", - (set_attrs & MEM_ATTR_X) ? "x" : "", - (clear_attrs & MEM_ATTR_R) ? "r" : "", - (clear_attrs & MEM_ATTR_W) ? "w" : "", - (clear_attrs & MEM_ATTR_X) ? "x" : "", - (unsigned long long)addr, (unsigned long long)(addr + size - 1), - (before & MEM_ATTR_R) ? 'r' : '-', - (before & MEM_ATTR_W) ? 'w' : '-', - (before & MEM_ATTR_X) ? 'x' : '-', - (after & MEM_ATTR_R) ? 'r' : '-', - (after & MEM_ATTR_W) ? 'w' : '-', - (after & MEM_ATTR_X) ? 'x' : '-'); - - return ret; -} - EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, PE_COFF_LOADER_IMAGE_CONTEXT *context) diff --git a/shim.h b/shim.h index f2246c90..59d90629 100644 --- a/shim.h +++ b/shim.h @@ -176,6 +176,7 @@ #include "include/ip6config.h" #include "include/load-options.h" #include "include/loader-proto.h" +#include "include/memattrs.h" #include "include/mok.h" #include "include/netboot.h" #include "include/passwordcrypt.h" -- cgit v1.2.3 From 848667d0f3a99401d93c93b3af16b55e3fb28cea Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2024 16:13:13 -0400 Subject: shim: add HSIStatus feature hughsie asked me if I can make shim tell userland what kinds of accesses are allowed to the heap, stack, and allocations on the running platform, so that these could be reported up through fwupd's Host Security ID program (see https://fwupd.github.io/libfwupdplugin/hsi.html ). This adds a new config-only (i.e. not a UEFI variable) variable generated during boot, "/sys/firmware/efi/mok-variables/HSIStatus", which tells us those properties as well as if the EFI Memory Attribute Protocol is present. Signed-off-by: Peter Jones --- MokVars.txt | 10 ++++++ globals.c | 1 + include/memattrs.h | 2 ++ include/mok.h | 10 ++++++ include/test-data-efivars-1.h | 6 ++++ memattrs.c | 72 +++++++++++++++++++++++++++++++++++++++++++ mok.c | 46 +++++++++++++++++++++++++++ shim.c | 1 + test-mok-mirror.c | 13 ++++++++ 9 files changed, 161 insertions(+) (limited to 'include') diff --git a/MokVars.txt b/MokVars.txt index 71b42c82..e6e68ce4 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -93,3 +93,13 @@ to trust CA keys in the MokList. BS,NV MokListTrustedRT: A copy of MokListTrusted made available to the kernel at runtime. BS,RT + +HSIStatus: Status of various security features: + heap-is-executable: 0: heap allocations are not executable by default + 1: heap allocations are executable + stack-is-executable: 0: UEFI stack is not executable + 1: UEFI stack is executable + ro-sections-are-writable: 0: read-only sections are not writable + 1: read-only sections are writable + has-memory-attribute-protocol: 0: platform does not provide the EFI Memory Attribute Protocol + 1: platform does provide the EFI Memory Attribute Protocol diff --git a/globals.c b/globals.c index d574df16..de64c44c 100644 --- a/globals.c +++ b/globals.c @@ -28,6 +28,7 @@ verification_method_t verification_method; SHIM_IMAGE_LOADER shim_image_loader_interface; UINT8 user_insecure_mode; +UINTN hsi_status = 0; UINT8 ignore_db; UINT8 trust_mok_list; UINT8 mok_policy = 0; diff --git a/include/memattrs.h b/include/memattrs.h index 8fefef22..5c40b4cc 100644 --- a/include/memattrs.h +++ b/include/memattrs.h @@ -11,5 +11,7 @@ extern EFI_STATUS get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs); extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size, uint64_t set_attrs, uint64_t clear_attrs); +extern void get_hsi_mem_info(void); + #endif /* !SHIM_MEMATTRS_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/mok.h b/include/mok.h index c37ccba5..e6921e09 100644 --- a/include/mok.h +++ b/include/mok.h @@ -125,5 +125,15 @@ struct mok_variable_config_entry { */ #define MOK_POLICY_REQUIRE_NX 1 +extern UINTN hsi_status; +/* heap is executable */ +#define SHIM_HSI_STATUS_HEAPX 0x00000001ULL +/* stack is executable */ +#define SHIM_HSI_STATUS_STACKX 0x00000002ULL +/* read-only sections are writable */ +#define SHIM_HSI_STATUS_ROW 0x00000004ULL +/* platform provides the EFI Memory Attribute Protocol */ +#define SHIM_HSI_STATUS_HASMAP 0x00000008ULL + #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index 2831bd23..d97a4d6d 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -106,5 +106,11 @@ static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={ 0x01 }; +static const unsigned char test_data_efivars_1_HSIStatus[] = + "heap-is-executable: 0\n" + "stack-is-executable: 0\n" + "ro-sections-are-writable: 0\n" + "has-memory-attribute-protocol: 0\n"; + #endif /* !TEST_DATA_EFIVARS_1_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/memattrs.c b/memattrs.c index 99268cd1..988459c3 100644 --- a/memattrs.c +++ b/memattrs.c @@ -164,4 +164,76 @@ update_mem_attrs(uintptr_t addr, uint64_t size, return ret; } +void +get_hsi_mem_info(void) +{ + EFI_STATUS efi_status; + uintptr_t addr; + uint64_t attrs = 0; + uint32_t *tmp_alloc; + + addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK; + + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { +error: + /* + * In this case we can't actually tell anything, so assume + * and report the worst case scenario. + */ + hsi_status = SHIM_HSI_STATUS_HEAPX | + SHIM_HSI_STATUS_STACKX | + SHIM_HSI_STATUS_ROW; + dprint(L"Setting HSI to 0x%lx due to error: %r\n", hsi_status, efi_status); + return; + } else { + hsi_status = SHIM_HSI_STATUS_HASMAP; + dprint(L"Setting HSI to 0x%lx\n", hsi_status); + } + + if (!(hsi_status & SHIM_HSI_STATUS_HASMAP)) { + dprint(L"No memory protocol, not testing further\n"); + return; + } + + hsi_status = SHIM_HSI_STATUS_HASMAP; + if (attrs & MEM_ATTR_W) { + dprint(L"get_hsi_mem_info() is on a writable page: 0x%x->0x%x\n", + hsi_status, hsi_status | SHIM_HSI_STATUS_ROW); + hsi_status |= SHIM_HSI_STATUS_ROW; + } + + addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + + if (attrs & MEM_ATTR_X) { + dprint(L"Stack variable is on an executable page: 0x%x->0x%x\n", + hsi_status, hsi_status | SHIM_HSI_STATUS_STACKX); + hsi_status |= SHIM_HSI_STATUS_STACKX; + } + + tmp_alloc = AllocatePool(EFI_PAGE_SIZE); + if (!tmp_alloc) { + dprint(L"Failed to allocate heap variable.\n"); + goto error; + } + + addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + FreePool(tmp_alloc); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + if (attrs & MEM_ATTR_X) { + dprint(L"Heap variable is on an executable page: 0x%x->0x%x\n", + hsi_status, hsi_status | SHIM_HSI_STATUS_HEAPX); + hsi_status |= SHIM_HSI_STATUS_HEAPX; + } +} + // vim:fenc=utf-8:tw=75:noet diff --git a/mok.c b/mok.c index 67a798a3..5c7f9a2b 100644 --- a/mok.c +++ b/mok.c @@ -34,6 +34,44 @@ static BOOLEAN check_var(CHAR16 *varname) efi_status_; \ }) +static UINTN +format_hsi_status(UINT8 *buf, size_t sz, + struct mok_state_variable *msv UNUSED) +{ + const char heapx[] = "heap-is-executable: "; + const char stackx[] = "\nstack-is-executable: "; + const char row[] = "\nro-sections-are-writable: "; + const char hasmap[] = "\nhas-memory-attribute-protocol: "; + const char finale[] = "\n"; + char *pos; + + /* + * sizeof includes the trailing NUL which is where our 0 or 1 value + * fits + */ + UINTN ret = sizeof(heapx) + sizeof(stackx) + + sizeof(row) + sizeof(hasmap) + + sizeof(finale); + + if (buf == 0 || sz < ret) { + return ret; + } + + buf[0] = 0; + pos = (char *)buf; + pos = stpcpy(pos, heapx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HEAPX) ? "1" : "0"); + pos = stpcpy(pos, stackx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_STACKX) ? "1" : "0"); + pos = stpcpy(pos, row); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_ROW) ? "1" : "0"); + pos = stpcpy(pos, hasmap); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASMAP) ? "1" : "0"); + stpcpy(pos, finale); + + return ret; +} + /* * If the OS has set any of these variables we need to drop into MOK and * handle them appropriately @@ -223,6 +261,14 @@ struct mok_state_variable mok_state_variable_data[] = { .pcr = 14, .state = &mok_policy, }, + {.name = L"HSIStatus", + .name8 = "HSIStatus", + .rtname = L"HSIStatus", + .rtname8 = "HSIStatus", + .guid = &SHIM_LOCK_GUID, + .flags = MOK_VARIABLE_CONFIG_ONLY, + .format = format_hsi_status, + }, { NULL, } }; size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); diff --git a/shim.c b/shim.c index fb86589b..6afb35c5 100644 --- a/shim.c +++ b/shim.c @@ -2033,6 +2033,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) } init_openssl(); + get_hsi_mem_info(); efi_status = load_unbundled_trust(global_image_handle); if (EFI_ERROR(efi_status)) { diff --git a/test-mok-mirror.c b/test-mok-mirror.c index 316424ad..38b7ed97 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -398,6 +398,11 @@ test_mok_mirror_with_enough_space(void) EFI_VARIABLE_RUNTIME_ACCESS, .ops = { NONE, }, }, + {.guid = SHIM_LOCK_GUID, + .name = L"HSIStatus", + .attrs = 0, + .ops = { NONE, }, + }, {.guid = { 0, }, .name = NULL, } @@ -424,6 +429,10 @@ test_mok_mirror_with_enough_space(void) .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), .data = test_data_efivars_1_MokListTrustedRT }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, {.name = { 0, }, .data_size = 0, .data = NULL, @@ -614,6 +623,10 @@ test_mok_mirror_setvar_out_of_resources(void) .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), .data = test_data_efivars_1_MokListTrustedRT }, + {.name = "HSIStatus", + .data_size = sizeof(test_data_efivars_1_HSIStatus), + .data = test_data_efivars_1_HSIStatus + }, {.name = { 0, }, .data_size = 0, .data = NULL, -- cgit v1.2.3 From f0958baa7cc0fcce0de09323d89dda7bf23afec3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 18 Feb 2025 15:19:21 -0500 Subject: test-mock-variables: improve some debug prints This changes test-mock-variables and related code to not print all debug messages at SHIM_DEBUG=1, and also adds some prints and comments for context as to what's going on in the tests. Signed-off-by: Peter Jones --- include/test.h | 18 ++++++------ mock-variables.c | 81 +++++++++++++++++++++++++++++++++++++++------------ test-mock-variables.c | 23 +++++++++++++-- 3 files changed, 91 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/test.h b/include/test.h index 5261dbd9..ccb61148 100644 --- a/include/test.h +++ b/include/test.h @@ -85,14 +85,14 @@ extern EFI_RUNTIME_SERVICES *RT; static inline INT64 guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): Comparing "GUID_FMT" to "GUID_FMT"\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(*guid0), GUID_ARGS(*guid1)); #endif if (guid0->Data1 != guid1->Data1) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data1, (INT64)guid1->Data1, @@ -102,7 +102,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data2 != guid1->Data2) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data2, (INT64)guid1->Data2, @@ -112,7 +112,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data3 != guid1->Data3) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data3, (INT64)guid1->Data3, @@ -126,7 +126,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) * representation of it. */ if (guid0->Data4[1] != guid1->Data4[1]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[1], (INT64)guid1->Data4[1], @@ -136,7 +136,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } if (guid0->Data4[0] != guid1->Data4[0]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[0], (INT64)guid1->Data4[0], @@ -147,7 +147,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) for (UINTN i = 2; i < 8; i++) { if (guid0->Data4[i] != guid1->Data4[i]) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x%"PRIx64"-0x%"PRIx64"->0x%"PRIx64"\n", __FILE__, __LINE__-1, __func__, (INT64)guid0->Data4[i], (INT64)guid1->Data4[i], @@ -157,7 +157,7 @@ guidcmp_helper(const EFI_GUID * const guid0, const EFI_GUID * const guid1) } } -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): returning 0x0\n", __FILE__, __LINE__-1, __func__); #endif @@ -177,7 +177,7 @@ guidcmp(const EFI_GUID * const guid0, const EFI_GUID * const guid1) cmp = guidcmp_helper(guida, guidb); ret = cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():CompareGuid("GUID_FMT","GUID_FMT")->%lld (%d)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(*guida), GUID_ARGS(*guidb), cmp, ret); diff --git a/mock-variables.c b/mock-variables.c index 81828560..723cdda2 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -163,7 +163,7 @@ variable_cmp(const struct mock_variable * const v0, ret = CompareGuid(&v0->guid, &v1->guid); ret <<= 8ul; -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3) printf("%s:%d:%s(): "GUID_FMT" %s "GUID_FMT" (0x%011"PRIx64" %"PRId64")\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(v0->guid), @@ -177,7 +177,7 @@ variable_cmp(const struct mock_variable * const v0, } ret = StrCmp(v0->name, v1->name); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if (defined(SHIM_DEBUG) && SHIM_DEBUG > 3) printf("%s:%d:%s(): \"%s\" %s \"%s\" (0x%02hhx (%d)\n", __FILE__, __LINE__-1, __func__, Str2str(v0->name), @@ -284,7 +284,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid, *size = StrSize(result->name); status = EFI_BUFFER_TOO_SMALL; mock_gnvn_post_hook(size, name, guid, &status); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3 printf("%s:%d:%s(): returning %lx\n", __FILE__, __LINE__-1, __func__, status); #endif @@ -297,7 +297,7 @@ mock_gnvn_set_result(UINTN *size, CHAR16 *name, EFI_GUID *guid, status = EFI_SUCCESS; mock_gnvn_post_hook(size, name, guid, &status); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 3 printf("%s:%d:%s(): returning %lx\n", __FILE__, __LINE__-1, __func__, status); #endif @@ -351,15 +351,20 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) struct mock_variable *var; var = list_entry(pos, struct mock_variable, list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) +# if SHIM_DEBUG > 1 printf("%s:%d:%s(): candidate var:%p &var->guid:%p &var->list:%p\n", __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); +# elif SHIM_DEBUG > 0 + printf("%s:%d:%s(): candidate var:%p var->guid:" GUID_FMT"\n", + __FILE__, __LINE__-1, __func__, var, GUID_ARGS(var->guid)); +# endif #endif if (name[0] == 0) { if (CompareGuid(&var->guid, guid) == 0) { #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) - printf("%s:%d:%s(): found\n", - __FILE__, __LINE__-1, __func__); + printf("%s:%d:%s(): found guid in entry var:%p var->name:%p\n", + __FILE__, __LINE__-1, __func__, var, var->name); #endif result = var; found = true; @@ -374,14 +379,14 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) continue; } -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(goal.guid), Str2str(goal.name), GUID_ARGS(var->guid), Str2str(var->name)); #endif if (variable_cmp(&goal, var) == 0) { -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): found\n", __FILE__, __LINE__-1, __func__); #endif @@ -398,8 +403,8 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) __FILE__, __LINE__-1, __func__, GUID_ARGS(result->guid), Str2str(result->name)); } else { - printf("%s:%d:%s(): not found\n", - __FILE__, __LINE__-1, __func__); + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); } #endif @@ -408,13 +413,25 @@ mock_get_next_variable_name(UINTN *size, CHAR16 *name, EFI_GUID *guid) status = EFI_NOT_FOUND; else status = EFI_INVALID_PARAMETER; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif mock_gnvn_post_hook(size, name, guid, &status); +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): not found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif return status; } if (!result) { status = EFI_NOT_FOUND; mock_gnvn_post_hook(size, name, guid, &status); +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): found (found:%d status:0x%016x)\n", + __FILE__, __LINE__-1, __func__, found, status); +#endif return status; } @@ -678,7 +695,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, } var = (struct mock_variable *)buf; -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): var:%p &var->guid:%p &var->list:%p\n", __FILE__, __LINE__-1, __func__, var, &var->guid, &var->list); #endif @@ -695,7 +712,7 @@ mock_new_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, var->attrs = attrs; INIT_LIST_HEAD(&var->list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): var: "GUID_FMT"-%s\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(var->guid), Str2str(var->name)); @@ -772,7 +789,7 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, } #endif -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():Setting "GUID_FMT"-%s size:0x%"PRIx64"\n", __FILE__, __LINE__ - 1, __func__, GUID_ARGS(*guid), Str2str(name), size); @@ -800,7 +817,7 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, list_for_each_safe(pos, tmp, &mock_variables) { found = false; var = list_entry(pos, struct mock_variable, list); -#if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s(): varcmp("GUID_FMT"-%s, "GUID_FMT"-%s)\n", __FILE__, __LINE__-1, __func__, GUID_ARGS(goal.guid), Str2str(goal.name), @@ -832,14 +849,14 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, if (found) break; } -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():var_list:%p &mock_variables:%p cmp:%ld\n", __FILE__, __LINE__ - 1, __func__, var_list, &mock_variables, cmp); #endif if (cmp != 0 || (cmp == 0 && var_list == &mock_variables)) { size_t totalsz = size + StrSize(name); -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 printf("%s:%d:%s():var:%p attrs:0x%lx\n", __FILE__, __LINE__ - 1, __func__, var, attrs); #endif @@ -857,7 +874,7 @@ mock_set_variable(CHAR16 *name, EFI_GUID *guid, UINT32 attrs, UINTN size, return status; } -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 printf("%s:%d:%s(): Adding "GUID_FMT"-%s %s %s\n", __FILE__, __LINE__ - 1, __func__, GUID_ARGS(var->guid), Str2str(var->name), @@ -1088,7 +1105,8 @@ mock_load_one_variable(int dfd, const char * const dirname, char * const name) name[namelen-1] = 0; #if (defined(SHIM_DEBUG) && SHIM_DEBUG != 0) - printf("loading %s-%s\n", &name[namelen], name); + printf("%s:%d:%s(): loading %s-%s\n", __FILE__, __LINE__, __func__, + &name[namelen], name); #endif for (size_t i = 0; i < namelen; i++) namebuf[i] = name[i]; @@ -1118,6 +1136,9 @@ mock_load_variables(const char *const dirname, const char *filters[], DIR *d; struct dirent *entry; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Started loading variablles from \"%s\"\n", dirname); +#endif d = opendir(dirname); if (!d) err(1, "Could not open directory \"%s\"", dirname); @@ -1130,6 +1151,11 @@ mock_load_variables(const char *const dirname, const char *filters[], while ((entry = readdir(d)) != NULL) { size_t len = strlen(entry->d_name); bool found = false; + if (entry->d_type != DT_REG) + continue; +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): maybe adding entry \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name); +#endif if (filters && len > guidstr_size + 1) { char spacebuf[len]; @@ -1140,6 +1166,9 @@ mock_load_variables(const char *const dirname, const char *filters[], if (strlen(filters[i]) > len) continue; if (!strncmp(entry->d_name, filters[i], len)) { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 2 + printf("%s:%d:%s(): filter matched for \"%s\" && \"%s\"\n", __FILE__, __LINE__, __func__, entry->d_name, filters[i]); +#endif found = true; break; } @@ -1147,9 +1176,23 @@ mock_load_variables(const char *const dirname, const char *filters[], } if ((found == false && filter_out == true) || (found == true && filter_out == false)) { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): Adding \"%s\" because filter %s\n", + __FILE__, __LINE__-1, __func__, entry->d_name, + found ? "matched" : "did not match"); +#endif mock_load_one_variable(dfd, dirname, entry->d_name); + } else { +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("%s:%d:%s(): Skipping \"%s\" because filter %s\n", + __FILE__, __LINE__-1, __func__, entry->d_name, + found ? "matched" : "did not match"); +#endif } } +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Done loading variablles from \"%s\"\n", dirname); +#endif closedir(d); #if 0 diff --git a/test-mock-variables.c b/test-mock-variables.c index c7e42b05..79e6628f 100644 --- a/test-mock-variables.c +++ b/test-mock-variables.c @@ -212,14 +212,23 @@ test_gnvn_helper(char *testvars) mock_load_variables(testvars, mok_rt_vars, true); +#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 + dump_mock_variables(__FILE__, __LINE__, __func__); +#endif + + /* + * This tests the sort policy, filtering for only variables in the + * EFI "global" namespace. If ascending the first thing should + * be Boot0000, if descending it should be dbxDefault + */ +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Testing mock variable sorting in the global namespace\n"); +#endif size = sizeof(buf); buf[0] = L'\0'; status = RT->GetNextVariableName(&size, buf, &GV_GUID); assert_equal_goto(status, EFI_SUCCESS, err, "0x%lx != 0x%lx\n"); -#if defined(SHIM_DEBUG) && SHIM_DEBUG != 0 - dump_mock_variables(__FILE__, __LINE__, __func__); -#endif switch (mock_variable_sort_policy) { case MOCK_SORT_DESCENDING: dump_mock_variables_if_wrong(__FILE__, __LINE__, __func__, @@ -236,6 +245,14 @@ test_gnvn_helper(char *testvars) break; } + /* + * Do it again but test for only variables in the Secure Boot + * policy guid namespace. Ascending should be "db", descending + * "dbx". + */ +#if defined(SHIM_DEBUG) && SHIM_DEBUG >= 1 + printf("Testing mock variable sorting in the Secure Boot GUID namespace\n"); +#endif size = sizeof(buf); buf[0] = 0; status = RT->GetNextVariableName(&size, buf, &EFI_SECURE_BOOT_DB_GUID); -- cgit v1.2.3 From b216543d691050d6cdd37c3500571cf67882f1bc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 18 Feb 2025 15:10:22 -0500 Subject: Move mok state variable data flag definitions to the header. Previously the mok mirror state flags were only used in the mok mirroring code. But there are other consumers of that data, namely our variable test cases, and it's useful for them to be able to check the flags. Signed-off-by: Peter Jones --- include/mok.h | 7 +++++++ mok.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/mok.h b/include/mok.h index e6921e09..1b44217c 100644 --- a/include/mok.h +++ b/include/mok.h @@ -19,6 +19,13 @@ struct mok_state_variable; typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *); typedef UINTN (mok_variable_format_helper_t)(UINT8 *buf, size_t sz, struct mok_state_variable *); +#define MOK_MIRROR_KEYDB 0x01 +#define MOK_MIRROR_DELETE_FIRST 0x02 +#define MOK_VARIABLE_MEASURE 0x04 +#define MOK_VARIABLE_LOG 0x08 +#define MOK_VARIABLE_INVERSE 0x10 +#define MOK_VARIABLE_CONFIG_ONLY 0x20 + /* * MoK variables that need to have their storage validated. * diff --git a/mok.c b/mok.c index 5c7f9a2b..f98e36de 100644 --- a/mok.c +++ b/mok.c @@ -144,13 +144,6 @@ categorize_deauthorized(struct mok_state_variable *v) return VENDOR_ADDEND_DB; } -#define MOK_MIRROR_KEYDB 0x01 -#define MOK_MIRROR_DELETE_FIRST 0x02 -#define MOK_VARIABLE_MEASURE 0x04 -#define MOK_VARIABLE_LOG 0x08 -#define MOK_VARIABLE_INVERSE 0x10 -#define MOK_VARIABLE_CONFIG_ONLY 0x20 - struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", .name8 = "MokList", -- cgit v1.2.3 From c41b1f066b9f279b70d933095f277eddbd7c6433 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 27 Jun 2024 15:19:38 -0400 Subject: Add support for DXE memory attribute updates. This adds DXE implementations of get_mem_attrs() and update_mem_attrs() for machines that implement DXE but don't yet have the EFI_MEMORY_ATTRIBUTE_PROTOCOL. Signed-off-by: Peter Jones --- include/guid.h | 10 ++ memattrs.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 290 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/guid.h b/include/guid.h index e32dfc07..26628d1e 100644 --- a/include/guid.h +++ b/include/guid.h @@ -3,6 +3,16 @@ #ifndef SHIM_GUID_H #define SHIM_GUID_H +#define LGUID_FMT L"%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" +#define GUID_FMT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" + +#define GUID_ARGS(guid) \ + ((EFI_GUID)guid).Data1, ((EFI_GUID)guid).Data2, ((EFI_GUID)guid).Data3, \ + ((EFI_GUID)guid).Data4[1], ((EFI_GUID)guid).Data4[0], \ + ((EFI_GUID)guid).Data4[2], ((EFI_GUID)guid).Data4[3], \ + ((EFI_GUID)guid).Data4[4], ((EFI_GUID)guid).Data4[5], \ + ((EFI_GUID)guid).Data4[6], ((EFI_GUID)guid).Data4[7] + extern EFI_GUID BDS_GUID; extern EFI_GUID GV_GUID; extern EFI_GUID SIG_DB; diff --git a/memattrs.c b/memattrs.c index 988459c3..a2c1777c 100644 --- a/memattrs.c +++ b/memattrs.c @@ -44,21 +44,227 @@ uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) return ret; } -EFI_STATUS -get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) +static void +get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp) +{ + static EFI_DXE_SERVICES_TABLE *dst = NULL; + + if (dst == NULL) { + dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid)); + + for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; + dprint(L"Testing configuration table " LGUID_FMT L"\n", GUID_ARGS(ct->VendorGuid)); + if (CompareMem(&ct->VendorGuid, &gEfiDxeServicesTableGuid, sizeof(EFI_GUID)) != 0) + continue; + + dst = (EFI_DXE_SERVICES_TABLE *)ct->VendorTable; + dprint(L"Looking for DXE Services Signature 0x%16llx, found signature 0x%16llx\n", + EFI_DXE_SERVICES_TABLE_SIGNATURE, dst->Hdr.Signature); + if (dst->Hdr.Signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) + continue; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) { + /* + * purposefully not treating this as an error so that HSIStatus + * can tell us about it later. + */ + dprint(L"DXE Services lacks Get/SetMemorySpace* functions\n"); + } + + dprint(L"Setting dxe_services_table to 0x%llx\n", dst); + *dstp = dst; + return; + } + } else { + *dstp = dst; + return; + } + + dst = NULL; + dprint(L"Couldn't find DXE services\n"); +} + +static EFI_STATUS +dxe_get_mem_attrs(uintptr_t physaddr, size_t size, uint64_t *attrs) +{ + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + start = ALIGN_DOWN(physaddr, EFI_PAGE_SIZE); + end = ALIGN_UP(physaddr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + *attrs = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS +dxe_update_mem_attrs(uintptr_t addr, size_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ +#if 0 + EFI_STATUS status; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR desc; + EFI_PHYSICAL_ADDRESS start, end, next; + uint64_t before = 0, after = 0, dxe_set_attrs, dxe_clear_attrs; +#endif + EFI_DXE_SERVICES_TABLE *dst = NULL; + + get_dxe_services_table(&dst); + if (!dst) + return EFI_UNSUPPORTED; + + if (!dst->GetMemorySpaceDescriptor || !dst->SetMemorySpaceAttributes) + return EFI_UNSUPPORTED; + + if (!IS_PAGE_ALIGNED(addr) || !IS_PAGE_ALIGNED(size) || size == 0) { + perror(L"Invalid call %a(addr:0x%llx-0x%llx, size:0x%llx, +%a%a%a, -%a%a%a)\n", + __func__, (unsigned long long)addr, + (unsigned long long)(addr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + if (!IS_PAGE_ALIGNED(addr)) + perror(L" addr is not page aligned\n"); + if (!IS_PAGE_ALIGNED(size)) + perror(L" size is not page aligned\n"); + if (size == 0) + perror(L" size is 0\n"); + return EFI_SUCCESS; + } + + /* + * We know this only works coincidentally, so nerfing it for now + * until we have a chance to debug more thoroughly on these niche + * systems. + */ +#if 0 + start = ALIGN_DOWN(addr, EFI_PAGE_SIZE); + end = ALIGN_UP(addr + size, EFI_PAGE_SIZE); + + for (; start < end; start = next) { + EFI_PHYSICAL_ADDRESS mod_start; + UINT64 mod_size; + + status = dst->GetMemorySpaceDescriptor(start, &desc); + if (EFI_ERROR(status)) { + dprint(L"GetMemorySpaceDescriptor(0x%llx, ...): %r\n", + start, status); + return status; + } + + next = desc.BaseAddress + desc.Length; + + if (desc.GcdMemoryType != EFI_GCD_MEMORY_TYPE_SYSTEM_MEMORY) + continue; + + mod_start = MAX(start, desc.BaseAddress); + mod_size = MIN(end, next) - mod_start; + + before = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + dxe_set_attrs = shim_mem_attrs_to_uefi_mem_attrs(set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, dxe_set_attrs); + dxe_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs(clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, dxe_clear_attrs); + desc.Attributes |= dxe_set_attrs; + desc.Attributes &= ~dxe_clear_attrs; + after = uefi_mem_attrs_to_shim_mem_attrs(desc.Attributes); + + status = dst->SetMemorySpaceAttributes(mod_start, mod_size, desc.Attributes); + if (EFI_ERROR(status)) { + dprint(L"Failed to update memory attrs:0x%0x addr:0x%llx size:0x%0lx status:%r\n", + desc.Attributes, mod_start, mod_size, status); + return status; + } + + break; + } + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); +#endif + + return EFI_SUCCESS; +} + +static void +get_efi_mem_attr_protocol(EFI_MEMORY_ATTRIBUTE_PROTOCOL **protop) +{ + static EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + static bool has_mem_access_proto = true; + + if (proto == NULL && has_mem_access_proto == true) { + EFI_STATUS efi_status; + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) { + has_mem_access_proto = false; + *protop = NULL; + } + } + + *protop = proto; +} + +static EFI_STATUS +efi_get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) { EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; EFI_PHYSICAL_ADDRESS physaddr = addr; EFI_STATUS efi_status; - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) { - dprint(L"No memory attribute protocol found: %r\n", efi_status); - if (!EFI_ERROR(efi_status)) - efi_status = EFI_UNSUPPORTED; - return efi_status; - } + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) { dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", @@ -79,23 +285,22 @@ get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) return efi_status; } -EFI_STATUS -update_mem_attrs(uintptr_t addr, uint64_t size, - uint64_t set_attrs, uint64_t clear_attrs) +static EFI_STATUS +efi_update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) { EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; EFI_PHYSICAL_ADDRESS physaddr = addr; EFI_STATUS efi_status, ret; uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; - efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, - (VOID **)&proto); - if (EFI_ERROR(efi_status) || !proto) - return efi_status; + get_efi_mem_attr_protocol(&proto); + if (!proto) + return EFI_UNSUPPORTED; - efi_status = get_mem_attrs (addr, size, &before); + efi_status = efi_get_mem_attrs(addr, size, &before); if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + dprint(L"efi_get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", (unsigned long long)addr, (unsigned long long)size, &before, efi_status); @@ -116,7 +321,7 @@ update_mem_attrs(uintptr_t addr, uint64_t size, perror(L" size is not page aligned\n"); if (size == 0) perror(L" size is 0\n"); - return 0; + return EFI_SUCCESS; } uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); @@ -140,9 +345,9 @@ update_mem_attrs(uintptr_t addr, uint64_t size, } ret = efi_status; - efi_status = get_mem_attrs (addr, size, &after); + efi_status = efi_get_mem_attrs(addr, size, &after); if (EFI_ERROR(efi_status)) - dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + dprint(L"efi_get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", (unsigned long long)addr, (unsigned long long)size, &after, efi_status); @@ -164,6 +369,37 @@ update_mem_attrs(uintptr_t addr, uint64_t size, return ret; } +EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_update_mem_attrs(addr, size, set_attrs, clear_attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_update_mem_attrs(addr, size, set_attrs, clear_attrs); + + return efi_status; +} + +EFI_STATUS +get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_STATUS efi_status; + + efi_status = efi_get_mem_attrs(addr, size, attrs); + if (!EFI_ERROR(efi_status)) + return efi_status; + + if (efi_status == EFI_UNSUPPORTED) + efi_status = dxe_get_mem_attrs(addr, size, attrs); + + return efi_status; +} + void get_hsi_mem_info(void) { @@ -171,22 +407,10 @@ get_hsi_mem_info(void) uintptr_t addr; uint64_t attrs = 0; uint32_t *tmp_alloc; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL; - addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK; - - efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); - if (EFI_ERROR(efi_status)) { -error: - /* - * In this case we can't actually tell anything, so assume - * and report the worst case scenario. - */ - hsi_status = SHIM_HSI_STATUS_HEAPX | - SHIM_HSI_STATUS_STACKX | - SHIM_HSI_STATUS_ROW; - dprint(L"Setting HSI to 0x%lx due to error: %r\n", hsi_status, efi_status); - return; - } else { + get_efi_mem_attr_protocol(&efiproto); + if (efiproto) { hsi_status = SHIM_HSI_STATUS_HASMAP; dprint(L"Setting HSI to 0x%lx\n", hsi_status); } @@ -196,7 +420,13 @@ error: return; } - hsi_status = SHIM_HSI_STATUS_HASMAP; + addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK; + efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs); + if (EFI_ERROR(efi_status)) { + dprint(L"get_mem_attrs(0x%016llx, 0x%x, &attrs) failed.\n", addr, EFI_PAGE_SIZE); + goto error; + } + if (attrs & MEM_ATTR_W) { dprint(L"get_hsi_mem_info() is on a writable page: 0x%x->0x%x\n", hsi_status, hsi_status | SHIM_HSI_STATUS_ROW); @@ -234,6 +464,18 @@ error: hsi_status, hsi_status | SHIM_HSI_STATUS_HEAPX); hsi_status |= SHIM_HSI_STATUS_HEAPX; } + + return; + +error: + /* + * In this case we can't actually tell anything, so assume + * and report the worst case scenario. + */ + hsi_status = SHIM_HSI_STATUS_HEAPX | + SHIM_HSI_STATUS_STACKX | + SHIM_HSI_STATUS_ROW; + dprint(L"Setting HSI to 0x%lx due to error: %r\n", hsi_status, efi_status); } // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 9269e9b0aa15ae9832f7eba6c5eeef0c5e1f4edb Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 20 Feb 2025 14:44:10 -0500 Subject: Add DXE Services information to HSI This adds three more entries to our HSI data: has-dxe-services-table: technically only tells us if UEFI's LocateProtocol will give us a DXE services table, but practically also tells us if the machine is implementing DXE in any way. has-get-memory-space-descriptor: tells us if DXE->GetMemorySpaceDescriptor is populated has-set-memory-space-descriptor: tells us if DXE->SetMemorySpaceDescriptor is populated Signed-off-by: Peter Jones --- MokVars.txt | 6 ++++++ include/mok.h | 6 ++++++ include/test-data-efivars-1.h | 6 +++++- memattrs.c | 16 ++++++++++++++-- mok.c | 11 +++++++++++ 5 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/MokVars.txt b/MokVars.txt index e6e68ce4..0ab81ff6 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -103,3 +103,9 @@ HSIStatus: Status of various security features: 1: read-only sections are writable has-memory-attribute-protocol: 0: platform does not provide the EFI Memory Attribute Protocol 1: platform does provide the EFI Memory Attribute Protocol + has-dxe-services-table: 0: platform does not provide the DXE Services Table + 1: platform does provide the DXE Services Table + has-get-memory-space-descriptor: 0: platform's DST does not populate GetMemorySpaceDescriptor + 1: platform's DST does populate GetMemorySpaceDescriptor + has-set-memory-space-descriptor: 0: platform's DST does not populate SetMemorySpaceDescriptor + 1: platform's DST does populate SetMemorySpaceDescriptor diff --git a/include/mok.h b/include/mok.h index 1b44217c..cea4c997 100644 --- a/include/mok.h +++ b/include/mok.h @@ -141,6 +141,12 @@ extern UINTN hsi_status; #define SHIM_HSI_STATUS_ROW 0x00000004ULL /* platform provides the EFI Memory Attribute Protocol */ #define SHIM_HSI_STATUS_HASMAP 0x00000008ULL +/* platform provides DXE Services Table */ +#define SHIM_HSI_STATUS_HASDST 0x00000010ULL +/* platform has DST->GetMemorySpaceDescriptor */ +#define SHIM_HSI_STATUS_HASDSTGMSD 0x00000020ULL +/* platform has DST->SetMemorySpaceAttributes */ +#define SHIM_HSI_STATUS_HASDSTSMSA 0x00000040ULL #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index d97a4d6d..7a34ea70 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -110,7 +110,11 @@ static const unsigned char test_data_efivars_1_HSIStatus[] = "heap-is-executable: 0\n" "stack-is-executable: 0\n" "ro-sections-are-writable: 0\n" - "has-memory-attribute-protocol: 0\n"; + "has-memory-attribute-protocol: 0\n" + "has-dxe-services-table: 0\n" + "has-get-memory-space-descriptor: 0\n" + "has-set-memory-space-attributes: 0\n" + ; #endif /* !TEST_DATA_EFIVARS_1_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/memattrs.c b/memattrs.c index a2c1777c..f502805f 100644 --- a/memattrs.c +++ b/memattrs.c @@ -50,7 +50,7 @@ get_dxe_services_table(EFI_DXE_SERVICES_TABLE **dstp) static EFI_DXE_SERVICES_TABLE *dst = NULL; if (dst == NULL) { - dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid)); + dprint(L"Looking for configuration table " LGUID_FMT L"\n", GUID_ARGS(gEfiDxeServicesTableGuid)); for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { EFI_CONFIGURATION_TABLE *ct = &ST->ConfigurationTable[i]; @@ -408,6 +408,7 @@ get_hsi_mem_info(void) uint64_t attrs = 0; uint32_t *tmp_alloc; EFI_MEMORY_ATTRIBUTE_PROTOCOL *efiproto = NULL; + EFI_DXE_SERVICES_TABLE *dst = NULL; get_efi_mem_attr_protocol(&efiproto); if (efiproto) { @@ -415,7 +416,18 @@ get_hsi_mem_info(void) dprint(L"Setting HSI to 0x%lx\n", hsi_status); } - if (!(hsi_status & SHIM_HSI_STATUS_HASMAP)) { + get_dxe_services_table(&dst); + if (dst) { + hsi_status |= SHIM_HSI_STATUS_HASDST; + if (dst->GetMemorySpaceDescriptor) + hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD; + if (dst->SetMemorySpaceAttributes) + hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA; + } + + if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) && + !(hsi_status & SHIM_HSI_STATUS_HASDSTGMSD && + hsi_status & SHIM_HSI_STATUS_HASDSTSMSA)) { dprint(L"No memory protocol, not testing further\n"); return; } diff --git a/mok.c b/mok.c index 97d4a0eb..cb70e7e2 100644 --- a/mok.c +++ b/mok.c @@ -42,6 +42,9 @@ format_hsi_status(UINT8 *buf, size_t sz, const char stackx[] = "\nstack-is-executable: "; const char row[] = "\nro-sections-are-writable: "; const char hasmap[] = "\nhas-memory-attribute-protocol: "; + const char hasdxeservices[] = "\nhas-dxe-services-table: "; + const char hasdsgmsd[] = "\nhas-get-memory-space-descriptor: "; + const char hasdssmsa[] = "\nhas-set-memory-space-attributes: "; const char finale[] = "\n"; char *pos; @@ -51,6 +54,8 @@ format_hsi_status(UINT8 *buf, size_t sz, */ UINTN ret = sizeof(heapx) + sizeof(stackx) + sizeof(row) + sizeof(hasmap) + + sizeof(hasdxeservices) + sizeof(hasdsgmsd) + + sizeof(hasdssmsa) + sizeof(finale); if (buf == 0 || sz < ret) { @@ -67,6 +72,12 @@ format_hsi_status(UINT8 *buf, size_t sz, pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_ROW) ? "1" : "0"); pos = stpcpy(pos, hasmap); pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASMAP) ? "1" : "0"); + pos = stpcpy(pos, hasdxeservices); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDST) ? "1" : "0"); + pos = stpcpy(pos, hasdsgmsd); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTGMSD) ? "1" : "0"); + pos = stpcpy(pos, hasdssmsa); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTSMSA) ? "1" : "0"); stpcpy(pos, finale); return ret; -- cgit v1.2.3 From c868d54def8cebd151964fdeaa8257932b8cfc39 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 24 Feb 2025 12:00:33 -0500 Subject: hexdump: give a different debug log for size==0 Signed-off-by: Peter Jones --- include/hexdump.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/hexdump.h b/include/hexdump.h index e8f4fe1a..6f3d5fa5 100644 --- a/include/hexdump.h +++ b/include/hexdump.h @@ -89,10 +89,14 @@ vhexdumpf(const char *file, int line, const char *func, const CHAR16 *const fmt, if (verbose == 0) return; - if (!data || !size) { + if (!data) { dprint(L"hexdump of a NULL pointer!\n"); return; } + if (!size) { + dprint(L"hexdump of a 0 size region!\n"); + return; + } while (offset < size) { char hexbuf[49]; -- cgit v1.2.3 From 1baf1efb37e2728104765477b12b70aeef3090af Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 25 Feb 2025 10:41:41 -0500 Subject: HSI: Add decode_hsi_bits() for easier reading of the debug log This changes all the HSI bitfield operations to print a string showing the change instead of just hex values. Signed-off-by: Peter Jones --- include/memattrs.h | 1 + memattrs.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 68 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/memattrs.h b/include/memattrs.h index 5c40b4cc..193da988 100644 --- a/include/memattrs.h +++ b/include/memattrs.h @@ -12,6 +12,7 @@ extern EFI_STATUS update_mem_attrs(uintptr_t addr, uint64_t size, uint64_t set_attrs, uint64_t clear_attrs); extern void get_hsi_mem_info(void); +extern char *decode_hsi_bits(UINTN hsi); #endif /* !SHIM_MEMATTRS_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/memattrs.c b/memattrs.c index f502805f..bc571641 100644 --- a/memattrs.c +++ b/memattrs.c @@ -400,6 +400,46 @@ get_mem_attrs(uintptr_t addr, size_t size, uint64_t *attrs) return efi_status; } +char * +decode_hsi_bits(UINTN hsi) +{ + static const struct { + UINTN bit; + char name[16]; + } bits[] = { + {.bit = SHIM_HSI_STATUS_HEAPX, .name = "HEAPX"}, + {.bit = SHIM_HSI_STATUS_STACKX, .name = "STACKX"}, + {.bit = SHIM_HSI_STATUS_ROW, .name = "ROW"}, + {.bit = SHIM_HSI_STATUS_HASMAP, .name = "HASMAP"}, + {.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"}, + {.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"}, + {.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"}, + {.bit = 0, .name = ""}, + }; + static int x = 0; + static char retbufs[2][sizeof(bits)]; + char *retbuf = &retbufs[x % 2][0]; + char *prev = &retbuf[0]; + char *pos = &retbuf[0]; + + x = ( x + 1 ) % 2; + + ZeroMem(retbuf, sizeof(bits)); + + if (hsi == 0) { + prev = stpcpy(retbuf, "0"); + } else { + for (UINTN i = 0; bits[i].bit != 0; i++) { + if (hsi & bits[i].bit) { + prev = stpcpy(pos, bits[i].name); + pos = stpcpy(prev, "|"); + } + } + } + prev[0] = '\0'; + return retbuf; +} + void get_hsi_mem_info(void) { @@ -412,17 +452,30 @@ get_hsi_mem_info(void) get_efi_mem_attr_protocol(&efiproto); if (efiproto) { - hsi_status = SHIM_HSI_STATUS_HASMAP; - dprint(L"Setting HSI to 0x%lx\n", hsi_status); + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASMAP)); + hsi_status |= SHIM_HSI_STATUS_HASMAP; } get_dxe_services_table(&dst); if (dst) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDST)); hsi_status |= SHIM_HSI_STATUS_HASDST; - if (dst->GetMemorySpaceDescriptor) + if (dst->GetMemorySpaceDescriptor) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTGMSD)); hsi_status |= SHIM_HSI_STATUS_HASDSTGMSD; - if (dst->SetMemorySpaceAttributes) + } + if (dst->SetMemorySpaceAttributes) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HASDSTSMSA)); hsi_status |= SHIM_HSI_STATUS_HASDSTSMSA; + } } if (!(hsi_status & SHIM_HSI_STATUS_HASMAP) && @@ -440,8 +493,9 @@ get_hsi_mem_info(void) } if (attrs & MEM_ATTR_W) { - dprint(L"get_hsi_mem_info() is on a writable page: 0x%x->0x%x\n", - hsi_status, hsi_status | SHIM_HSI_STATUS_ROW); + dprint(L"get_hsi_mem_info() is on a writable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_ROW)); hsi_status |= SHIM_HSI_STATUS_ROW; } @@ -453,8 +507,9 @@ get_hsi_mem_info(void) } if (attrs & MEM_ATTR_X) { - dprint(L"Stack variable is on an executable page: 0x%x->0x%x\n", - hsi_status, hsi_status | SHIM_HSI_STATUS_STACKX); + dprint(L"Stack variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_STACKX)); hsi_status |= SHIM_HSI_STATUS_STACKX; } @@ -472,8 +527,9 @@ get_hsi_mem_info(void) goto error; } if (attrs & MEM_ATTR_X) { - dprint(L"Heap variable is on an executable page: 0x%x->0x%x\n", - hsi_status, hsi_status | SHIM_HSI_STATUS_HEAPX); + dprint(L"Heap variable is on an executable page: %a->%a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_HEAPX)); hsi_status |= SHIM_HSI_STATUS_HEAPX; } @@ -487,7 +543,7 @@ error: hsi_status = SHIM_HSI_STATUS_HEAPX | SHIM_HSI_STATUS_STACKX | SHIM_HSI_STATUS_ROW; - dprint(L"Setting HSI to 0x%lx due to error: %r\n", hsi_status, efi_status); + dprint(L"Setting HSI to 0x%lx due to error: %r\n", decode_hsi_bits(hsi_status), efi_status); } // vim:fenc=utf-8:tw=75:noet -- cgit v1.2.3 From 3bce11831343ba6e67740f23ab3a6c6f09bc0bca Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 25 Feb 2025 11:44:11 -0500 Subject: pe: read_header(): allow skipping SecDir content validation When we're parsing the PE header of shim itself from the Loaded Image object, the signatures aren't present, but the Certificate Table entry in the Data Directory has not been cleared, so it'll fail verification. We know when we're doing that, so this patch makes that test optional. Signed-off-by: Peter Jones --- fuzz-pe-relocate.c | 2 +- include/pe.h | 3 ++- pe-relocate.c | 12 ++++++++---- pe.c | 4 ++-- shim.c | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fuzz-pe-relocate.c b/fuzz-pe-relocate.c index 1f62234d..09d38331 100644 --- a/fuzz-pe-relocate.c +++ b/fuzz-pe-relocate.c @@ -28,7 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) memcpy(data_copy, data, size); data_copy[size] = 0; - status = read_header(data_copy, size, &context); + status = read_header(data_copy, size, &context, true); free(data_copy); diff --git a/include/pe.h b/include/pe.h index 9ea9eb44..a1eb8853 100644 --- a/include/pe.h +++ b/include/pe.h @@ -12,7 +12,8 @@ ImageAddress (void *image, uint64_t size, uint64_t address); EFI_STATUS read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context); + PE_COFF_LOADER_IMAGE_CONTEXT *context, + bool check_secdir); EFI_STATUS verify_image(void *data, unsigned int datasize, EFI_LOADED_IMAGE *li, diff --git a/pe-relocate.c b/pe-relocate.c index bde71729..b436d3ec 100644 --- a/pe-relocate.c +++ b/pe-relocate.c @@ -368,7 +368,8 @@ image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) */ EFI_STATUS read_header(void *data, unsigned int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context) + PE_COFF_LOADER_IMAGE_CONTEXT *context, + bool check_secdir) { EFI_IMAGE_DOS_HEADER *DosHdr = data; EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; @@ -542,9 +543,12 @@ read_header(void *data, unsigned int datasize, return EFI_UNSUPPORTED; } - if (context->SecDir->VirtualAddress > datasize || - (context->SecDir->VirtualAddress == datasize && - context->SecDir->Size > 0)) { + if (check_secdir && + (context->SecDir->VirtualAddress > datasize || + (context->SecDir->VirtualAddress == datasize && + context->SecDir->Size > 0))) { + dprint(L"context->SecDir->VirtualAddress:0x%llx context->SecDir->Size:0x%llx datasize:0x%llx\n", + context->SecDir->VirtualAddress, context->SecDir->Size, datasize); perror(L"Malformed security header\n"); return EFI_INVALID_PARAMETER; } diff --git a/pe.c b/pe.c index 40812bb7..d785c44e 100644 --- a/pe.c +++ b/pe.c @@ -406,7 +406,7 @@ EFI_STATUS verify_image(void *data, unsigned int datasize, /* * The binary header contains relevant context and section pointers */ - efi_status = read_header(data, datasize, context); + efi_status = read_header(data, datasize, context, true); if (EFI_ERROR(efi_status)) { perror(L"Failed to read header: %r\n", efi_status); return efi_status; @@ -482,7 +482,7 @@ handle_image (void *data, unsigned int datasize, /* * The binary header contains relevant context and section pointers */ - efi_status = read_header(data, datasize, &context); + efi_status = read_header(data, datasize, &context, true); if (EFI_ERROR(efi_status)) { perror(L"Failed to read header: %r\n", efi_status); return efi_status; diff --git a/shim.c b/shim.c index 6afb35c5..9efbde11 100644 --- a/shim.c +++ b/shim.c @@ -961,7 +961,7 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) in_protocol = 1; - efi_status = read_header(buffer, size, &context); + efi_status = read_header(buffer, size, &context, true); if (EFI_ERROR(efi_status)) goto done; @@ -1016,7 +1016,7 @@ static EFI_STATUS shim_read_header(void *data, unsigned int datasize, EFI_STATUS efi_status; in_protocol = 1; - efi_status = read_header(data, datasize, context); + efi_status = read_header(data, datasize, context, true); in_protocol = 0; return efi_status; -- cgit v1.2.3 From 89e615081af5fbafefeae5b09def3a003e467838 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 20 Feb 2025 19:20:47 -0500 Subject: Add shim's current NX_COMPAT status to HSIStatus hughsie asked me to also make it observable at runtime whether the shim binary that was used to boot was set as NX_COMPAT or not. This adds that into the HSIStatus data as "shim-has-nx-compat-set". Signed-off-by: Peter Jones --- MokVars.txt | 2 ++ include/mok.h | 2 ++ include/pe.h | 3 +++ include/test-data-efivars-1.h | 1 + memattrs.c | 1 + mok.c | 5 ++++- pe-relocate.c | 37 +++++++++++++++++++++++++++++++++---- shim.c | 2 ++ 8 files changed, 48 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/MokVars.txt b/MokVars.txt index 0ab81ff6..6ad8ce70 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -109,3 +109,5 @@ HSIStatus: Status of various security features: 1: platform's DST does populate GetMemorySpaceDescriptor has-set-memory-space-descriptor: 0: platform's DST does not populate SetMemorySpaceDescriptor 1: platform's DST does populate SetMemorySpaceDescriptor + shim-has-nx-compat-set: 0: the running shim binary does not have NX_COMPAT bit set + 1: the running shim binary does have the NX_COMPAT bit set diff --git a/include/mok.h b/include/mok.h index cea4c997..89edf9de 100644 --- a/include/mok.h +++ b/include/mok.h @@ -147,6 +147,8 @@ extern UINTN hsi_status; #define SHIM_HSI_STATUS_HASDSTGMSD 0x00000020ULL /* platform has DST->SetMemorySpaceAttributes */ #define SHIM_HSI_STATUS_HASDSTSMSA 0x00000040ULL +/* This shim has the NX_COMPAT bit set */ +#define SHIM_HSI_STATUS_NX 0x00000100ULL #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/pe.h b/include/pe.h index a1eb8853..ea40184b 100644 --- a/include/pe.h +++ b/include/pe.h @@ -53,5 +53,8 @@ relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, EFI_IMAGE_SECTION_HEADER *Section, void *orig, void *data); +void +get_shim_nx_capability(EFI_HANDLE image_handle); + #endif /* !PE_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index 7a34ea70..259558e0 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -114,6 +114,7 @@ static const unsigned char test_data_efivars_1_HSIStatus[] = "has-dxe-services-table: 0\n" "has-get-memory-space-descriptor: 0\n" "has-set-memory-space-attributes: 0\n" + "shim-has-nx-compat-set: 0\n" ; #endif /* !TEST_DATA_EFIVARS_1_H_ */ diff --git a/memattrs.c b/memattrs.c index bc571641..ed8a3ae2 100644 --- a/memattrs.c +++ b/memattrs.c @@ -414,6 +414,7 @@ decode_hsi_bits(UINTN hsi) {.bit = SHIM_HSI_STATUS_HASDST, .name = "HASDST"}, {.bit = SHIM_HSI_STATUS_HASDSTGMSD, .name = "HASDSTGMSD"}, {.bit = SHIM_HSI_STATUS_HASDSTSMSA, .name = "HASDSTSMSA"}, + {.bit = SHIM_HSI_STATUS_NX, .name = "NX"}, {.bit = 0, .name = ""}, }; static int x = 0; diff --git a/mok.c b/mok.c index cb70e7e2..fb4c1489 100644 --- a/mok.c +++ b/mok.c @@ -45,6 +45,7 @@ format_hsi_status(UINT8 *buf, size_t sz, const char hasdxeservices[] = "\nhas-dxe-services-table: "; const char hasdsgmsd[] = "\nhas-get-memory-space-descriptor: "; const char hasdssmsa[] = "\nhas-set-memory-space-attributes: "; + const char shimhasnx[] = "\nshim-has-nx-compat-set: "; const char finale[] = "\n"; char *pos; @@ -55,7 +56,7 @@ format_hsi_status(UINT8 *buf, size_t sz, UINTN ret = sizeof(heapx) + sizeof(stackx) + sizeof(row) + sizeof(hasmap) + sizeof(hasdxeservices) + sizeof(hasdsgmsd) + - sizeof(hasdssmsa) + + sizeof(hasdssmsa) + sizeof(shimhasnx) + sizeof(finale); if (buf == 0 || sz < ret) { @@ -78,6 +79,8 @@ format_hsi_status(UINT8 *buf, size_t sz, pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTGMSD) ? "1" : "0"); pos = stpcpy(pos, hasdssmsa); pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_HASDSTSMSA) ? "1" : "0"); + pos = stpcpy(pos, shimhasnx); + pos = stpcpy(pos, (hsi_status & SHIM_HSI_STATUS_NX) ? "1" : "0"); stpcpy(pos, finale); return ret; diff --git a/pe-relocate.c b/pe-relocate.c index b436d3ec..4991dbe1 100644 --- a/pe-relocate.c +++ b/pe-relocate.c @@ -375,7 +375,6 @@ read_header(void *data, unsigned int datasize, EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; unsigned long FileAlignment = 0; - UINT16 DllFlags; size_t dos_sz = 0; size_t tmpsz0, tmpsz1; @@ -504,17 +503,17 @@ read_header(void *data, unsigned int datasize, context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; + context->DllCharacteristics = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; } else { context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; + context->DllCharacteristics = PEHdr->Pe32.OptionalHeader.DllCharacteristics; } if ((mok_policy & MOK_POLICY_REQUIRE_NX) && - !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + !(context->DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { perror(L"Policy requires NX, but image does not support NX\n"); return EFI_UNSUPPORTED; } @@ -555,4 +554,34 @@ read_header(void *data, unsigned int datasize, return EFI_SUCCESS; } +void +get_shim_nx_capability(EFI_HANDLE image_handle) +{ + EFI_LOADED_IMAGE_PROTOCOL*li = NULL; + EFI_STATUS efi_status; + PE_COFF_LOADER_IMAGE_CONTEXT context; + + efi_status = BS->HandleProtocol(image_handle, &gEfiLoadedImageProtocolGuid, (void **)&li); + if (EFI_ERROR(efi_status) || !li) { + dprint(L"Could not get loaded image protocol: %r\n", efi_status); + return; + } + + ZeroMem(&context, sizeof(context)); + efi_status = read_header(li->ImageBase, li->ImageSize, &context, false); + if (EFI_ERROR(efi_status)) { + dprint(L"Couldn't parse image header: %r\n", efi_status); + return; + } + + dprint(L"DllCharacteristics:0x%lx\n", context.DllCharacteristics); + if (context.DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT) { + dprint(L"Setting HSI from %a to %a\n", + decode_hsi_bits(hsi_status), + decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_NX)); + hsi_status |= SHIM_HSI_STATUS_NX; + } +} + + // vim:fenc=utf-8:tw=75:noet diff --git a/shim.c b/shim.c index 9efbde11..f85ce00e 100644 --- a/shim.c +++ b/shim.c @@ -1993,6 +1993,8 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); + get_shim_nx_capability(image_handle); + efi_status = set_sbat_uefi_variable_internal(); if (EFI_ERROR(efi_status) && secure_mode()) { perror(L"%s variable initialization failed\n", SBAT_VAR_NAME); -- cgit v1.2.3 From c5c5287b8a0da20b711601921dc34a9cfa2e39db Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 25 Feb 2025 09:00:20 -0500 Subject: peimage.h: minor whitespace fixes Signed-off-by: Peter Jones --- include/peimage.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/peimage.h b/include/peimage.h index 4182a17b..5d049686 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -144,12 +144,12 @@ typedef struct { /// /// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and +/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and /// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary /// after NT additional fields. /// #define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b - + /// /// Optional Header Standard Fields for PE32. /// @@ -195,7 +195,7 @@ typedef struct { /// /// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and +/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and /// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary /// after NT additional fields. /// @@ -465,7 +465,7 @@ typedef struct { #define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 #define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 #define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 - + // // the following values only be referred in PeCoff, not defined in PECOFF. // @@ -500,9 +500,9 @@ typedef struct { #define EFI_IMAGE_REL_I386_SECREL 0x000B #define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address. -// +// // x64 processor relocation types. -// +// #define IMAGE_REL_AMD64_ABSOLUTE 0x0000 #define IMAGE_REL_AMD64_ADDR64 0x0001 #define IMAGE_REL_AMD64_ADDR32 0x0002 -- cgit v1.2.3 From 9c423e09cd9a0196888253b16fe7022deadc20fc Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 25 Feb 2025 14:33:50 -0500 Subject: Some save_logs() improvements. In d972515e608e ("Save the debug and error logs in mok-variables") had a few deficiencies: 1) the size of the result table isn't correctly computed when either errlog or dbglog is 0 sized (much more likely for the former), 2) when we save the error log we leak the allocation for the previous mok variables, and 3) original mok variables were allocated with AllocatePages(), but the new ones were allocated with AllocateZeroPool(). The former guarantees page alignment, which we want here. This fixes all three of these. Signed-off-by: Peter Jones --- errlog.c | 64 ++++++++++++++++++++++++++++++++++++++++++++--------------- globals.c | 3 +++ include/mok.h | 3 +++ mok.c | 2 ++ 4 files changed, 56 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/errlog.c b/errlog.c index fc89ae0a..b43a4bc2 100644 --- a/errlog.c +++ b/errlog.c @@ -172,17 +172,42 @@ format_debug_log(UINT8 *dest, size_t dest_sz) return debug_log_sz; } +void +replace_config_table(EFI_CONFIGURATION_TABLE *CT, EFI_PHYSICAL_ADDRESS new_table, UINTN new_table_pages) +{ + EFI_GUID bogus_guid = { 0x29f2f0db, 0xd025, 0x4aa6, { 0x99, 0x58, 0xa0, 0x21, 0x8b, 0x1d, 0xec, 0x0e }}; + EFI_STATUS efi_status; + + if (CT) { + CopyMem(&CT->VendorGuid, &bogus_guid, sizeof(bogus_guid)); + if (CT->VendorTable && + CT->VendorTable == (void *)(uintptr_t)mok_config_table) { + BS->FreePages(mok_config_table, mok_config_table_pages); + CT->VendorTable = NULL; + } + } + + efi_status = BS->InstallConfigurationTable(&MOK_VARIABLE_STORE, + (void *)(uintptr_t)new_table); + if (EFI_ERROR(efi_status)) { + console_print(L"Could not re-install MoK configuration table: %r\n", efi_status); + } else { + mok_config_table = new_table; + mok_config_table_pages = new_table_pages; + } +} + void save_logs(void) { struct mok_variable_config_entry *cfg_table = NULL; struct mok_variable_config_entry *new_table = NULL; struct mok_variable_config_entry *entry = NULL; + EFI_PHYSICAL_ADDRESS physaddr = 0; + UINTN new_table_pages = 0; size_t new_table_sz; UINTN pos = 0; EFI_STATUS efi_status; - EFI_CONFIGURATION_TABLE *CT; - EFI_GUID bogus_guid = { 0x29f2f0db, 0xd025, 0x4aa6, { 0x99, 0x58, 0xa0, 0x21, 0x8b, 0x1d, 0xec, 0x0e }}; size_t errlog_sz, dbglog_sz; errlog_sz = format_error_log(NULL, 0); @@ -194,6 +219,7 @@ save_logs(void) } for (UINTN i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *CT; CT = &ST->ConfigurationTable[i]; if (CompareGuid(&MOK_VARIABLE_STORE, &CT->VendorGuid) == 0) { @@ -208,18 +234,28 @@ save_logs(void) size_t entry_sz; entry = (struct mok_variable_config_entry *)((uintptr_t)cfg_table + pos); - entry_sz = sizeof(*entry); - entry_sz += entry->data_size; - - if (entry->name[0] != 0) + if (entry->name[0] != 0) { + entry_sz = sizeof(*entry); + entry_sz += entry->data_size; pos += entry_sz; + } } - new_table_sz = pos + 3 * sizeof(*entry) + errlog_sz + dbglog_sz; - new_table = AllocateZeroPool(new_table_sz); + new_table_sz = pos + + (errlog_sz ? sizeof(*entry) + errlog_sz : 0) + + (dbglog_sz ? sizeof(*entry) + dbglog_sz : 0) + + sizeof(*entry); + new_table = NULL; + new_table_pages = ALIGN_UP(new_table_sz + 4*EFI_PAGE_SIZE, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + efi_status = BS->AllocatePages(AllocateAnyPages, EfiRuntimeServicesData, new_table_pages, &physaddr); + if (EFI_ERROR(efi_status)) { + perror(L"Couldn't allocate %llu pages\n", new_table_pages); + return; + } + new_table = (void *)(uintptr_t)physaddr; if (!new_table) return; - + ZeroMem(new_table, new_table_pages * EFI_PAGE_SIZE); CopyMem(new_table, cfg_table, pos); entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); @@ -237,15 +273,11 @@ save_logs(void) format_debug_log(&entry->data[0], dbglog_sz); pos += sizeof(*entry) + dbglog_sz; - // entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); - } - if (CT) { - CopyMem(&CT->VendorGuid, &bogus_guid, sizeof(bogus_guid)); + entry = (struct mok_variable_config_entry *)((uintptr_t)new_table + pos); } - efi_status = BS->InstallConfigurationTable(&MOK_VARIABLE_STORE, new_table); - if (EFI_ERROR(efi_status)) - console_print(L"Could not re-install MoK configuration table: %r\n", efi_status); + + replace_config_table((EFI_CONFIGURATION_TABLE *)cfg_table, physaddr, new_table_pages); } // vim:fenc=utf-8:tw=75 diff --git a/globals.c b/globals.c index de64c44c..11197128 100644 --- a/globals.c +++ b/globals.c @@ -35,4 +35,7 @@ UINT8 mok_policy = 0; UINT32 verbose = 0; +EFI_PHYSICAL_ADDRESS mok_config_table = 0; +UINTN mok_config_table_pages = 0; + // vim:fenc=utf-8:tw=75:noet diff --git a/include/mok.h b/include/mok.h index 89edf9de..f4468ab0 100644 --- a/include/mok.h +++ b/include/mok.h @@ -127,6 +127,9 @@ struct mok_variable_config_entry { UINT8 data[]; }; +extern EFI_PHYSICAL_ADDRESS mok_config_table; +extern UINTN mok_config_table_pages; + /* * bit definitions for MokPolicy */ diff --git a/mok.c b/mok.c index fb4c1489..8367fad5 100644 --- a/mok.c +++ b/mok.c @@ -1305,6 +1305,8 @@ EFI_STATUS import_mok_state(EFI_HANDLE image_handle) config_table = NULL; } else { ZeroMem(config_table, npages << EFI_PAGE_SHIFT); + mok_config_table = (EFI_PHYSICAL_ADDRESS)(uintptr_t)config_table; + mok_config_table_pages = npages; } } -- cgit v1.2.3 From db1f1dac7e7f49b985d1c338c667c34424cb8d54 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 12 Mar 2025 16:28:14 -0400 Subject: Make 'make fanalyzer' work again. It breaks every time somehow. Signed-off-by: Peter Jones --- include/fanalyzer.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/fanalyzer.mk b/include/fanalyzer.mk index a0679e3e..b22656ea 100644 --- a/include/fanalyzer.mk +++ b/include/fanalyzer.mk @@ -21,7 +21,7 @@ fanalyzer-build-all : COMPILER=gcc fanalyzer-build-all : CCACHE_DISABLE=1 fanalyzer-build-all : FEATUREFLAGS+=-fanalyzer fanalyzer-build-all : WERRFLAGS=-Werror=analyzer-null-dereference -fanalyzer-build-all : IGNORE_COMPILER_ERRORS=" || :" +fanalyzer-build-all : IGNORE_COMPILER_ERRORS= || : fanalyzer-build-all : all fanalyzer-no-openssl : | fanalyzer-test -- cgit v1.2.3