linuxkm/: implement wc_linuxkm_pie_reloc_tab and wc_linuxkm_normalize_relocations(), and integrate with updateFipsHash().

This commit is contained in:
Daniel Pouzzner
2025-08-22 00:34:01 -05:00
parent 7ab4c6fa14
commit af4e2d127f
4 changed files with 324 additions and 29 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -43,6 +43,12 @@
#endif
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/sha256.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#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 <linux/unaligned.h>
#else
#include <asm-generic/unaligned.h>
#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;
}

View File

@@ -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);
}