From af4e2d127fa1d9f9f51494fec64f10091f719c8c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 22 Aug 2025 00:34:01 -0500 Subject: [PATCH] linuxkm/: implement wc_linuxkm_pie_reloc_tab and wc_linuxkm_normalize_relocations(), and integrate with updateFipsHash(). --- linuxkm/Makefile | 89 ++++++++++----- linuxkm/linuxkm_wc_port.h | 22 ++++ linuxkm/module_hooks.c | 234 +++++++++++++++++++++++++++++++++++++- linuxkm/wolfcrypt.lds | 8 ++ 4 files changed, 324 insertions(+), 29 deletions(-) diff --git a/linuxkm/Makefile b/linuxkm/Makefile index 7fb380cea..3674140cf 100644 --- a/linuxkm/Makefile +++ b/linuxkm/Makefile @@ -82,6 +82,14 @@ ifdef LD endif endif +ifndef READELF + READELF := readelf +endif + +ifndef AWK + AWK := awk +endif + libwolfssl.ko: @if test -z '$(KERNEL_ROOT)'; then echo '$$KERNEL_ROOT is unset' >&2; exit 1; fi @if test -z '$(AM_CFLAGS)$(CFLAGS)'; then echo '$$AM_CFLAGS and $$CFLAGS are both unset.' >&2; exit 1; fi @@ -92,6 +100,31 @@ libwolfssl.ko: @test '$(SRC_TOP)/wolfcrypt/src/wc_port.c' -ef '$(MODULE_TOP)/wolfcrypt/src/wc_port.c' || cp --no-dereference --symbolic-link --no-clobber --recursive '$(SRC_TOP)/wolfcrypt' '$(MODULE_TOP)/' @test '$(SRC_TOP)/src/wolfio.c' -ef '$(MODULE_TOP)/src/wolfio.c' || cp --no-dereference --symbolic-link --no-clobber --recursive '$(SRC_TOP)/src' '$(MODULE_TOP)/' ifeq "$(ENABLED_LINUXKM_PIE)" "yes" + @echo -e "const unsigned int wc_linuxkm_pie_reloc_tab[] = { ~0U };\nconst size_t wc_linuxkm_pie_reloc_tab_length = 1;" > wc_linuxkm_pie_reloc_tab.c + +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= + @$(READELF) --wide -r libwolfssl.ko | \ + $(AWK) 'BEGIN { \ + n=0; \ + printf("%s\n ", \ + "const unsigned int wc_linuxkm_pie_reloc_tab[] = { "); \ + } \ + /^Relocation section '\''\.rela\.text\.wolfcrypt'\''/ { \ + p=1; \ + next; \ + } \ + /^Relocation section/ { \ + p=0; \ + } \ + /^0/ { \ + if (p) { \ + printf("0x%s%s", \ + gensub("^0*","",1,$$1), \ + ((++n%8) ? ", " : ",\n ")); \ + } \ + } \ + END { \ + print "~0U };\nconst size_t wc_linuxkm_pie_reloc_tab_length = sizeof wc_linuxkm_pie_reloc_tab / sizeof wc_linuxkm_pie_reloc_tab[0];";\ + }' > wc_linuxkm_pie_reloc_tab.c +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= else +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) @@ -101,34 +134,34 @@ libwolfssl.ko.signed: libwolfssl.ko ifdef FORCE_NO_MODULE_SIG @echo 'Skipping module signature operation because FORCE_NO_MODULE_SIG.' else - @cd '$(KERNEL_ROOT)' || exit $$?; \ - while read configline; do \ - case "$$configline" in \ - CONFIG_MODULE_SIG*=*) \ - declare "$${configline%=*}"="$${configline#*=}" \ - ;; \ - esac; \ - done < .config || exit $$?; \ - if [[ "$${CONFIG_MODULE_SIG}" = "y" && -n "$${CONFIG_MODULE_SIG_KEY}" && \ - -n "$${CONFIG_MODULE_SIG_HASH}" && ( ! -f '$(MODULE_TOP)/$@' || \ - '$(MODULE_TOP)/$<' -nt '$(MODULE_TOP)/$@' ) ]]; then \ - CONFIG_MODULE_SIG_KEY="$${CONFIG_MODULE_SIG_KEY#\"}"; \ - CONFIG_MODULE_SIG_KEY="$${CONFIG_MODULE_SIG_KEY%\"}"; \ - CONFIG_MODULE_SIG_HASH="$${CONFIG_MODULE_SIG_HASH#\"}"; \ - CONFIG_MODULE_SIG_HASH="$${CONFIG_MODULE_SIG_HASH%\"}"; \ - cp -p '$(MODULE_TOP)/$<' '$(MODULE_TOP)/$@' || exit $$?; \ - ./scripts/sign-file "$${CONFIG_MODULE_SIG_HASH}" \ - "$${CONFIG_MODULE_SIG_KEY}" \ - "$${CONFIG_MODULE_SIG_KEY/%.pem/.x509}" \ - '$(MODULE_TOP)/$@'; \ - sign_file_exitval=$$?; \ - if [[ $$sign_file_exitval != 0 ]]; then \ - $(RM) -f '$(MODULE_TOP)/$@'; \ - exit $$sign_file_exitval; \ - fi; \ - if [[ "$(quiet)" != "silent_" ]]; then \ - echo " Module $@ signed by $${CONFIG_MODULE_SIG_KEY}."; \ - fi \ + @cd '$(KERNEL_ROOT)' || exit $$?; \ + while read configline; do \ + case "$$configline" in \ + CONFIG_MODULE_SIG*=*) \ + declare "$${configline%=*}"="$${configline#*=}" \ + ;; \ + esac; \ + done < .config || exit $$?; \ + if [[ "$${CONFIG_MODULE_SIG}" = "y" && -n "$${CONFIG_MODULE_SIG_KEY}" && \ + -n "$${CONFIG_MODULE_SIG_HASH}" && ( ! -f '$(MODULE_TOP)/$@' || \ + '$(MODULE_TOP)/$<' -nt '$(MODULE_TOP)/$@' ) ]]; then \ + CONFIG_MODULE_SIG_KEY="$${CONFIG_MODULE_SIG_KEY#\"}"; \ + CONFIG_MODULE_SIG_KEY="$${CONFIG_MODULE_SIG_KEY%\"}"; \ + CONFIG_MODULE_SIG_HASH="$${CONFIG_MODULE_SIG_HASH#\"}"; \ + CONFIG_MODULE_SIG_HASH="$${CONFIG_MODULE_SIG_HASH%\"}"; \ + cp -p '$(MODULE_TOP)/$<' '$(MODULE_TOP)/$@' || exit $$?; \ + ./scripts/sign-file "$${CONFIG_MODULE_SIG_HASH}" \ + "$${CONFIG_MODULE_SIG_KEY}" \ + "$${CONFIG_MODULE_SIG_KEY/%.pem/.x509}" \ + '$(MODULE_TOP)/$@'; \ + sign_file_exitval=$$?; \ + if [[ $$sign_file_exitval != 0 ]]; then \ + $(RM) -f '$(MODULE_TOP)/$@'; \ + exit $$sign_file_exitval; \ + fi; \ + if [[ "$(quiet)" != "silent_" ]]; then \ + echo " Module $@ signed by $${CONFIG_MODULE_SIG_KEY}."; \ + fi \ fi endif diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 506e42141..c4e451b3e 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -654,7 +654,26 @@ extern int memcmp(const void *s1, const void *s2, size_t n); #endif + extern const u8 + __wc_text_start[], + __wc_text_end[], + __wc_rodata_start[], + __wc_rodata_end[], + __wc_rwdata_start[], + __wc_rwdata_end[], + __wc_bss_start[], + __wc_bss_end[]; + extern const unsigned int wc_linuxkm_pie_reloc_tab[]; + extern const size_t wc_linuxkm_pie_reloc_tab_length; + extern ssize_t wc_linuxkm_normalize_relocations( + const u8 *text_in, + size_t text_in_len, + u8 *text_out, + ssize_t *cur_index_p); + struct wolfssl_linuxkm_pie_redirect_table { + typeof(wc_linuxkm_normalize_relocations) *wc_linuxkm_normalize_relocations; + #ifndef __ARCH_MEMCMP_NO_REDIRECT typeof(memcmp) *memcmp; #endif @@ -933,6 +952,9 @@ #ifdef __PIE__ + #define wc_linuxkm_normalize_relocations \ + WC_LKM_INDIRECT_SYM(wc_linuxkm_normalize_relocations) + #ifndef __ARCH_MEMCMP_NO_REDIRECT #define memcmp WC_LKM_INDIRECT_SYM(memcmp) #endif diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index 9b28234b8..d9e0064fb 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -43,6 +43,12 @@ #endif #include #include +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif #ifdef WOLFSSL_DEBUG_TRACE_ERROR_CODES enum linux_errcodes { @@ -548,12 +554,202 @@ static int my_preempt_count(void) { return preempt_count(); } +#include "linuxkm/wc_linuxkm_pie_reloc_tab.c" + +static inline int find_reloc_tab_offset(size_t text_in_offset) { + int ret, hop; + if (wc_linuxkm_pie_reloc_tab_length <= 1) + return -1; + if (text_in_offset >= (size_t)((uintptr_t)__wc_text_end - (uintptr_t)__wc_text_start)) + return -1; + if (text_in_offset >= (size_t)wc_linuxkm_pie_reloc_tab[wc_linuxkm_pie_reloc_tab_length - 1]) + return -1; + for (ret = 0, + hop = (int)wc_linuxkm_pie_reloc_tab_length / 2; + hop; + hop >>= 1) + { + if (text_in_offset == (size_t)wc_linuxkm_pie_reloc_tab[ret]) + break; + else if (text_in_offset > (size_t)wc_linuxkm_pie_reloc_tab[ret]) + ret += hop; + else if (ret) + ret -= hop; + } + + while ((ret < (int)wc_linuxkm_pie_reloc_tab_length - 1) && + ((size_t)wc_linuxkm_pie_reloc_tab[ret] < text_in_offset)) + ++ret; + + while ((ret > 0) && + ((size_t)wc_linuxkm_pie_reloc_tab[ret - 1] >= text_in_offset)) + --ret; + + return ret; +} + +#define WC_RODATA_TAG (0x1U << 29) +#define WC_RWDATA_TAG (0x2U << 29) +#define WC_BSS_TAG (0x3U << 29) +#define WC_OTHER_TAG (0x4U << 29) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) +#include +#else +#include +#endif + +ssize_t wc_linuxkm_normalize_relocations( + const u8 *text_in, + size_t text_in_len, + u8 *text_out, + ssize_t *cur_index_p) +{ + ssize_t i = -1; + size_t text_in_offset; +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + int n_text_r = 0, n_rodata_r = 0, n_rwdata_r = 0, n_bss_r = 0, n_other_r = 0; +#endif + + if ((text_in < __wc_text_start) || + (text_in >= __wc_text_end)) + { + return -1; + } + + text_in_offset = (uintptr_t)text_in - (uintptr_t)__wc_text_start; + + if (cur_index_p) + i = *cur_index_p; + + if (i == -1) + i = find_reloc_tab_offset(text_in_offset); + + if (i < 0) { + return i; + } + + WC_SANITIZE_DISABLE(); + memcpy(text_out, text_in, text_in_len); + WC_SANITIZE_ENABLE(); + + for (; + (size_t)i < wc_linuxkm_pie_reloc_tab_length - 1; + ++i) + { + size_t next_reloc = wc_linuxkm_pie_reloc_tab[i]; + int reloc_buf; + uintptr_t abs_ptr; + + next_reloc -= text_in_offset; + + if (next_reloc >= text_in_len) { + /* no more relocations in this buffer. */ + break; + } + if (next_reloc > text_in_len - sizeof reloc_buf) { + /* relocation straddles buffer at end -- caller will try again with + * that relocation at the start. + */ + text_in_len -= (sizeof reloc_buf - 1); + break; + } + + reloc_buf = (int)get_unaligned((int32_t *)&text_out[next_reloc]); + + /* the +4 accounts for the disp32 field size, as RIP points to the next + * instruction byte per the x86_64 ABI. + */ + abs_ptr = (uintptr_t)text_in + next_reloc + 4 + reloc_buf; + + if ((abs_ptr >= (uintptr_t)__wc_text_start) && + (abs_ptr < (uintptr_t)__wc_text_end)) + { + /* internal references in the .wolfcrypt.text segment don't need + * normalization. + */ +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + ++n_text_r; +#endif + continue; + } + else if ((abs_ptr >= (uintptr_t)__wc_rodata_start) && + (abs_ptr < (uintptr_t)__wc_rodata_end)) + { +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + ++n_rodata_r; +#endif + reloc_buf -= (int)((uintptr_t)__wc_rodata_start - + (uintptr_t)__wc_text_start); + reloc_buf |= WC_RODATA_TAG; + } + else if ((abs_ptr >= (uintptr_t)__wc_rwdata_start) && + (abs_ptr < (uintptr_t)__wc_rwdata_end)) + { +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + ++n_rwdata_r; +#endif + reloc_buf -= (int)((uintptr_t)__wc_rwdata_start - + (uintptr_t)__wc_text_start); + reloc_buf |= WC_RWDATA_TAG; + } + else if ((abs_ptr >= (uintptr_t)__wc_bss_start) && + (abs_ptr < (uintptr_t)__wc_bss_end)) + { +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + ++n_bss_r; +#endif + reloc_buf -= (int)((uintptr_t)__wc_bss_start - + (uintptr_t)__wc_text_start); + reloc_buf |= WC_BSS_TAG; + } + else { + /* relocation referring to non-wolfcrypt segment -- these can only + * be stabilized by zeroing them. + */ + reloc_buf = WC_OTHER_TAG; +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + ++n_other_r; + pr_notice("found non-wolfcrypt relocation at text offset 0x%x to " + "addr 0x%lx, text=%px-%px, rodata=%px-%px, " + "rwdata=%px-%px, bss=%px-%px\n", + wc_linuxkm_pie_reloc_tab[i], + abs_ptr, + __wc_text_start, + __wc_text_end, + __wc_rodata_start, + __wc_rodata_end, + __wc_rwdata_start, + __wc_rwdata_end, + __wc_bss_start, + __wc_bss_end); +#endif + } + put_unaligned((u32)reloc_buf, (int32_t *)&text_out[next_reloc]); + } + +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + if (n_other_r > 0) + pr_notice("text_in=%px relocs=%d/%d/%d/%d/%d ret = %zu\n", + text_in, n_text_r, n_rodata_r, n_rwdata_r, n_bss_r, n_other_r, + text_in_len); +#endif + + if (cur_index_p) + *cur_index_p = i; + + return text_in_len; +} + static int set_up_wolfssl_linuxkm_pie_redirect_table(void) { memset( &wolfssl_linuxkm_pie_redirect_table, 0, sizeof wolfssl_linuxkm_pie_redirect_table); + wolfssl_linuxkm_pie_redirect_table.wc_linuxkm_normalize_relocations = + wc_linuxkm_normalize_relocations; + #ifndef __ARCH_MEMCMP_NO_REDIRECT wolfssl_linuxkm_pie_redirect_table.memcmp = memcmp; #endif @@ -958,12 +1154,47 @@ static int updateFipsHash(void) goto out; } - WC_SANITIZE_DISABLE(); +#if defined(WOLFSSL_LINUXKM) && defined(USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE) + { + ssize_t cur_reloc_index = -1; + const byte *text_p = (const byte *)first; + byte *buf = XMALLOC(8192, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (! buf) { + pr_err("ERROR: malloc failed in updateFipsHash()\n"); + ret = MEMORY_E; + goto out; + } + + while (text_p < (const byte *)last) { + ssize_t progress = wc_linuxkm_normalize_relocations( + text_p, + min(8192, (word32)((const byte *)last - text_p)), + buf, + &cur_reloc_index); + if (progress < 0) { + ret = IN_CORE_FIPS_E; + break; + } + ret = crypto_shash_update(desc, buf, (word32)progress); + if (ret) + break; + text_p += progress; + } + + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_SANITIZE_DISABLE(); +#else + WC_SANITIZE_DISABLE(); ret = crypto_shash_update(desc, (byte *)(wc_ptr_t)first, (word32)code_sz); +#endif /* !WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE */ + if (ret) { pr_err("ERROR: crypto_shash_update failed: err %d\n", ret); ret = BAD_STATE_E; + WC_SANITIZE_ENABLE(); goto out; } @@ -983,6 +1214,7 @@ static int updateFipsHash(void) if (ret) { pr_err("ERROR: crypto_shash_update failed: err %d\n", ret); ret = BAD_STATE_E; + WC_SANITIZE_ENABLE(); goto out; } diff --git a/linuxkm/wolfcrypt.lds b/linuxkm/wolfcrypt.lds index 4db5774b2..55a5a78f7 100644 --- a/linuxkm/wolfcrypt.lds +++ b/linuxkm/wolfcrypt.lds @@ -1,19 +1,27 @@ SECTIONS { . = ALIGN(4096); .text.wolfcrypt : { + __wc_text_start = .; *(.text.wolfcrypt) + __wc_text_end = .; } . = ALIGN(4096); .rodata.wolfcrypt : { + __wc_rodata_start = .; *(.rodata.wolfcrypt) + __wc_rodata_end = .; } . = ALIGN(4096); .data.wolfcrypt : { + __wc_rwdata_start = .; *(.data.wolfcrypt) + __wc_rwdata_end = .; } . = ALIGN(4096); .bss.wolfcrypt : { + __wc_bss_start = .; *(.bss.wolfcrypt) + __wc_bss_end = .; } . = ALIGN(4096); }