From f376ae210e1483a6378a4f86779e856174f07d5a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 18 Feb 2026 23:58:11 -0600 Subject: [PATCH 1/7] Implement Linux kernel module offline integrity hash calculation: Add: * linuxkm/linuxkm-fips-hash.c * linuxkm/linuxkm-fips-hash-wrapper.sh * linuxkm/linuxkm_memory.h Move from linuxkm/module_hooks.c to linuxkm/linuxkm_memory.c: * reloc_layouts[] * find_reloc_tab_offset() * the body of wc_linuxkm_normalize_relocations() as wc_reloc_normalize_text() * most of updateFipsHash() as wc_fips_generate_hash() Move from linuxkm/linuxkm_wc_port.h to linuxkm/linuxkm_memory.h: * struct wc_linuxkm_pie_reloc_tab_ent * enum wc_reloc_dest_segment * enum wc_reloc_type linuxkm/Makefile: * Update GENERATE_RELOC_TAB recipe to populate new fields in struct wc_reloc_table_ent. * Add targets: * libwolfssl-user-build/src/.libs/libwolfssl.so * linuxkm-fips-hash * module-with-matching-fips-hash * module-with-matching-fips-hash-no-sign * Add support for alternate target module name, via LIBWOLFSSL_NAME make variable. linuxkm/linuxkm_wc_port.h and linuxkm/module_hooks.c: * Fixes to make linuxkm-pie work with CONFIG_KASAN. * Implement WC_LINUXKM_STACK_DEBUG: * wc_linuxkm_stack_bottom() * wc_linuxkm_stack_top() * wc_linuxkm_stack_current() * wc_linuxkm_stack_left() * wc_linuxkm_stack_hwm_prepare() * wc_linuxkm_stack_hwm_measure_rel() * wc_linuxkm_stack_hwm_measure_total() wolfssl/wolfcrypt/settings.h: * When WOLFSSL_KERNEL_MODE, make sure WOLFSSL_GENERAL_ALIGNMENT is at least SIZEOF_LONG. * When WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE, make sure WOLFSSL_BASE16 is defined. configure.ac and wolfcrypt/benchmark/benchmark.c: Disable scrypt when KERNEL_MODE_DEFAULTS, due to excessive memory requirements. --- .gitignore | 2 + .wolfssl_known_macro_extras | 5 +- Makefile.am | 9 +- configure.ac | 12 +- linuxkm/Kbuild | 21 +- linuxkm/Makefile | 86 ++- linuxkm/include.am | 3 + linuxkm/linuxkm-fips-hash-wrapper.sh | 67 ++ linuxkm/linuxkm-fips-hash.c | 357 +++++++++ linuxkm/linuxkm_memory.c | 763 ++++++++++++++++++- linuxkm/linuxkm_memory.h | 229 ++++++ linuxkm/linuxkm_wc_port.h | 246 ++++-- linuxkm/module_hooks.c | 1028 +++++++++----------------- wolfcrypt/benchmark/benchmark.c | 6 +- wolfcrypt/src/memory.c | 5 +- wolfssl/wolfcrypt/memory.h | 5 + wolfssl/wolfcrypt/settings.h | 9 + 17 files changed, 2073 insertions(+), 780 deletions(-) create mode 100755 linuxkm/linuxkm-fips-hash-wrapper.sh create mode 100644 linuxkm/linuxkm-fips-hash.c create mode 100644 linuxkm/linuxkm_memory.h diff --git a/.gitignore b/.gitignore index 7174921634e..7a280bc21d9 100644 --- a/.gitignore +++ b/.gitignore @@ -245,6 +245,8 @@ linuxkm/linuxkm/get_thread_size linuxkm/linuxkm linuxkm/src linuxkm/patches/src +linuxkm/libwolfssl-user-build +linuxkm/linuxkm-fips-hash *.nds # Generated during FreeBSD kernel module build. diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index f59a604a4a9..dd128d06612 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -141,6 +141,8 @@ CONFIG_PTHREAD_IPC CONFIG_SCHED_INFO CONFIG_SMP CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH +CONFIG_STACK_GROWSUP +CONFIG_THREAD_INFO_IN_TASK CONFIG_TIMER_TASK_STACK_DEPTH CONFIG_TIMER_TASK_STACK_SIZE CONFIG_TLS_STACK_WOLFSSL @@ -294,6 +296,7 @@ HAVE_THREADX HAVE_TM_TYPE HAVE_VALIDATE_DATE HAVE_VA_COPY +HAVE_WC_FIPS_OPTEST_CONTESTFAILURE_EXPORT HAVE_X448 HONOR_MATH_USED_LENGTH HSM_KEY_TYPE_HMAC_224 @@ -658,7 +661,7 @@ WIFI_101 WIFI_AVAILABLE WIFI_NINA WIN_REUSE_CRYPT_HANDLE -WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE +WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE_SUPPORT WOLFSENTRY_H WOLFSENTRY_NO_JSON WOLFSSL_32BIT_MILLI_TIME diff --git a/Makefile.am b/Makefile.am index 721e471eff8..ea86253f1b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -225,7 +225,8 @@ if BUILD_LINUXKM CFLAGS_FPU_DISABLE CFLAGS_FPU_ENABLE CFLAGS_SIMD_DISABLE CFLAGS_SIMD_ENABLE \ CFLAGS_AUTO_VECTORIZE_DISABLE CFLAGS_AUTO_VECTORIZE_ENABLE \ ASFLAGS_FPU_DISABLE_SIMD_ENABLE ASFLAGS_FPU_ENABLE_SIMD_DISABLE \ - ASFLAGS_FPUSIMD_DISABLE ASFLAGS_FPUSIMD_ENABLE ENABLED_KERNEL_BENCHMARKS + ASFLAGS_FPUSIMD_DISABLE ASFLAGS_FPUSIMD_ENABLE ENABLED_KERNEL_BENCHMARKS \ + FIPS_FLAVOR module: +$(MAKE) -C linuxkm libwolfssl.ko @@ -233,6 +234,12 @@ module: module-update-fips-hash: +$(MAKE) -C linuxkm module-update-fips-hash +module-with-matching-fips-hash: + +$(MAKE) -C linuxkm module-with-matching-fips-hash + +module-with-matching-fips-hash-no-sign: + +$(MAKE) -C linuxkm module-with-matching-fips-hash-no-sign + clean_module: +$(MAKE) -C linuxkm clean diff --git a/configure.ac b/configure.ac index bc0af28bbfe..76eac743feb 100644 --- a/configure.ac +++ b/configure.ac @@ -415,6 +415,9 @@ AC_ARG_ENABLE([fips], [ENABLED_FIPS=$enableval], [ENABLED_FIPS="no"]) +FIPS_FLAVOR="$ENABLED_FIPS" +AC_SUBST([FIPS_FLAVOR]) + # wolfProvider Options AC_ARG_ENABLE([wolfprovider], [AS_HELP_STRING([--enable-wolfprovider],[Enable wolfProvider options (default: disabled)])], @@ -1463,7 +1466,6 @@ then test "$enable_pwdbased" = "" && enable_pwdbased=yes test "$enable_aeskeywrap" = "" && enable_aeskeywrap=yes test "$enable_x963kdf" = "" && enable_x963kdf=yes - test "$enable_scrypt" = "" && test "$enable_hmac" != "no" && enable_scrypt=yes test "$enable_indef" = "" && enable_indef=yes test "$enable_enckeys" = "" && enable_enckeys=yes test "$enable_hashflags" = "" && enable_hashflags=yes @@ -1480,11 +1482,11 @@ then test "$enable_ssh" = "" && test "$enable_hmac" != "no" && enable_ssh=yes test "$enable_rng_bank" = "" && enable_rng_bank=yes - # the compiler optimizer generates a weird out-of-bounds bss reference for - # find_hole() in the FP_ECC implementation. - if test "$ENABLED_LINUXKM_PIE" != yes + if test "$KERNEL_MODE_DEFAULTS" != "yes" then - test "$enable_fpecc" = "" && test "$enable_ecc" != "no" && enable_fpecc=yes + # Scrypt is excluded from kernel module builds (unless explicitly + # enabled) because of its excessive memory requirements. + test "$enable_scrypt" = "" && test "$enable_hmac" != "no" && enable_scrypt=yes fi if test "x$FIPS_VERSION" != "xv1" diff --git a/linuxkm/Kbuild b/linuxkm/Kbuild index aae32bcd483..7922077810e 100644 --- a/linuxkm/Kbuild +++ b/linuxkm/Kbuild @@ -49,7 +49,11 @@ else ifeq "$(KERNEL_ARCH)" "arm" WOLFSSL_CFLAGS += -fno-optimize-sibling-calls -Os endif -obj-m := libwolfssl.o +ifndef LIBWOLFSSL_NAME + LIBWOLFSSL_NAME := libwolfssl +endif + +obj-m := $(LIBWOLFSSL_NAME).o WOLFSSL_OBJ_TARGETS := $(patsubst %, $(obj)/%, $(WOLFSSL_OBJ_FILES)) @@ -88,10 +92,10 @@ ifndef KERNEL_THREAD_STACK_SIZE endif MAX_STACK_FRAME_SIZE=$(shell echo $$(( $(KERNEL_THREAD_STACK_SIZE) / 4))) -libwolfssl-y := $(WOLFSSL_OBJ_FILES) linuxkm/module_hooks.o linuxkm/module_exports.o +$(LIBWOLFSSL_NAME)-y := $(WOLFSSL_OBJ_FILES) linuxkm/module_hooks.o linuxkm/module_exports.o ifeq "$(FIPS_OPTEST)" "1" -libwolfssl-y += linuxkm/optest-140-3/linuxkm_optest_wrapper.o + $(LIBWOLFSSL_NAME)-y += linuxkm/optest-140-3/linuxkm_optest_wrapper.o endif WOLFSSL_CFLAGS_NO_VECTOR_INSNS := $(CFLAGS_SIMD_DISABLE) $(CFLAGS_FPU_DISABLE) @@ -162,7 +166,7 @@ ifdef KERNEL_EXTRA_CFLAGS_REMOVE ccflags-remove-y += $(KERNEL_EXTRA_CFLAGS_REMOVE) endif -$(obj)/libwolfssl.mod.o: ccflags-y := $(PIE_SUPPORT_FLAGS) +$(obj)/$(LIBWOLFSSL_NAME).mod.o: ccflags-y := $(PIE_SUPPORT_FLAGS) $(obj)/wolfcrypt/test/test.o: ccflags-y += -DNO_MAIN_DRIVER -DWOLFSSL_NO_OPTIONS_H $(obj)/wolfcrypt/src/aes.o: ccflags-y := $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_YES_VECTOR_INSNS) $(PIE_FLAGS) $(PIE_SUPPORT_FLAGS) $(obj)/wolfcrypt/benchmark/benchmark.o: ccflags-y := $(WOLFSSL_CFLAGS) $(CFLAGS_FPU_ENABLE) $(CFLAGS_SIMD_ENABLE) $(PIE_SUPPORT_FLAGS) -DNO_MAIN_FUNCTION -DWOLFSSL_NO_OPTIONS_H @@ -331,15 +335,19 @@ endif # auto-generate the exported symbol list, leveraging the WOLFSSL_API visibility tags. # exclude symbols that don't match wc_* or wolf*. EXPORT_SYMBOL := EXPORT_SYMBOL_NS_GPL +ifndef WOLFSSL_NS + WOLFSSL_NS := WOLFSSL +endif $(obj)/linuxkm/module_exports.c: $(src)/module_exports.c.template $(WOLFSSL_OBJ_TARGETS) $(obj)/linuxkm/module_hooks.o @$(RENAME_PIE_TEXT_AND_DATA_SECTIONS) @cp $< $@ || exit $$? if [[ "$${VERSION}" -gt 6 || ("$${VERSION}" -eq 6 && "$${PATCHLEVEL}" -ge 13) ]]; then # use ASCII octal escape to avoid syntax disruption in the awk script. - ns='\042WOLFSSL\042' + ns='\042$(WOLFSSL_NS)\042' else - ns='WOLFSSL' + ns='$(WOLFSSL_NS)' fi +ifndef NO_EXPORTS $(READELF) --symbols --wide $(filter %.o,$^) | $(AWK) '/^ *[0-9]+: / { if ($$8 !~ /^(wc_|wolf|WOLF|TLSX_)/){next;} @@ -348,5 +356,6 @@ $(obj)/linuxkm/module_exports.c: $(src)/module_exports.c.template $(WOLFSSL_OBJ_ } }' >> $@ || exit $$? echo -e "#ifndef NO_CRYPT_TEST\n$(EXPORT_SYMBOL)(wolfcrypt_test, $${ns});\n#endif" >> $@ +endif clean-files := linuxkm src wolfcrypt diff --git a/linuxkm/Makefile b/linuxkm/Makefile index eba05351880..e5524599a5f 100644 --- a/linuxkm/Makefile +++ b/linuxkm/Makefile @@ -21,7 +21,11 @@ .ONESHELL: SHELL=bash -all: libwolfssl.ko libwolfssl.ko.signed +ifndef LIBWOLFSSL_NAME + LIBWOLFSSL_NAME := libwolfssl +endif + +all: $(LIBWOLFSSL_NAME).ko $(LIBWOLFSSL_NAME).ko.signed ifndef MODULE_TOP MODULE_TOP=$(CURDIR) @@ -147,8 +151,9 @@ GENERATE_RELOC_TAB := $(AWK) ' \ n=0; \ bad_relocs=0; \ print "\#include "; \ + print "\#include "; \ printf("%s\n ", \ - "WOLFSSL_LOCAL const struct wc_linuxkm_pie_reloc_tab_ent wc_linuxkm_pie_reloc_tab[] = { "); \ + "WOLFSSL_LOCAL const struct wc_reloc_table_ent wc_linuxkm_pie_reloc_tab[] = { "); \ if ("SECTION_MAP" in ENVIRON) { \ while (getline 0) \ section_map[$$1] = $$2; \ @@ -204,8 +209,10 @@ GENERATE_RELOC_TAB := $(AWK) ' \ print >"/dev/stderr"; \ exit(1); \ } \ - printf(" { .offset = 0x%xU, .dest_segment = %s, .reloc_type = WC_%s },\n", \ - strtonum("0x" gensub("^0*","",1,$$1)), \ + printf(" { .offset = 0x%xU, .dest_offset = 0x%xU, .dest_addend = %+d, .dest_segment = %s, .reloc_type = WC_%s },\n", \ + strtonum("0x" $$1), \ + strtonum("0x" $$4), \ + $$6 strtonum("0x" $$7), \ section_tag, reloc_type); \ } \ } \ @@ -214,8 +221,8 @@ GENERATE_RELOC_TAB := $(AWK) ' \ print "Found " bad_relocs " unresolvable relocations." >"/dev/stderr"; \ exit(1); \ } \ - print " { .offset = ~0U, .dest_segment = WC_R_SEG_NONE, .reloc_type = WC_R_NONE } };"; \ - print "WOLFSSL_LOCAL const unsigned long wc_linuxkm_pie_reloc_tab_length = sizeof wc_linuxkm_pie_reloc_tab / sizeof wc_linuxkm_pie_reloc_tab[0];"; \ + print " { .offset = ~0U, .dest_offset = ~0U, .dest_addend = 0, .dest_segment = WC_R_SEG_NONE, .reloc_type = WC_R_NONE } };"; \ + print "WOLFSSL_LOCAL const unsigned int wc_linuxkm_pie_reloc_tab_length = (unsigned int)(sizeof wc_linuxkm_pie_reloc_tab / sizeof wc_linuxkm_pie_reloc_tab[0]);"; \ }' ifeq "$(V)" "1" @@ -225,8 +232,8 @@ endif # This rule is .PHONY because it doesn't actually build the module -- Kbuild # does, and we always need to call Kbuild to enforce rebuild for dependencies # and config changes. -.PHONY: libwolfssl.ko -libwolfssl.ko: +.PHONY: $(LIBWOLFSSL_NAME).ko +$(LIBWOLFSSL_NAME).ko: @set -e @[[ '$(V)' == 1 ]] && { echo 'MODULE_TOP = "$(MODULE_TOP)"'; echo 'SRC_TOP = "$(SRC_TOP)"'; @@ -303,7 +310,7 @@ else endif .PHONY: module-update-fips-hash -module-update-fips-hash: libwolfssl.ko +module-update-fips-hash: $(LIBWOLFSSL_NAME).ko @set -e @if test -z '$(FIPS_HASH)'; then echo ' $$FIPS_HASH is unset' >&2; exit 1; fi @if [[ ! '$(FIPS_HASH)' =~ [0-9a-fA-F]{64} ]]; then echo ' $$FIPS_HASH is malformed' >&2; exit 1; fi @@ -320,9 +327,64 @@ module-update-fips-hash: libwolfssl.ko if [[ '$(FIPS_HASH)' == "$$current_verifyCore" ]]; then echo ' Supplied FIPS_HASH matches existing verifyCore -- no update needed.'; exit 0; fi; \ echo -n '$(FIPS_HASH)' | dd bs=1 conv=notrunc of="$<" seek=$$verifyCore_offset count=64 status=none && \ echo " FIPS verifyCore updated successfully." && \ - if [[ -f libwolfssl.ko.signed ]]; then $(MAKE) -C . libwolfssl.ko.signed; fi + if [[ -f '$(LIBWOLFSSL_NAME).ko.signed' ]]; then $(MAKE) -C . '$(LIBWOLFSSL_NAME).ko.signed'; fi + + +# linuxkm-fips-hash implements offline (no-load) FIPS hash calculation and graft-in. +# +# libwolfssl.so is built from the same sources as the kernel module, with the +# same FIPS setting, then used with linuxkm-fips-hash to calculate and overwrite +# the hash in libwolfssl.ko. Finally, the module is [re]signed. +# +# Note that libwolfssl.so has to be built from a hierarchy of symlinks, to avoid +# depending on changes/config in the source directory. Also, aside from +# FIPS_FLAVOR, inherited configuration settings in the environment are cleansed. + +libwolfssl-user-build/src/.libs/libwolfssl.so: + @set -e + @$(RM) -rf '$(MODULE_TOP)/libwolfssl-user-build' + @mkdir '$(MODULE_TOP)/libwolfssl-user-build' + @cd '$(MODULE_TOP)/libwolfssl-user-build' + @pushd '$(SRC_TOP)' >/dev/null + @echo -n 'Populating tree of symlinks...' + @readarray -d '' -t srcfiles < <(find examples src support tests testsuite wolfcrypt wolfssl configure *.in build-aux debian rpm scripts certs doc mcapi cmake linuxkm/*.[ch] \( -name options.h -o -name user_settings\* \) -prune -o \( ! -type d \) \( -name '*.[chsSi]' -o -name configure -o -name '*.in' -o -name \*.sh -o -path support/\* -o -path build-aux/\* -o -path debian/\* -o -path rpm/\* -o -path scripts/\* -o -path certs/\* -o -path doc/\* -o -path mcapi/\* -o -path cmake/\* \) -print0) + @popd >/dev/null + @for file in "$${srcfiles[@]}"; do if [[ ! -e "$$file" ]]; then mkdir -p "$$(dirname "$$file")" && cp --no-dereference --symbolic-link --no-clobber '$(SRC_TOP)'/"$$file" "$$file"; fi; done + @echo ' done.' + @echo '__attribute__ ((visibility("default"))) extern const char coreKey[];' > user_settings.h + @echo > user_settings_asm.h + @echo -n 'Configuring libwolfssl.so...' + @unset WOLFSSL_CFLAGS WOLFCRYPT_PIE_FILES ASFLAGS_FPUSIMD_ENABLE ASFLAGS_FPU_DISABLE_SIMD_ENABLE src_libwolfssl_la_OBJECTS WOLFSSL_ASFLAGS AM_CFLAGS WOLFSSL_OBJ_FILES ENABLED_LINUXKM_LKCAPI_REGISTER EXTRA_LDFLAGS CC LD + @./configure --quiet --disable-jobserver --enable-cryptonly --enable-fips="$$FIPS_FLAVOR" CFLAGS='-DWC_SYM_RELOC_TABLES_SUPPORT -DWOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE -DWOLFSSL_USER_SETTINGS -DWOLFSSL_USER_SETTINGS_ASM' + @echo ' done.' + @echo -n 'Compiling and linking libwolfssl.so...' + @$(MAKE) >/dev/null + @echo ' done.' + @echo -n 'Fixing FIPS hash...' + @userhash=$$(wolfcrypt/test/testwolfcrypt 2>&1 | sed -n -E 's/^hash = (.+)$$/\1/p') + @if [[ -z "$$userhash" ]]; then echo ' FIPS hash not found!' >&2; exit 1; fi + @find wolfcrypt/src -name '*fips_test*o' -delete + @$(MAKE) EXTRA_CFLAGS=-DWOLFCRYPT_FIPS_CORE_HASH_VALUE="$$userhash" + @echo ' done.' + +linuxkm-fips-hash: libwolfssl-user-build/src/.libs/libwolfssl.so linuxkm-fips-hash.c + @echo -n 'Compiling linuxkm-fips-hash...' +# note direct invocation of cc -- we are compiling for the build host, not the target host. + @cc -I'$(MODULE_TOP)/libwolfssl-user-build' -o linuxkm-fips-hash linuxkm/linuxkm-fips-hash.c -L '$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath-link='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -lwolfssl + @echo ' done.' + +.PHONY: module-with-matching-fips-hash +module-with-matching-fips-hash: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash + @set -e + ./linuxkm-fips-hash-wrapper.sh "$<" + $(MAKE) -C . '$(LIBWOLFSSL_NAME).ko.signed' + +.PHONY: module-with-matching-fips-hash-no-sign +module-with-matching-fips-hash-no-sign: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash + @set -e + ./linuxkm-fips-hash-wrapper.sh "$<" -libwolfssl.ko.signed: libwolfssl.ko +$(LIBWOLFSSL_NAME).ko.signed: $(LIBWOLFSSL_NAME).ko ifdef FORCE_NO_MODULE_SIG @echo 'Skipping module signature operation because FORCE_NO_MODULE_SIG.' else @@ -371,6 +433,8 @@ clean: $(RM) -rf '$(MODULE_TOP)/linuxkm' $(RM) -rf '$(MODULE_TOP)/wolfcrypt' $(RM) -rf '$(MODULE_TOP)/src' + $(RM) -rf '$(MODULE_TOP)/libwolfssl-user-build' + $(RM) -f '$(MODULE_TOP)/linuxkm-fips-hash' .PHONY: check check: diff --git a/linuxkm/include.am b/linuxkm/include.am index 87cfae72e9b..aceafdf14e0 100644 --- a/linuxkm/include.am +++ b/linuxkm/include.am @@ -7,10 +7,13 @@ EXTRA_DIST += m4/ax_linuxkm.m4 \ linuxkm/Makefile \ linuxkm/README.md \ linuxkm/get_thread_size.c \ + linuxkm/linuxkm-fips-hash.c \ + linuxkm/linuxkm-fips-hash-wrapper.sh \ linuxkm/module_hooks.c \ linuxkm/module_exports.c.template \ linuxkm/pie_redirect_table.c \ linuxkm/linuxkm_memory.c \ + linuxkm/linuxkm_memory.h \ linuxkm/linuxkm_wc_port.h \ linuxkm/x86_vector_register_glue.c \ linuxkm/lkcapi_glue.c \ diff --git a/linuxkm/linuxkm-fips-hash-wrapper.sh b/linuxkm/linuxkm-fips-hash-wrapper.sh new file mode 100755 index 00000000000..49af5994b94 --- /dev/null +++ b/linuxkm/linuxkm-fips-hash-wrapper.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -o noclobber -o nounset -o pipefail -o errexit + +mod_path=$1 + +readarray -t fenceposts < <(readelf --wide --sections --symbols "$mod_path" | awk ' +BEGIN { + fips_fenceposts["wc_linuxkm_pie_reloc_tab"] = "reloc_tab_start"; + fips_fenceposts["wc_linuxkm_pie_reloc_tab_length"] = "reloc_tab_len_start"; + fips_fenceposts["verifyCore"] = "verifyCore_start"; + fips_fenceposts["wolfCrypt_FIPS_first"] = "fips_text_start"; + fips_fenceposts["wolfCrypt_FIPS_last"] = "fips_text_end"; + fips_fenceposts["wolfCrypt_FIPS_ro_start"] = "fips_rodata_start"; + fips_fenceposts["wolfCrypt_FIPS_ro_end"] = "fips_rodata_end"; + singleton_ends["wc_linuxkm_pie_reloc_tab"] = "reloc_tab_end"; + singleton_ends["wc_linuxkm_pie_reloc_tab_length"] = "reloc_tab_len_end"; + singleton_ends["verifyCore"] = "verifyCore_end"; +} + +/^Section Headers:/ { + in_sections = 1; + in_symbols = 0; + next; +} + +/^Symbol table / { + if (! in_sections) { + print "symbol table appeared before section headers." >"/dev/stderr"; + exit(1); + } + in_sections = 0; + in_symbols = 1; + next; +} +{ + if (in_sections) { + if (match($0, + "^[[:space:]]*\\[([^]]+)\\][[:space:]]+\\.([^[:space:].]+)_wolfcrypt[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+[[:space:]]+([0-9a-f]+)[[:space:]]+([0-9a-f]+)[[:space:]]", + section_line_a)) { + segnum = strtonum(section_line_a[1]); + segname = section_line_a[2]; + segstart = section_line_a[3]; + segsize = section_line_a[4]; + seg_starts_by_id[segnum] = strtonum("0x" segstart); + printf("--%s_start\n0x%x\n--%s_end\n0x%x\n", segname, strtonum("0x" segstart), segname, strtonum("0x" segstart) + strtonum("0x" segsize)); + next; + } + } + if (in_symbols) { + if ($7 !~ "^[0-9]+$") + next; + if (($4 != "NOTYPE") && ($4 != "OBJECT") && ($4 != "FUNC")) + next; + if (! ($8 in fips_fenceposts)) + next; + if (! ($7 in seg_starts_by_id)) { + print "segment offset missing for segment " $7 " for symbol " $8 "." >"/dev/stderr"; + exit(1); + } + printf("--%s\n0x%x\n", fips_fenceposts[$8], seg_starts_by_id[$7] + strtonum("0x" $2)); + if ($8 in singleton_ends) + printf("--%s\n0x%x\n", singleton_ends[$8], seg_starts_by_id[$7] + strtonum("0x" $2) + strtonum($3)); + } +}') + +./linuxkm-fips-hash "${fenceposts[@]}" --mod-path "$mod_path" --in-place --quiet diff --git a/linuxkm/linuxkm-fips-hash.c b/linuxkm/linuxkm-fips-hash.c new file mode 100644 index 00000000000..fa09a97ba7c --- /dev/null +++ b/linuxkm/linuxkm-fips-hash.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* failsafe definitions for FIPS <5.3 */ +#ifndef FIPS_IN_CORE_DIGEST_SIZE + #ifndef NO_SHA256 + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA256_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA256 + #elif defined(WOLFSSL_SHA384) + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA384_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA384 + #else + #error Unsupported FIPS hash alg. + #endif +#endif + +#ifndef FIPS_IN_CORE_KEY_SZ + #define FIPS_IN_CORE_KEY_SZ FIPS_IN_CORE_DIGEST_SIZE +#endif +#ifndef FIPS_IN_CORE_VERIFY_SZ + #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_DIGEST_SIZE +#endif + +static const char *user_coreKey; +#ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE +extern const char coreKey[FIPS_IN_CORE_KEY_SZ*2 + 1]; +#endif + +static int hmac_setkey_cb(Hmac *hmac, const byte *key, word32 key_len) { + int ret; + + ret = wc_HmacSetKey(hmac, FIPS_IN_CORE_HASH_TYPE, key, key_len); + if (ret != 0) + return ret; + return 0; +} + +static int hmac_update_cb(Hmac *hmac, const byte *in, word32 in_len) { + return wc_HmacUpdate_fips(hmac, in, in_len); +} + +static int hmac_final_cb(Hmac *hmac, byte *out, word32 out_sz) { + int actual_size = wc_HmacSizeByType(hmac->macType); + if (actual_size < 0) + return actual_size; + if ((int)out_sz != actual_size) + return BUFFER_E; + return wc_HmacFinal(hmac, out); +} + +int main(int argc, char **argv) +{ + Hmac hmac; + int ret; + struct wc_reloc_table_segments seg_map = WC_RELOC_TABLE_SEGMENTS_INITIALIZER; + word32 new_verifyCore_size = FIPS_IN_CORE_DIGEST_SIZE*2 + 1; + char new_verifyCore[new_verifyCore_size]; + const char *progname = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; + const char *mod_path = NULL; + int mod_fd; + struct stat st; + byte *mod_map = NULL; + word32 reloc_tab_len; + struct wc_reloc_counts reloc_counts; + int inplace = 0; + int quiet = 0; + int verbose = 0; + + static const struct option long_options[] = { +#define FENCEPOST_OPT_FLAG (1U << 30) +#define FENCEPOST_OPT(x) { .name = #x, .has_arg = required_argument, .flag = NULL, .val = FENCEPOST_OPT_FLAG | offsetof(typeof(seg_map), x) } + FENCEPOST_OPT(text_start), + FENCEPOST_OPT(text_end), + FENCEPOST_OPT(reloc_tab_start), + FENCEPOST_OPT(reloc_tab_end), + FENCEPOST_OPT(reloc_tab_len_start), + FENCEPOST_OPT(reloc_tab_len_end), + FENCEPOST_OPT(fips_text_start), + FENCEPOST_OPT(fips_text_end), + FENCEPOST_OPT(rodata_start), + FENCEPOST_OPT(rodata_end), + FENCEPOST_OPT(fips_rodata_start), + FENCEPOST_OPT(fips_rodata_end), + FENCEPOST_OPT(verifyCore_start), + FENCEPOST_OPT(verifyCore_end), + FENCEPOST_OPT(data_start), + FENCEPOST_OPT(data_end), + FENCEPOST_OPT(bss_start), + FENCEPOST_OPT(bss_end), + { "core-key", required_argument, NULL, 'k' }, + { "mod-path", required_argument, NULL, 'f' }, + { "in-place", no_argument, NULL, 'i' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } + }; + + ret = wolfCrypt_Init(); + if (ret < 0) { + fprintf(stderr, "%s: wolfCrypt_Init() failed: %s.\n", progname, wc_GetErrorString(ret)); + exit(1); + } + + for (;;) { + int option_index = 0; + int c = getopt_long(argc, argv, "f:ik:qvh", long_options, &option_index); + if (c == -1) + break; + + if (c & FENCEPOST_OPT_FLAG) { + char *eptr; + c &= ~FENCEPOST_OPT_FLAG; + *((unsigned long *)((byte *)&seg_map + c)) = strtoul(optarg, &eptr, 0); + if (*eptr != '\0') { + fprintf(stderr, "%s: %s: supplied arg \"%s\" isn't a valid number.\n", progname, long_options[option_index].name, optarg); + exit(1); + } + continue; + } + + switch (c) { + case 'f': + mod_path = optarg; + break; + case 'i': + inplace = 1; + break; + case 'k': + user_coreKey = optarg; + break; + case 'q': + quiet = 1; + break; + case 'v': + verbose = 1; + break; + case 'h': + printf("usage: %s \\\n", progname); + for (int i=0; i < (int)(sizeof long_options / sizeof long_options[0]); ++i) { + const struct option *opt = &long_options[i]; + if (opt->name == NULL) { + printf("\n"); + continue; + } + if (i > 0) + printf(" \\\n"); + if (opt->has_arg == no_argument) + printf(" --%s", opt->name); + else if (opt->val & FENCEPOST_OPT_FLAG) + printf(" --%s ", opt->name); + else + printf(" --%s ", opt->name); + } + exit(0); + case ':': + fprintf(stderr, "%s: Missing argument. Try --help.\n", progname); + exit(1); + __builtin_unreachable(); + case '?': + fprintf(stderr, "%s: Unrecognized option. Try --help.\n", progname); + exit(1); + __builtin_unreachable(); + default: + fprintf(stderr, "%s: Unexpected error. Try --help.\n", progname); + exit(1); + __builtin_unreachable(); + } + } + + if (optind < argc) { + fprintf(stderr, "%s: unexpected trailing argument(s).\n", progname); + exit(1); + } + + if (user_coreKey == NULL) { +#ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE + user_coreKey = coreKey; +#else + fprintf(stderr, "%s: must supply --core-key.\n", progname); + exit(1); +#endif + } + +#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS + if ((seg_map.fips_text_start != ~0UL) || + (seg_map.fips_text_end != ~0UL) || + (seg_map.fips_rodata_start != ~0UL) || + (seg_map.fips_rodata_end != ~0UL)) + { + fprintf(stderr, "%s: note, ignoring explicit FIPS fenceposts because WC_USE_PIE_FENCEPOSTS_FOR_FIPS.\n", progname); + } + + seg_map.fips_text_start = seg_map.text_start; + seg_map.fips_text_end = seg_map.text_end; + seg_map.fips_rodata_start = seg_map.rodata_start; + seg_map.fips_rodata_end = seg_map.rodata_end; +#endif + + if ((seg_map.text_start == ~0UL) || + (seg_map.text_end == ~0UL) || + (seg_map.reloc_tab_start == ~0UL) || + (seg_map.reloc_tab_end == ~0UL) || + (seg_map.reloc_tab_len_start == ~0UL) || + (seg_map.reloc_tab_len_end == ~0UL) || + (seg_map.fips_text_start == ~0UL) || + (seg_map.fips_text_end == ~0UL) || + (seg_map.rodata_start == ~0UL) || + (seg_map.rodata_end == ~0UL) || + (seg_map.fips_rodata_start == ~0UL) || + (seg_map.fips_rodata_end == ~0UL) || + (seg_map.verifyCore_start == ~0UL) || + (seg_map.verifyCore_end == ~0UL) || + (seg_map.data_start == ~0UL) || + (seg_map.data_end == ~0UL) || + (seg_map.bss_start == ~0UL) || + (seg_map.bss_end == ~0UL)) + { + fprintf(stderr, "%s: segment fencepost(s) missing. Try --help.\n", progname); + exit(1); + } + + if (mod_path == NULL) { + fprintf(stderr, "%s: module path missing. Try --help.\n", progname); + exit(1); + } + + mod_fd = open(mod_path, inplace ? O_RDWR : O_RDONLY); + if (mod_fd < 0) { + fprintf(stderr, "%s: open %s: %m.\n", progname, mod_path); + exit(1); + } + + ret = fstat(mod_fd, &st); + if (ret < 0) { + fprintf(stderr, "%s: fstat %s: %m.\n", progname, mod_path); + exit(1); + } + + if ((seg_map.reloc_tab_start >= seg_map.reloc_tab_end) || + (seg_map.reloc_tab_end >= st.st_size) || + (seg_map.reloc_tab_len_start >= seg_map.reloc_tab_len_end) || + (seg_map.reloc_tab_len_end >= st.st_size)) + { + fprintf(stderr, "%s: supplied reloc_tab fencepost(s) are out of bounds.\n", progname, mod_path, st.st_size); + exit(1); + } + + mod_map = (byte *)mmap(NULL, st.st_size, inplace ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED | MAP_POPULATE, mod_fd, 0); + if (mod_map == MAP_FAILED) { + fprintf(stderr, "%s: mmap() of %s, length %zu: %m.\n", progname, mod_path, st.st_size); + exit(1); + } + + seg_map.start = (unsigned long)mod_map; + seg_map.end = (unsigned long)mod_map + st.st_size; + + seg_map.reloc_tab_start += (unsigned long)mod_map; + seg_map.reloc_tab_end += (unsigned long)mod_map; + seg_map.reloc_tab_len_start += (unsigned long)mod_map; + seg_map.reloc_tab_len_end += (unsigned long)mod_map; + + seg_map.verifyCore_start += (unsigned long)mod_map; + seg_map.verifyCore_end += (unsigned long)mod_map; + seg_map.fips_text_start += (unsigned long)mod_map; + seg_map.fips_text_end += (unsigned long)mod_map; + seg_map.fips_rodata_start += (unsigned long)mod_map; + seg_map.fips_rodata_end += (unsigned long)mod_map; + + seg_map.text_start += (unsigned long)mod_map; + seg_map.text_end += (unsigned long)mod_map; + seg_map.rodata_start += (unsigned long)mod_map; + seg_map.rodata_end += (unsigned long)mod_map; + seg_map.data_start += (unsigned long)mod_map; + seg_map.data_end += (unsigned long)mod_map; + seg_map.bss_start += (unsigned long)mod_map; + seg_map.bss_end += (unsigned long)mod_map; + + ret = wc_HmacInit(&hmac, NULL, INVALID_DEVID); + if (ret != 0) { + fprintf(stderr, "%s: wc_HmacInit() failed: %s.\n", progname, wc_GetErrorString(ret)); + exit(1); + } + + if (seg_map.verifyCore_end - seg_map.verifyCore_start != new_verifyCore_size) { + fprintf(stderr, "%s: unexpected verifyCore length %zu.\n", progname, (size_t)(seg_map.verifyCore_end - seg_map.verifyCore_start)); + goto out; + } + + XMEMSET(&reloc_counts, 0, sizeof(reloc_counts)); + + ret = wc_fips_generate_hash( + &seg_map, + FIPS_IN_CORE_DIGEST_SIZE, + coreKey, + &hmac, + (wc_fips_verifyCore_hmac_setkey_fn)hmac_setkey_cb, + (wc_fips_verifyCore_hmac_update_fn)hmac_update_cb, + (wc_fips_verifyCore_hmac_final_fn)hmac_final_cb, + new_verifyCore, + &new_verifyCore_size, + &reloc_counts); + + if (ret < 0) { + fprintf(stderr, "%s: wc_fips_generate_hash() failed: %s.\n", progname, wc_GetErrorString(ret)); + goto out; + } + + if (verbose) + fprintf(inplace ? stdout : stderr, "FIPS-bounded relocation normalizations: text=%d, rodata=%d, rwdata=%d, bss=%d, other=%d\n", + reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, reloc_counts.bss, reloc_counts.other); + + if (new_verifyCore_size < sizeof new_verifyCore) { + fprintf(stderr, "%s: wc_fips_generate_hash() returned unexpected verifyCore length %u.\n", progname, new_verifyCore_size); + goto out; + } + + if ((! quiet) && (verbose || !inplace)) + printf("%s\n", new_verifyCore); + + if (strcmp((char *)seg_map.verifyCore_start, new_verifyCore) == 0) { + fprintf(stderr, "%s: note, verifyCore already matches.\n", progname); + } + else if (inplace) { + XMEMCPY((void *)seg_map.verifyCore_start, new_verifyCore, new_verifyCore_size); + ret = munmap(mod_map, st.st_size); + if (ret < 0) { + fprintf(stderr, "%s: munmap: %m\n", progname); + exit(1); + } + ret = close(mod_fd); + if (ret < 0) { + fprintf(stderr, "%s: close: %m\n", progname); + exit(1); + } + printf("FIPS integrity hash updated successfully.\n"); + } + + out: + + wc_HmacFree(&hmac); + + if (ret) + exit(1); + else + exit(0); +} diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c index 030dabb2818..3aeeaa99c4e 100644 --- a/linuxkm/linuxkm_memory.c +++ b/linuxkm/linuxkm_memory.c @@ -21,10 +21,771 @@ /* included by wolfcrypt/src/memory.c */ -#if defined(WC_SYM_RELOC_TABLES) && defined(CONFIG_FORTIFY_SOURCE) +#if defined(WOLFSSL_LINUXKM) && defined(WC_SYM_RELOC_TABLES) && defined(CONFIG_FORTIFY_SOURCE) /* needed because FORTIFY_SOURCE inline implementations call fortify_panic(). */ void __my_fortify_panic(const char *name) { pr_emerg("__my_fortify_panic in %s\n", name); BUG(); } #endif + +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + #define RELOC_DEBUG_PRINTF(fmt, ...) WOLFSSL_DEBUG_PRINTF("%s L %d: " fmt, __FILE__, __LINE__, ## __VA_ARGS__) +#else + #define RELOC_DEBUG_PRINTF(...) WC_DO_NOTHING +#endif + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + +static const struct reloc_layout_ent { + const char *name; + word64 mask; + word64 width; + word64 is_signed:1; + word64 is_relative:1; + word64 is_pages:1; + word64 is_pair_lo:1; + word64 is_pair_hi:1; +} reloc_layouts[] = { + [WC_R_X86_64_32] = { "R_X86_64_32", ~0UL, 32, .is_signed = 0, .is_relative = 0 }, + [WC_R_X86_64_32S] = { "R_X86_64_32S", ~0UL, 32, .is_signed = 1, .is_relative = 0 }, + [WC_R_X86_64_64] = { "R_X86_64_64", ~0UL, 64, .is_signed = 0, .is_relative = 0 }, + [WC_R_X86_64_PC32] = { "R_X86_64_PC32", ~0UL, 32, .is_signed = 1, .is_relative = 1 }, + [WC_R_X86_64_PLT32] = { "R_X86_64_PLT32", ~0UL, 32, .is_signed = 1, .is_relative = 1 }, + [WC_R_AARCH64_ABS32] = { "R_AARCH64_ABS32", ~0UL, 32, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_AARCH64_ABS64] = { "R_AARCH64_ABS64", ~0UL, 64, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_AARCH64_ADD_ABS_LO12_NC] = { "R_AARCH64_ADD_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, + [WC_R_AARCH64_ADR_PREL_PG_HI21] = { "R_AARCH64_ADR_PREL_PG_HI21", 0b01100000111111111111111111100000, 32, .is_signed = 1, .is_relative = 1, .is_pages = 1, .is_pair_lo = 0, .is_pair_hi = 1 }, + [WC_R_AARCH64_CALL26] = { "R_AARCH64_CALL26", 0b00000011111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_AARCH64_JUMP26] = { "R_AARCH64_JUMP26", 0b00000011111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_AARCH64_LDST8_ABS_LO12_NC] = { "R_AARCH64_LDST8_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, + [WC_R_AARCH64_LDST16_ABS_LO12_NC] = { "R_AARCH64_LDST16_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, + [WC_R_AARCH64_LDST32_ABS_LO12_NC] = { "R_AARCH64_LDST32_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, + [WC_R_AARCH64_LDST64_ABS_LO12_NC] = { "R_AARCH64_LDST64_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, + [WC_R_AARCH64_PREL32] = { "R_AARCH64_PREL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_ABS32] = { "R_ARM_ABS32", ~0UL, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_PREL31] = { "R_ARM_PREL31", 0b01111111111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_REL32] = { "R_ARM_REL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_THM_CALL] = { "R_ARM_THM_CALL", 0b00000111111111110010111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_THM_JUMP24] = { "R_ARM_THM_JUMP24", 0b00000111111111110010111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_THM_JUMP11] = { "R_ARM_THM_JUMP11", 0b00000000000000000000011111111111, 16, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, + [WC_R_ARM_THM_MOVT_ABS] = { "R_ARM_THM_MOVT_ABS", 0b00000100000011110111000011111111, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 1 }, + [WC_R_ARM_THM_MOVW_ABS_NC] = { "R_ARM_THM_MOVW_ABS_NC", 0b00000100000011110111000011111111, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 } + }; + +static inline long find_reloc_tab_offset( + const struct wc_reloc_table_segments *seg_map, + const struct wc_reloc_table_ent reloc_tab[], + word32 reloc_tab_len, + size_t text_in_offset) +{ + long ret; + unsigned long hop; + if (reloc_tab_len <= 1) { + RELOC_DEBUG_PRINTF("ERROR: %s failed.\n", __FUNCTION__); + return -1; + } + if (text_in_offset >= (size_t)(seg_map->text_end - seg_map->text_start)) { + RELOC_DEBUG_PRINTF("ERROR: %s failed.\n", __FUNCTION__); + return -1; + } + if (text_in_offset >= (size_t)reloc_tab[reloc_tab_len - 1].offset) { + RELOC_DEBUG_PRINTF("ERROR: %s failed.\n", __FUNCTION__); + return -1; + } + for (ret = 0, + hop = reloc_tab_len >> 1; + hop; + hop >>= 1) + { + if (text_in_offset == (size_t)reloc_tab[ret].offset) + break; + else if (text_in_offset > (size_t)reloc_tab[ret].offset) + ret += hop; + else if (ret) + ret -= hop; + } + + while ((ret < (long)reloc_tab_len - 1) && + ((size_t)reloc_tab[ret].offset < text_in_offset)) + ++ret; + + while ((ret > 0) && + ((size_t)reloc_tab[ret - 1].offset >= text_in_offset)) + --ret; + +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + if (ret < 0) + RELOC_DEBUG_PRINTF("ERROR: %s returning %ld.\n", __FUNCTION__, ret); +#endif + return ret; +} + +/* Note we are not currently accommodating endianness conflicts between the + * build and target host, but if we were, these macros would byte swap. + * Currently, we detect and fail early on endianness conflicts. + */ +#define wc_get_unaligned(v) ({ typeof(*(v)) _v_aligned; XMEMCPY((void *)&_v_aligned, (void *)(v), sizeof _v_aligned); _v_aligned; }) +#define wc_put_unaligned(v, v_out) do { typeof(v) _v = (v); XMEMCPY((void *)(v_out), (void *)&_v, sizeof(typeof(*(v_out)))); } while (0) + +ssize_t wc_reloc_normalize_text( + const byte *text_in, + size_t text_in_len, + byte *text_out, + ssize_t *cur_index_p, + const struct wc_reloc_table_segments *seg_map, + struct wc_reloc_counts *reloc_counts) +{ + ssize_t i; + size_t text_in_offset; + const struct wc_reloc_table_ent *last_reloc; /* for error-checking order in reloc_tab[] */ + int n_text_r = 0, n_rodata_r = 0, n_rwdata_r = 0, n_bss_r = 0, n_other_r = 0, n_oob_r = 0; + const struct wc_reloc_table_ent *reloc_tab = (const struct wc_reloc_table_ent *)seg_map->reloc_tab_start; + const word32 reloc_tab_len = *(const word32 *)seg_map->reloc_tab_len_start; + + if ((text_in_len == 0) || + ((uintptr_t)text_in < seg_map->text_start) || + ((uintptr_t)(text_in + text_in_len) > seg_map->text_end)) + { + RELOC_DEBUG_PRINTF("ERROR: %s returning -1 with span %llx-%llx versus segment %llx-%llx.\n", + __FUNCTION__, + (unsigned long long)(uintptr_t)text_in, + (unsigned long long)(uintptr_t)(text_in + text_in_len), + (unsigned long long)seg_map->text_start, + (unsigned long long)seg_map->text_end); + return -1; + } + + text_in_offset = (uintptr_t)text_in - seg_map->text_start; + + if (cur_index_p) + i = *cur_index_p; + else + i = -1; + + if (i == -1) + i = find_reloc_tab_offset(seg_map, reloc_tab, reloc_tab_len, text_in_offset); + + if (i < 0) + return i; + + WC_SANITIZE_DISABLE(); + memcpy(text_out, text_in, text_in_len); + WC_SANITIZE_ENABLE(); + + for (last_reloc = &reloc_tab[i > 0 ? i-1 : 0]; + (size_t)i < reloc_tab_len - 1; + ++i) + { + const struct wc_reloc_table_ent *next_reloc = &reloc_tab[i]; + enum wc_reloc_dest_segment dest_seg; + uintptr_t seg_beg; +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + uintptr_t seg_end; + const char *seg_name; +#endif + word64 reloc_buf = 0; + const struct reloc_layout_ent *layout; + unsigned int next_reloc_rel; + + if (next_reloc->dest_segment == WC_R_SEG_NONE) { + RELOC_DEBUG_PRINTF("BUG: missing dest segment for relocation at reloc_tab[%zd]\n", i); + continue; + } + + if (last_reloc->offset > next_reloc->offset) { + RELOC_DEBUG_PRINTF("BUG: out-of-order offset found at reloc_tab[%zd]: %u > %u\n", + i, last_reloc->offset, next_reloc->offset); + return -1; + } + + last_reloc = next_reloc; + + if (next_reloc->reloc_type >= (sizeof reloc_layouts / sizeof reloc_layouts[0])) { + RELOC_DEBUG_PRINTF("BUG: unknown relocation type %u found at reloc_tab[%zd]\n", + next_reloc->reloc_type, i); + return -1; + } + + layout = &reloc_layouts[next_reloc->reloc_type]; + + switch (layout->width) { + case 32: + case 64: + case 16: + break; + default: + RELOC_DEBUG_PRINTF("BUG: unexpected relocation width %llu found at reloc_tab[%lld], reloc type %u\n", + (unsigned long long)layout->width, (long long)i, next_reloc->reloc_type); + return -1; + } + + /* provisionally assign the destination segment from the reloc record -- + * it may wind up getting overridden later if things don't go as + * expected. + */ + dest_seg = next_reloc->dest_segment; + + /* next_reloc_rel is the offset of the relocation relative to the start + * of the current text chunk (text_in). i.e., text_in + next_reloc_rel + * is the start of the relocation. + */ + next_reloc_rel = next_reloc->offset - text_in_offset; + + if (next_reloc_rel >= text_in_len) { + /* no more relocations in this buffer. */ + break; + } + + if (next_reloc_rel > text_in_len - layout->width) { + /* relocation straddles buffer at end -- caller will try again with + * that relocation at the start. + */ + text_in_len = next_reloc_rel; + break; + } + + /* set reloc_buf to the address bits from the live text segment. on + * ARM, this will often also pull in some opcode bits, which we mask out. + */ + if (layout->is_signed) { + /* Note, the intermediate cast to sword64 is necessary to + * sign-extend the value to 64 bits before unsigned + * reinterpretation. Normalization later relies on this. + */ + switch (layout->width) { + case 32: + reloc_buf = (word64)(sword64)(wc_get_unaligned((sword32 *)&text_out[next_reloc_rel]) & (sword32)layout->mask); + break; + case 64: + reloc_buf = (word64)(sword64)(wc_get_unaligned((sword64 *)&text_out[next_reloc_rel]) & (sword64)layout->mask); + break; + case 16: + reloc_buf = (word64)(sword64)(wc_get_unaligned((sword16 *)&text_out[next_reloc_rel]) & (sword16)layout->mask); + break; + } + } + else { + switch (layout->width) { + case 32: + reloc_buf = (word64)(wc_get_unaligned((word32 *)&text_out[next_reloc_rel]) & (word32)layout->mask); + break; + case 64: + reloc_buf = (word64)(wc_get_unaligned((word64 *)&text_out[next_reloc_rel]) & layout->mask); + break; + case 16: + reloc_buf = (word64)(wc_get_unaligned((word16 *)&text_out[next_reloc_rel]) & (word16)layout->mask); + break; + } + } + + switch (dest_seg) { + case WC_R_SEG_TEXT: + seg_beg = seg_map->text_start; + ++n_text_r; + #ifdef DEBUG_LINUXKM_PIE_SUPPORT + seg_end = seg_map->text_end; + seg_name = "text"; + #endif + break; + case WC_R_SEG_RODATA: + seg_beg = seg_map->rodata_start; + ++n_rodata_r; + #ifdef DEBUG_LINUXKM_PIE_SUPPORT + seg_end = seg_map->rodata_end; + seg_name = "rodata"; + #endif + break; + case WC_R_SEG_RWDATA: + seg_beg = seg_map->data_start; + ++n_rwdata_r; + #ifdef DEBUG_LINUXKM_PIE_SUPPORT + seg_end = seg_map->data_end; + seg_name = "data"; + #endif + break; + case WC_R_SEG_BSS: + seg_beg = seg_map->bss_start; + ++n_bss_r; + #ifdef DEBUG_LINUXKM_PIE_SUPPORT + seg_end = seg_map->bss_end; + seg_name = "bss"; + #endif + break; + default: + case WC_R_SEG_NONE: + dest_seg = WC_R_SEG_OTHER; + FALL_THROUGH; + case WC_R_SEG_OTHER: + seg_beg = 0; + #ifdef DEBUG_LINUXKM_PIE_SUPPORT + seg_end = 0; + seg_name = "other"; + #endif + break; + } + + switch (next_reloc->reloc_type) { + case WC_R_X86_64_PC32: + case WC_R_X86_64_PLT32: + case WC_R_X86_64_32: + case WC_R_X86_64_32S: + case WC_R_X86_64_64: + + if (dest_seg != WC_R_SEG_OTHER) { +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + word64 raw_dest_addr = reloc_buf; +#endif + if (seg_map->text_is_live) { + /* note these normalize to the base address of the + * destination symbol, S, and removes the addend A, which is + * baked into the reloc_tab for each relocation. + */ + if (layout->is_relative) + reloc_buf = reloc_buf + (uintptr_t)next_reloc->offset - (uintptr_t)next_reloc->dest_addend - (seg_beg - seg_map->text_start); + else + reloc_buf = reloc_buf - seg_beg - (uintptr_t)next_reloc->dest_addend; + } + else { + reloc_buf = (word64)next_reloc->dest_offset; + } +#ifdef DEBUG_LINUXKM_PIE_SUPPORT + if (reloc_buf >= seg_end - seg_beg) { + ++n_oob_r; + RELOC_DEBUG_PRINTF("WARNING: normalized value is out of bounds (%s0x%lx) at index %ld, text offset 0x%x, reloc type %s, " + "dest seg .%s_wolfcrypt, offset from text to dest segment %s0x%lx, raw dest addr %s0x%lx, " + "seg span 0x%lx - 0x%lx, seg size 0x%lx, text base 0x%lx\n", + (sword64)reloc_buf < 0 ? "-" : "", + (sword64)reloc_buf < 0 ? -reloc_buf : reloc_buf, + i, + next_reloc->offset, + layout->name, + seg_name, + seg_beg < seg_map->text_start ? "-" : "+", + seg_beg < seg_map->text_start ? (word64)seg_map->text_start - seg_beg : seg_beg - (word64)seg_map->text_start, + (layout->is_signed && ((sword64)raw_dest_addr < 0)) ? "-" : "", + (layout->is_signed && ((sword64)raw_dest_addr < 0)) ? (word64)-(sword64)raw_dest_addr : raw_dest_addr, + (word64)seg_beg, + (word64)seg_end, + (word64)(seg_end - seg_beg), + (word64)seg_map->text_start); + } +#endif + } + + break; + + case WC_R_ARM_ABS32: + case WC_R_ARM_PREL31: + case WC_R_ARM_REL32: + case WC_R_ARM_THM_CALL: + case WC_R_ARM_THM_JUMP11: + case WC_R_ARM_THM_JUMP24: + case WC_R_ARM_THM_MOVT_ABS: + case WC_R_ARM_THM_MOVW_ABS_NC: + /* Don't attempt to reconstruct ARM destination addresses -- just + * normalize to zero. They can be reconstructed using the + * parameters in reloc_layouts[] and reloc_tab[] but it's very + * fidgety. + */ + reloc_buf = 0; + break; + + case WC_R_AARCH64_ABS32: + case WC_R_AARCH64_ABS64: + case WC_R_AARCH64_ADD_ABS_LO12_NC: + case WC_R_AARCH64_ADR_PREL_PG_HI21: + case WC_R_AARCH64_CALL26: + case WC_R_AARCH64_JUMP26: + case WC_R_AARCH64_LDST16_ABS_LO12_NC: + case WC_R_AARCH64_LDST32_ABS_LO12_NC: + case WC_R_AARCH64_LDST64_ABS_LO12_NC: + case WC_R_AARCH64_LDST8_ABS_LO12_NC: + case WC_R_AARCH64_PREL32: + + /* Don't attempt to reconstruct ARM destination addresses -- just + * normalize to zero. They can be reconstructed using the + * parameters in reloc_layouts[] and reloc_tab[] but it's very + * fidgety. + */ + reloc_buf = 0; + break; + + default: + RELOC_DEBUG_PRINTF("BUG: unrecognized relocation type %u in reloc record %zu, text offset 0x%x\n", + (unsigned)next_reloc->reloc_type, i, reloc_tab[i].offset); + ++n_oob_r; + dest_seg = WC_R_SEG_OTHER; + } + + if (dest_seg == WC_R_SEG_OTHER) { + /* relocation referring to non-wolfcrypt segment -- these can only + * be stabilized by zeroing them. + */ + reloc_buf = 0; + + ++n_other_r; + RELOC_DEBUG_PRINTF("found non-wolfcrypt relocation at index %lld, text offset 0x%x.\n", + (long long)i, reloc_tab[i].offset); + } + + /* xor in a label identifying the dest segment and reloc type. */ + reloc_buf ^= dest_seg << (layout->width - WC_RELOC_DEST_SEGMENT_BITS); + reloc_buf ^= next_reloc->reloc_type << (layout->width - (WC_RELOC_DEST_SEGMENT_BITS + WC_RELOC_TYPE_BITS)); + + /* write the modified reloc_buf to the destination buffer. */ + switch (layout->width) { + case 32: + wc_put_unaligned((word32)reloc_buf, (word32 *)&text_out[next_reloc_rel]); + break; + case 64: + wc_put_unaligned(reloc_buf, (word64 *)&text_out[next_reloc_rel]); + break; + case 16: + wc_put_unaligned((word16)reloc_buf, (word16 *)&text_out[next_reloc_rel]); + break; + } + } + + if (reloc_counts) { + reloc_counts->text += n_text_r; + reloc_counts->rodata += n_rodata_r; + reloc_counts->rwdata += n_rwdata_r; + reloc_counts->bss += n_bss_r; + reloc_counts->other += n_other_r; + } + + if ((n_other_r > 0) || (n_oob_r > 0)) + RELOC_DEBUG_PRINTF("text_in=%llx relocs=%d/%d/%d/%d/%d/%d ret = %llu\n", + (unsigned long long)(uintptr_t)text_in, n_text_r, n_rodata_r, + n_rwdata_r, n_bss_r, n_other_r, n_oob_r, + (unsigned long long)text_in_len); + + if (cur_index_p) + *cur_index_p = i; + + return (ssize_t)text_in_len; +} + +#endif /* WC_SYM_RELOC_TABLES || WC_SYM_RELOC_TABLES_SUPPORT */ + +#ifdef HAVE_FIPS + +#ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE + +#include +#ifndef MAX_FIPS_DATA_SZ + #define MAX_FIPS_DATA_SZ 10000000 +#endif +#ifndef MAX_FIPS_CODE_SZ + #define MAX_FIPS_CODE_SZ 10000000 +#endif + +#include + +#ifndef NO_SHA256 + #include +#endif +#if defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512) + #include +#endif + +/* failsafe definitions for FIPS <5.3 */ +#ifndef FIPS_IN_CORE_DIGEST_SIZE + #ifndef NO_SHA256 + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA256_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA256 + #elif defined(WOLFSSL_SHA384) + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA384_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA384 + #elif defined(WOLFSSL_SHA512) + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA512_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA512 + #else + #error Unsupported FIPS hash alg. + #endif +#endif + +#ifndef FIPS_IN_CORE_KEY_SZ + #define FIPS_IN_CORE_KEY_SZ FIPS_IN_CORE_DIGEST_SIZE +#endif +#ifndef FIPS_IN_CORE_VERIFY_SZ + #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_DIGEST_SIZE +#endif + +int wc_fips_generate_hash( + const struct wc_reloc_table_segments *seg_map, + word32 digest_size, + const char *hmac_key_base16, + void *hmac_ctx, + wc_fips_verifyCore_hmac_setkey_fn hmac_setkey, + wc_fips_verifyCore_hmac_update_fn hmac_update, + wc_fips_verifyCore_hmac_final_fn hmac_final, + char *out, + word32 *out_size, + struct wc_reloc_counts *reloc_counts) +{ + word32 binCoreSz = FIPS_IN_CORE_KEY_SZ; + int ret; + byte *hash = NULL; + byte *binCoreKey = NULL; + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + if (seg_map->text_is_live) { + if ((seg_map->reloc_tab_start == 0) || + (seg_map->reloc_tab_len_start == 0)) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + } + else { + if ((seg_map->reloc_tab_end == 0) || + (seg_map->reloc_tab_len_end == 0)) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + } +#endif + + if (((seg_map->end > 0) && (seg_map->start >= seg_map->end)) || + (seg_map->fips_text_start >= seg_map->fips_text_end) || + (seg_map->fips_rodata_start >= seg_map->fips_rodata_end) +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + || + ((seg_map->reloc_tab_end != 0) && (seg_map->reloc_tab_start >= seg_map->reloc_tab_end)) || + ((seg_map->reloc_tab_len_end != 0) && (seg_map->reloc_tab_len_start >= seg_map->reloc_tab_len_end)) || + (seg_map->text_start >= seg_map->text_end) || + (seg_map->rodata_start >= seg_map->rodata_end) || + (seg_map->data_start >= seg_map->data_end) || + (seg_map->bss_start >= seg_map->bss_end) +#endif + ) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + + if (seg_map->end > 0) { + if ((seg_map->fips_text_start < seg_map->start) || + (seg_map->fips_text_end >= seg_map->end) || + (seg_map->fips_rodata_start < seg_map->start) || + (seg_map->fips_rodata_end > seg_map->end) || + (seg_map->verifyCore_start < seg_map->start) || + (seg_map->verifyCore_end >= seg_map->end) +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + || + ((seg_map->reloc_tab_end != 0) && + ((seg_map->reloc_tab_start < seg_map->start) || + (seg_map->reloc_tab_end >= seg_map->end))) || + ((seg_map->reloc_tab_len_end != 0) && + ((seg_map->reloc_tab_len_start < seg_map->start) || + (seg_map->reloc_tab_len_end >= seg_map->end))) || + (seg_map->text_start < seg_map->start) || + (seg_map->text_end >= seg_map->end) || + (seg_map->rodata_start < seg_map->start) || + (seg_map->rodata_end >= seg_map->end) || + (seg_map->data_start < seg_map->start) || + (seg_map->data_end >= seg_map->end) || + (seg_map->bss_start < seg_map->start) || + (seg_map->bss_end >= seg_map->end) +#endif + ) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BUFFER_E; + } + } + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + if ((seg_map->reloc_tab_len_end != 0) && + (seg_map->reloc_tab_len_end - seg_map->reloc_tab_len_start != sizeof(word32))) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + else if (seg_map->reloc_tab_len_start & (sizeof(word32) - 1)) { + /* fprintf(stderr, "%s: seg_map->reloc_tab_len_start isn't properly aligned: 0x%llx.\n", progname, ( + unsigned long long)seg_map->reloc_tab_len_start); */ + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_ALIGN_E; + } + else { + /* Note we don't currently handle modules that are endian-conflicted + * with the build host -- that'll be caught here, when reloc_tab_len is + * a nonsense byte-swapped value, or the final reloc_tab ent has + * nonsense flags. + */ + word32 reloc_tab_len = *(const word32 *)seg_map->reloc_tab_len_start; + const struct wc_reloc_table_ent *reloc_tab = (const struct wc_reloc_table_ent *)seg_map->reloc_tab_start; + if (reloc_tab_len == 0) { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + else if ((seg_map->end != 0) && + ((unsigned long)(reloc_tab + reloc_tab_len) > seg_map->end)) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + else if ((reloc_tab[reloc_tab_len - 1].dest_segment != WC_R_SEG_NONE) || + (reloc_tab[reloc_tab_len - 1].reloc_type != WC_R_NONE)) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + else if ((seg_map->reloc_tab_end != 0) && + (seg_map->reloc_tab_end - seg_map->reloc_tab_start != sizeof(struct wc_reloc_table_ent) * *(const word32 *)seg_map->reloc_tab_len_start)) + { + /* + fprintf(stderr, "%s: wc_linuxkm_pie_reloc_tab_length from module (%u) is inconsistent with actual reloc_tab size %llu.\n", + progname, + *(const word32 *)seg_map->reloc_tab_len_start, + (unsigned long long)(seg_map->reloc_tab_end - seg_map->reloc_tab_start)); + */ + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + } +#endif + + if (out_size == NULL) { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BAD_FUNC_ARG; + } + + if (*out_size < (digest_size * 2) + 1) { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BUFFER_E; + } + + hash = XMALLOC(digest_size, 0, DYNAMIC_TYPE_TMP_BUFFER); + if (hash == NULL) { + ret = MEMORY_E; + RELOC_DEBUG_PRINTF("XMALLOC() failed.\n"); + goto out; + } + + binCoreKey = XMALLOC(binCoreSz, 0, DYNAMIC_TYPE_TMP_BUFFER); + if (binCoreKey == NULL) { + ret = MEMORY_E; + RELOC_DEBUG_PRINTF("XMALLOC() failed.\n"); + goto out; + } + + { + word32 base16_out_len = binCoreSz; + ret = Base16_Decode((const byte *)hmac_key_base16, strlen(hmac_key_base16), binCoreKey, &base16_out_len); + if (ret != 0) { + RELOC_DEBUG_PRINTF("Base16_Decode() failed.\n"); + goto out; + } + if (base16_out_len != binCoreSz) { + ret = BAD_FUNC_ARG; + RELOC_DEBUG_PRINTF("assert failed.\n"); + goto out; + } + } + + ret = hmac_setkey(hmac_ctx, binCoreKey, binCoreSz); + if (ret) { + RELOC_DEBUG_PRINTF("hmac_setkey() failed.\n"); + goto out; + } + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + { + ssize_t cur_reloc_index = -1; + const byte *text_p = (const byte *)seg_map->fips_text_start; + byte *buf = XMALLOC(8192, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + if (! buf) { + ret = MEMORY_E; + RELOC_DEBUG_PRINTF("XMALLOC() failed.\n"); + goto out; + } + + while (text_p < (const byte *)seg_map->fips_text_end) { + /* wc_reloc_normalize_text() does its own WC_SANITIZE_DISABLE()s, so + * we defer it here. + */ + ssize_t progress = wc_reloc_normalize_text( + text_p, + min(8192, (word32)((const byte *)seg_map->fips_text_end - text_p)), + buf, + &cur_reloc_index, + seg_map, + reloc_counts); + if (progress <= 0) { + ret = IN_CORE_FIPS_E; + RELOC_DEBUG_PRINTF("wc_reloc_normalize_text() failed.\n"); + break; + } + ret = hmac_update(hmac_ctx, buf, (word32)progress); + if (ret) { + RELOC_DEBUG_PRINTF("hmac_update() failed.\n"); + break; + } + text_p += progress; + } + + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + WC_SANITIZE_DISABLE(); +#else + (void)reloc_counts; + WC_SANITIZE_DISABLE(); + ret = hmac_update(hmac_ctx, (byte *)(wc_ptr_t)seg_map->fips_text_start, (word32)(seg_map->fips_text_end - seg_map->fips_text_start)); +#endif /* !WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE */ + + if (ret) { + RELOC_DEBUG_PRINTF("ERROR: hmac_update failed: err %d\n", ret); + ret = BAD_STATE_E; + WC_SANITIZE_ENABLE(); + goto out; + } + + /* don't hash verifyCore or changing verifyCore will change hash */ + if (seg_map->verifyCore_start >= seg_map->fips_rodata_start && seg_map->verifyCore_start < seg_map->fips_rodata_end) { + ret = hmac_update(hmac_ctx, (byte*)seg_map->fips_rodata_start, (word32)(seg_map->verifyCore_start - (unsigned long)seg_map->fips_rodata_start)); + if (ret) { + RELOC_DEBUG_PRINTF("ERROR: hmac_update failed: err %d\n", ret); + ret = BAD_STATE_E; + goto out; + } + ret = hmac_update(hmac_ctx, (const byte *)seg_map->verifyCore_end, (word32)(seg_map->fips_rodata_end - (unsigned long)seg_map->verifyCore_end)); + } + else { + ret = hmac_update(hmac_ctx, (byte*)seg_map->fips_rodata_start, (word32)(seg_map->fips_rodata_end - (unsigned long)seg_map->fips_rodata_start)); + } + + WC_SANITIZE_ENABLE(); + + if (ret) { + RELOC_DEBUG_PRINTF("ERROR: hmac_update failed: err %d\n", ret); + ret = BAD_STATE_E; + goto out; + } + + ret = hmac_final(hmac_ctx, hash, digest_size); + if (ret) { + RELOC_DEBUG_PRINTF("ERROR: hmac_final failed: err %d\n", ret); + ret = BAD_STATE_E; + goto out; + } + + ret = Base16_Encode(hash, digest_size, (byte *)out, out_size); + + out: + + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(binCoreKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} + +#endif /* WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE */ + +#endif /* HAVE_FIPS */ diff --git a/linuxkm/linuxkm_memory.h b/linuxkm/linuxkm_memory.h new file mode 100644 index 00000000000..86bb5933ea8 --- /dev/null +++ b/linuxkm/linuxkm_memory.h @@ -0,0 +1,229 @@ +/* linuxkm_memory.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* included by wolfssl/wolfcrypt/memory.h */ + +#ifndef LINUXKM_MEMORY_H +#define LINUXKM_MEMORY_H + +enum wc_reloc_dest_segment { + WC_R_SEG_NONE = 1, + WC_R_SEG_TEXT, + WC_R_SEG_RODATA, + WC_R_SEG_RWDATA, + WC_R_SEG_BSS, + WC_R_SEG_OTHER +}; + +enum wc_reloc_type { + WC_R_NONE = 1, + WC_R_X86_64_32, + WC_R_X86_64_32S, + WC_R_X86_64_64, + WC_R_X86_64_PC32, + WC_R_X86_64_PLT32, + WC_R_AARCH64_ABS32, + WC_R_AARCH64_ABS64, + WC_R_AARCH64_ADD_ABS_LO12_NC, + WC_R_AARCH64_ADR_PREL_PG_HI21, + WC_R_AARCH64_CALL26, + WC_R_AARCH64_JUMP26, + WC_R_AARCH64_LDST8_ABS_LO12_NC, + WC_R_AARCH64_LDST16_ABS_LO12_NC, + WC_R_AARCH64_LDST32_ABS_LO12_NC, + WC_R_AARCH64_LDST64_ABS_LO12_NC, + WC_R_AARCH64_PREL32, + WC_R_ARM_ABS32, + WC_R_ARM_PREL31, + WC_R_ARM_REL32, + WC_R_ARM_THM_CALL, + WC_R_ARM_THM_JUMP11, + WC_R_ARM_THM_JUMP24, + WC_R_ARM_THM_MOVT_ABS, + WC_R_ARM_THM_MOVW_ABS_NC +}; + +/* This structure is accessed natively by kernel module glue logic, and also + * from outside by linux-fips-hash.c -- pack it, with explicit pad bits, to + * remove all doubt about layout. + */ +struct __attribute__((packed)) wc_reloc_table_ent { + unsigned int offset; + unsigned int dest_offset; + signed int dest_addend; +#define WC_RELOC_DEST_SEGMENT_BITS 3 + unsigned int dest_segment:WC_RELOC_DEST_SEGMENT_BITS; +#define WC_RELOC_TYPE_BITS 5 + unsigned int reloc_type:WC_RELOC_TYPE_BITS; + unsigned int _pad_bits:(32 - (WC_RELOC_DEST_SEGMENT_BITS + WC_RELOC_TYPE_BITS)); +}; + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + +struct wc_reloc_table_segments { + unsigned long start; + unsigned long end; + unsigned long reloc_tab_start; + unsigned long reloc_tab_end; + unsigned long reloc_tab_len_start; + unsigned long reloc_tab_len_end; + unsigned long text_start; + unsigned long text_end; +#ifdef HAVE_FIPS + unsigned long fips_text_start; + unsigned long fips_text_end; +#endif /* HAVE_FIPS */ + unsigned long rodata_start; + unsigned long rodata_end; +#ifdef HAVE_FIPS + unsigned long fips_rodata_start; + unsigned long fips_rodata_end; + unsigned long verifyCore_start; + unsigned long verifyCore_end; +#endif /* HAVE_FIPS */ + unsigned long data_start; + unsigned long data_end; + unsigned long bss_start; + unsigned long bss_end; + int text_is_live; +}; + +#ifdef HAVE_FIPS + +#define WC_RELOC_TABLE_SEGMENTS_INITIALIZER { \ + .start = ~0UL, \ + .end = ~0UL, \ + .reloc_tab_start = ~0UL, \ + .reloc_tab_end = ~0UL, \ + .reloc_tab_len_start = ~0UL, \ + .reloc_tab_len_end = ~0UL, \ + .text_start = ~0UL, \ + .text_end = ~0UL, \ + .fips_text_start = ~0UL, \ + .fips_text_end = ~0UL, \ + .rodata_start = ~0UL, \ + .rodata_end = ~0UL, \ + .fips_rodata_start = ~0UL, \ + .fips_rodata_end = ~0UL, \ + .verifyCore_start = ~0UL, \ + .verifyCore_end = ~0UL, \ + .data_start = ~0UL, \ + .data_end = ~0UL, \ + .bss_start = ~0UL, \ + .bss_end = 0, \ + .text_is_live = 0 \ +} + +#else /* !HAVE_FIPS */ + +#define WC_RELOC_TABLE_SEGMENTS_INITIALIZER { \ + .start = ~0UL, \ + .end = ~0UL, \ + .reloc_tab_start = ~0UL, \ + .reloc_tab_end = ~0UL, \ + .reloc_tab_len_start = ~0UL, \ + .reloc_tab_len_end = ~0UL, \ + .text_start = ~0UL, \ + .text_end = ~0UL, \ + .rodata_start = ~0UL, \ + .rodata_end = ~0UL, \ + .data_start = ~0UL, \ + .data_end = ~0UL, \ + .bss_start = ~0UL, \ + .bss_end = 0, \ + .text_is_live = 0 \ +} + +#endif /* !HAVE_FIPS */ + +struct wc_reloc_counts { + int text; + int rodata; + int rwdata; + int bss; + int other; +}; + +#elif defined(HAVE_FIPS) + +struct wc_reloc_table_segments { + unsigned long start; + unsigned long end; + unsigned long fips_text_start; + unsigned long fips_text_end; + unsigned long fips_rodata_start; + unsigned long fips_rodata_end; + unsigned long verifyCore_start; + unsigned long verifyCore_end; +}; + +#define WC_RELOC_TABLE_SEGMENTS_INITIALIZER { \ + .start = ~0UL, \ + .end = ~0UL, \ + .fips_text_start = ~0UL, \ + .fips_text_end = ~0UL, \ + .fips_rodata_start = ~0UL, \ + .fips_rodata_end = ~0UL, \ + .verifyCore_start = ~0UL, \ + .verifyCore_end = ~0UL \ +} + +struct wc_reloc_counts { + int dummy; +}; + +#endif /* !WC_SYM_RELOC_TABLES && HAVE_FIPS */ + +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + +WOLFSSL_API ssize_t wc_reloc_normalize_text( + const byte *text_in, + size_t text_in_len, + byte *text_out, + ssize_t *cur_index_p, + const struct wc_reloc_table_segments *seg_map, + struct wc_reloc_counts *reloc_counts); + +#endif /* WC_SYM_RELOC_TABLES || WC_SYM_RELOC_TABLES_SUPPORT */ + +#ifdef HAVE_FIPS + +#if defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE) || defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE_SUPPORT) + typedef int (*wc_fips_verifyCore_hmac_setkey_fn)(void *ctx, const byte *key, word32 key_len); + typedef int (*wc_fips_verifyCore_hmac_update_fn)(void *ctx, const byte *in, word32 in_len); + typedef int (*wc_fips_verifyCore_hmac_final_fn)(void *ctx, byte *out, word32 out_sz); + + WOLFSSL_API int wc_fips_generate_hash( + const struct wc_reloc_table_segments *seg_map, + word32 digest_size, + const char *hmac_key_base16, + void *hmac_ctx, + wc_fips_verifyCore_hmac_setkey_fn hmac_setkey, + wc_fips_verifyCore_hmac_update_fn hmac_update, + wc_fips_verifyCore_hmac_final_fn hmac_final, + char *out, + word32 *out_size, + struct wc_reloc_counts *reloc_counts); +#endif + +#endif /* HAVE_FIPS */ + +#endif /* LINUXKM_MEMORY_H */ diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 28917ce89d5..9aa743b2cc3 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -24,27 +24,6 @@ #ifndef LINUXKM_WC_PORT_H #define LINUXKM_WC_PORT_H - /* - * CRITICAL: Disable ARM64 LSE atomics for out-of-tree modules. - * - * When CONFIG_ARM64_LSE_ATOMICS is enabled, the kernel uses static keys - * (jump labels) in system_uses_lse_atomics() to choose between LSE and - * LL/SC atomic implementations at runtime. These static keys generate - * asm goto statements that reference .jump_table section symbols which - * cannot be resolved in out-of-tree modules, causing: - * "error: impossible constraint in 'asm'" - * - * By undefining CONFIG_ARM64_LSE_ATOMICS here (before any kernel headers - * that use atomics are included), we force use of the LL/SC fallback path - * which works correctly in out-of-tree modules. - * - * This must appear BEFORE #include because that header - * may transitively include headers that use atomics. - */ - #ifdef CONFIG_ARM64_LSE_ATOMICS - #undef CONFIG_ARM64_LSE_ATOMICS - #endif - #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) @@ -322,6 +301,22 @@ #define queued_spin_lock_slowpath my__queued_spin_lock_slowpath #endif + /* + * Disable ARM64 LSE atomics for out-of-tree modules. + * + * When CONFIG_ARM64_LSE_ATOMICS is enabled, the kernel uses static keys + * (jump labels) in system_uses_lse_atomics() to choose between LSE and + * LL/SC atomic implementations at runtime. These static keys generate + * asm goto statements that reference .jump_table section symbols which + * cannot be resolved in out-of-tree modules, causing: + * "error: impossible constraint in 'asm'" + * + * By undefining CONFIG_ARM64_LSE_ATOMICS here (before any kernel headers + * that use atomics are included), we force use of the LL/SC fallback path + * which works correctly in out-of-tree modules. + */ + #undef CONFIG_ARM64_LSE_ATOMICS + #include #include @@ -511,6 +506,10 @@ #include #include + #if __has_include() + /* for task_stack_page() */ + #include + #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) /* for signal_pending() */ #include @@ -816,12 +815,6 @@ #ifdef WC_SYM_RELOC_TABLES - #ifndef WOLFSSL_TEXT_SEGMENT_CANONICALIZER - #define WOLFSSL_TEXT_SEGMENT_CANONICALIZER(text_in, text_in_len, text_out, cur_index_p) \ - wc_linuxkm_normalize_relocations(text_in, text_in_len, text_out, cur_index_p) - #define WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ 8192 - #endif - extern __attribute__((error("uncallable fencepost"))) int __wc_text_start(void); extern __attribute__((error("uncallable fencepost"))) int __wc_text_end(void); #ifdef HAVE_FIPS @@ -840,59 +833,18 @@ __wc_bss_start[], __wc_bss_end[]; - struct wc_linuxkm_pie_reloc_tab_ent { - unsigned int offset; - #define WC_RELOC_DEST_SEGMENT_BITS 3 - unsigned int dest_segment:WC_RELOC_DEST_SEGMENT_BITS; - #define WC_RELOC_TYPE_BITS 5 - unsigned int reloc_type:WC_RELOC_TYPE_BITS; - }; - - enum wc_reloc_dest_segment { - WC_R_SEG_NONE = 0, - WC_R_SEG_TEXT, - WC_R_SEG_RODATA, - WC_R_SEG_RWDATA, - WC_R_SEG_BSS, - WC_R_SEG_OTHER - }; - - enum wc_reloc_type { - WC_R_NONE = 0, - WC_R_X86_64_32, - WC_R_X86_64_32S, - WC_R_X86_64_64, - WC_R_X86_64_PC32, - WC_R_X86_64_PLT32, - WC_R_AARCH64_ABS32, - WC_R_AARCH64_ABS64, - WC_R_AARCH64_ADD_ABS_LO12_NC, - WC_R_AARCH64_ADR_PREL_PG_HI21, - WC_R_AARCH64_CALL26, - WC_R_AARCH64_JUMP26, - WC_R_AARCH64_LDST8_ABS_LO12_NC, - WC_R_AARCH64_LDST16_ABS_LO12_NC, - WC_R_AARCH64_LDST32_ABS_LO12_NC, - WC_R_AARCH64_LDST64_ABS_LO12_NC, - WC_R_AARCH64_PREL32, - WC_R_ARM_ABS32, - WC_R_ARM_PREL31, - WC_R_ARM_REL32, - WC_R_ARM_THM_CALL, - WC_R_ARM_THM_JUMP11, - WC_R_ARM_THM_JUMP24, - WC_R_ARM_THM_MOVT_ABS, - WC_R_ARM_THM_MOVW_ABS_NC - }; - - extern const struct wc_linuxkm_pie_reloc_tab_ent wc_linuxkm_pie_reloc_tab[]; - extern const unsigned long 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); + #ifndef WOLFSSL_TEXT_SEGMENT_CANONICALIZER + #define WOLFSSL_TEXT_SEGMENT_CANONICALIZER(text_in, text_in_len, text_out, cur_index_p) \ + wc_linuxkm_normalize_relocations(text_in, text_in_len, text_out, cur_index_p) + #define WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ 8192 + #endif + #ifdef CONFIG_MIPS #undef __ARCH_MEMCMP_NO_REDIRECT #undef memcmp @@ -900,7 +852,9 @@ #endif struct wolfssl_linuxkm_pie_redirect_table { + #ifdef HAVE_FIPS typeof(wc_linuxkm_normalize_relocations) *wc_linuxkm_normalize_relocations; + #endif #ifndef __ARCH_MEMCMP_NO_REDIRECT typeof(memcmp) *memcmp; @@ -1184,6 +1138,11 @@ typeof(wc_linuxkm_check_for_intr_signals) *wc_linuxkm_check_for_intr_signals; typeof(wc_linuxkm_relax_long_loop) *wc_linuxkm_relax_long_loop; + #ifdef CONFIG_KASAN + typeof(kasan_disable_current) *kasan_disable_current; + typeof(kasan_enable_current) *kasan_enable_current; + #endif + const void *_last_slot; }; @@ -1226,8 +1185,10 @@ #ifdef WC_CONTAINERIZE_THIS - #define wc_linuxkm_normalize_relocations \ - WC_PIE_INDIRECT_SYM(wc_linuxkm_normalize_relocations) + #ifdef HAVE_FIPS + #define wc_linuxkm_normalize_relocations \ + WC_PIE_INDIRECT_SYM(wc_linuxkm_normalize_relocations) + #endif #ifndef __ARCH_MEMCMP_NO_REDIRECT #define memcmp WC_PIE_INDIRECT_SYM(memcmp) @@ -1469,10 +1430,143 @@ #define wc_linuxkm_check_for_intr_signals WC_PIE_INDIRECT_SYM(wc_linuxkm_check_for_intr_signals) #define wc_linuxkm_relax_long_loop WC_PIE_INDIRECT_SYM(wc_linuxkm_relax_long_loop) + #ifdef CONFIG_KASAN + #define kasan_disable_current WC_PIE_INDIRECT_SYM(kasan_disable_current) + #define kasan_enable_current WC_PIE_INDIRECT_SYM(kasan_enable_current) + #endif + #endif /* WC_CONTAINERIZE_THIS */ #endif /* WC_SYM_RELOC_TABLES */ +#ifdef WC_LINUXKM_STACK_DEBUG + + #ifndef CONFIG_THREAD_INFO_IN_TASK + #error WC_LINUXKM_STACK_DEBUG requires CONFIG_THREAD_INFO_IN_TASK + #endif + #ifdef CONFIG_STACK_GROWSUP + #error WC_LINUXKM_STACK_DEBUG requires !CONFIG_STACK_GROWSUP + #endif + + static __always_inline unsigned long wc_linuxkm_stack_bottom(void) { + void *ret = task_stack_page(get_current()); + return (unsigned long)(uintptr_t)ret; + } + + static __always_inline unsigned long wc_linuxkm_stack_top(void) { + return wc_linuxkm_stack_bottom() + THREAD_SIZE; + } + + #if defined(CONFIG_X86) + + static __always_inline unsigned long wc_linuxkm_stack_current(void) { + unsigned long rsp; + + asm volatile("mov %%rsp, %0" : "=r" (rsp)); + return wc_linuxkm_stack_top() - rsp; + } + + static __always_inline unsigned long wc_linuxkm_stack_left(void) { + unsigned long rsp; + asm volatile("mov %%rsp, %0" : "=r" (rsp)); + return rsp - wc_linuxkm_stack_bottom(); + } + + #define WC_LINUXKM_HAVE_STACK_DEBUG + + #elif defined(CONFIG_ARM64) + + static __always_inline unsigned long wc_linuxkm_stack_current(void) { + unsigned long sp; + asm volatile("mov %0, sp" : "=r" (sp)); + return wc_linuxkm_stack_top() - sp; + } + + static __always_inline unsigned long wc_linuxkm_stack_left(void) { + unsigned long sp; + asm volatile("mov %0, sp" : "=r" (sp)); + return sp - wc_linuxkm_stack_bottom(); + } + + #define WC_LINUXKM_HAVE_STACK_DEBUG + + #elif defined(CONFIG_ARM) + + static __always_inline unsigned long wc_linuxkm_stack_current(void) { + unsigned long sp; + asm volatile("mov %0, sp" : "=r" (sp)); + return wc_linuxkm_stack_top() - sp; + } + + static __always_inline unsigned long wc_linuxkm_stack_left(void) { + unsigned long sp; + asm volatile("mov %0, sp" : "=r" (sp)); + return sp - wc_linuxkm_stack_bottom(); + } + + #define WC_LINUXKM_HAVE_STACK_DEBUG + + #endif /* CONFIG_ARM */ + + #ifndef WC_LINUXKM_HAVE_STACK_DEBUG + #error WC_LINUXKM_STACK_DEBUG implementation missing for target. + #endif + + /* An unsigned long STACK_END_MAGIC is stored at the bottom of the stack. + * Additionally, though the kernel stack doesn't have a red zone, it + * nonetheless uses some bytes below the current stack pointer and mayhem + * ensues immediately if it's overwritten. + */ + #ifndef WC_KERNEL_STACK_MARGIN_BOTTOM + #define WC_KERNEL_STACK_MARGIN_BOTTOM sizeof(unsigned long) + #endif + #ifndef WC_KERNEL_STACK_MARGIN_TOP + #define WC_KERNEL_STACK_MARGIN_TOP 8 + #endif + + static __always_inline void wc_linuxkm_stack_hwm_prepare(unsigned char sentinel) { + unsigned long s = wc_linuxkm_stack_bottom(); + unsigned long z; + unsigned long flags; + + if (*(unsigned long *)s != STACK_END_MAGIC) + pr_err("ERROR: bottom of stack is not STACK_END_MAGIC.\n"); + + local_irq_save(flags); + kasan_disable_current(); + z = wc_linuxkm_stack_left(); + if (z > WC_KERNEL_STACK_MARGIN_BOTTOM + WC_KERNEL_STACK_MARGIN_TOP) + memset((void *)(s + WC_KERNEL_STACK_MARGIN_BOTTOM), sentinel, + z - (WC_KERNEL_STACK_MARGIN_BOTTOM + WC_KERNEL_STACK_MARGIN_TOP)); + kasan_enable_current(); + local_irq_restore(flags); + if (z <= WC_KERNEL_STACK_MARGIN_BOTTOM + WC_KERNEL_STACK_MARGIN_TOP) + pr_err("ERROR: wc_linuxkm_stack_hwm_prepare() called with only %lu bytes of stack left, " + "versus margin %zu.\n", z, WC_KERNEL_STACK_MARGIN_BOTTOM + WC_KERNEL_STACK_MARGIN_TOP); + } + static __always_inline unsigned long wc_linuxkm_stack_hwm_measure_rel(unsigned char sentinel) { + unsigned long s = wc_linuxkm_stack_bottom(); + unsigned long z = wc_linuxkm_stack_left(); + unsigned char *i; + if (z <= WC_KERNEL_STACK_MARGIN_BOTTOM + WC_KERNEL_STACK_MARGIN_TOP) + return (unsigned long)-1; + kasan_disable_current(); + for (i = (unsigned char *)s + WC_KERNEL_STACK_MARGIN_BOTTOM; + i < ((unsigned char *)s + z) && (*i == sentinel); + ++i); + kasan_enable_current(); + return z - ((unsigned long)i - s); + } + static __always_inline unsigned long wc_linuxkm_stack_hwm_measure_total(unsigned char sentinel) { + unsigned long rel = wc_linuxkm_stack_hwm_measure_rel(sentinel); + if (rel == (unsigned long)-1) + return rel; + else + return rel + wc_linuxkm_stack_current(); + } + +#endif /* WC_LINUXKM_STACK_DEBUG */ + /* remove this multifariously conflicting macro, picked up from * Linux arch//include/asm/current.h. */ diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index f0442067bb1..f278857b0a1 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -36,6 +36,9 @@ #ifdef USE_CONTESTMUTEX #error USE_CONTESTMUTEX is incompatible with WOLFSSL_LINUXKM #endif + #ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE + #include + #endif #include #endif #if !defined(NO_CRYPT_TEST) || defined(LINUXKM_LKCAPI_REGISTER) @@ -88,8 +91,30 @@ static int libwolfssl_cleanup(void) { return ret; } -#if defined(HAVE_FIPS) && (FIPS_VERSION3_GE(6,0,0) || defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE)) -extern char verifyCore[WC_SHA256_DIGEST_SIZE*2 + 1]; +#ifdef HAVE_FIPS + /* failsafe definitions for FIPS <5.3 */ + #ifndef FIPS_IN_CORE_DIGEST_SIZE + #ifndef NO_SHA256 + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA256_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA256 + #elif defined(WOLFSSL_SHA384) + #define FIPS_IN_CORE_DIGEST_SIZE WC_SHA384_DIGEST_SIZE + #define FIPS_IN_CORE_HASH_TYPE WC_SHA384 + #else + #error Unsupported FIPS hash alg. + #endif + #endif + + #ifndef FIPS_IN_CORE_KEY_SZ + #define FIPS_IN_CORE_KEY_SZ FIPS_IN_CORE_DIGEST_SIZE + #endif + #ifndef FIPS_IN_CORE_VERIFY_SZ + #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_DIGEST_SIZE + #endif + + #if FIPS_VERSION3_GE(6,0,0) || defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE) + extern char verifyCore[FIPS_IN_CORE_DIGEST_SIZE*2 + 1]; + #endif #endif #ifdef WC_SYM_RELOC_TABLES @@ -111,8 +136,9 @@ static unsigned int hash_span(const u8 *start, const u8 *end, unsigned int sum) return sum; } -static int total_text_r = 0, total_rodata_r = 0, total_rwdata_r = 0, - total_bss_r = 0, total_other_r = 0; +#ifdef WC_SYM_RELOC_TABLES +struct wc_reloc_counts reloc_counts = {}; +#endif #endif /* DEBUG_LINUXKM_PIE_SUPPORT */ @@ -445,6 +471,9 @@ int wc_linuxkm_GenerateSeed_IntelRD(struct OS_Seed* os, byte* output, word32 sz) extern int linuxkm_op_test_1(int argc, const char* argv[]); extern int linuxkm_op_test_wrapper(void); static wolfSSL_Atomic_Int *conTestFailure_ptr = NULL; + #ifdef HAVE_WC_FIPS_OPTEST_CONTESTFAILURE_EXPORT + WOLFSSL_API extern wolfSSL_Atomic_Int wc_fips_optest_conTestFailure; + #endif static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static struct kobj_attribute FIPS_optest_trig_attr = __ATTR(FIPS_optest_run_code, 0220, NULL, FIPS_optest_trig_handler); @@ -459,6 +488,21 @@ static int wolfssl_init(void) { int ret; + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at entry to wolfssl_init(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + } + #endif + +#ifdef WC_SYM_RELOC_TABLES + ret = set_up_wolfssl_linuxkm_pie_redirect_table(); + if (ret < 0) + return ret; +#endif + #ifdef WC_LINUXKM_TEST_INET_PTON { const char *src; @@ -497,15 +541,26 @@ static int wolfssl_init(void) #if defined(CONFIG_HAVE_KPROBES) && (defined(DEBUG_LINUXKM_PIE_SUPPORT) || defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG)) pr_err("INFO: couldn't get verifyCore_ptr -- skipping verifyCore length check.\n"); #endif - verifyCore_len = WC_SHA256_DIGEST_SIZE*2; + verifyCore_len = FIPS_IN_CORE_DIGEST_SIZE*2; } #endif - if (verifyCore_len != WC_SHA256_DIGEST_SIZE*2) { - pr_err("ERROR: compile-time FIPS hash is the wrong length (expected %d hex digits, got %zu).\n", WC_SHA256_DIGEST_SIZE*2, verifyCore_len); + if (verifyCore_len != FIPS_IN_CORE_DIGEST_SIZE*2) { + pr_err("ERROR: compile-time FIPS hash is the wrong length (expected %d hex digits, got %zu).\n", FIPS_IN_CORE_DIGEST_SIZE*2, verifyCore_len); return -ECANCELED; } } +#ifdef WC_SYM_RELOC_TABLES + if (((uintptr_t)__wc_text_start > (uintptr_t)wolfCrypt_FIPS_first) || + ((uintptr_t)__wc_text_end < (uintptr_t)wolfCrypt_FIPS_last) || + ((uintptr_t)__wc_rodata_start > (uintptr_t)wolfCrypt_FIPS_ro_start) || + ((uintptr_t)__wc_rodata_end < (uintptr_t)wolfCrypt_FIPS_ro_end)) + { + pr_err("ERROR: ELF segment fenceposts and FIPS fenceposts conflict.\n"); + return -ECANCELED; + } +#endif + #ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE #ifdef CONFIG_MODULE_SIG if (THIS_MODULE->sig_ok == false) { @@ -513,31 +568,24 @@ static int wolfssl_init(void) return -ECANCELED; } #endif + #if defined(WC_SYM_RELOC_TABLES) && defined(DEBUG_LINUXKM_PIE_SUPPORT) + reloc_counts.text = reloc_counts.rodata = reloc_counts.rwdata = reloc_counts.bss = + reloc_counts.other = 0; + #endif ret = updateFipsHash(); if (ret < 0) { pr_err("ERROR: wolfSSL module load aborted -- updateFipsHash: %s\n",wc_GetErrorString(ret)); return -ECANCELED; } -#endif /* WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE */ -#endif /* HAVE_FIPS */ + #if defined(WC_SYM_RELOC_TABLES) && defined(DEBUG_LINUXKM_PIE_SUPPORT) + pr_info("FIPS-bounded relocation normalizations from updateFipsHash(): text=%d, rodata=%d, rwdata=%d, bss=%d, other=%d\n", + reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, reloc_counts.bss, reloc_counts.other); + #endif -#ifdef WC_SYM_RELOC_TABLES - ret = set_up_wolfssl_linuxkm_pie_redirect_table(); - if (ret < 0) - return ret; -#endif +#endif /* WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE */ -#if defined(HAVE_FIPS) && defined(WC_SYM_RELOC_TABLES) - if (((uintptr_t)__wc_text_start > (uintptr_t)wolfCrypt_FIPS_first) || - ((uintptr_t)__wc_text_end < (uintptr_t)wolfCrypt_FIPS_last) || - ((uintptr_t)__wc_rodata_start > (uintptr_t)wolfCrypt_FIPS_ro_start) || - ((uintptr_t)__wc_rodata_end < (uintptr_t)wolfCrypt_FIPS_ro_end)) - { - pr_err("ERROR: ELF segment fenceposts and FIPS fenceposts conflict.\n"); - return -ECANCELED; - } -#endif +#endif /* HAVE_FIPS */ #if defined(WC_SYM_RELOC_TABLES) && defined(DEBUG_LINUXKM_PIE_SUPPORT) @@ -572,8 +620,8 @@ static int wolfssl_init(void) return -ECANCELED; } - total_text_r = total_rodata_r = total_rwdata_r = total_bss_r = - total_other_r = 0; + reloc_counts.text = reloc_counts.rodata = reloc_counts.rwdata = reloc_counts.bss = + reloc_counts.other = 0; while (text_p < (const u8 *)__wc_text_end) { ssize_t progress = @@ -621,11 +669,21 @@ static int wolfssl_init(void) (unsigned long long)(uintptr_t)__wc_bss_end); pr_info("whole-segment relocation normalizations: text=%d, rodata=%d, rwdata=%d, bss=%d, other=%d\n", - total_text_r, total_rodata_r, total_rwdata_r, total_bss_r, total_other_r); + reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, reloc_counts.bss, reloc_counts.other); } #endif /* WC_SYM_RELOC_TABLES && DEBUG_LINUXKM_PIE_SUPPORT */ +#ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by wolfssl_init() initial setup: %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } +#endif + #ifdef HAVE_FIPS ret = wolfCrypt_SetCb_fips(lkmFipsCb); if (ret != 0) { @@ -634,8 +692,8 @@ static int wolfssl_init(void) } #if defined(WC_SYM_RELOC_TABLES) && defined(DEBUG_LINUXKM_PIE_SUPPORT) - total_text_r = total_rodata_r = total_rwdata_r = total_bss_r = - total_other_r = 0; + reloc_counts.text = reloc_counts.rodata = reloc_counts.rwdata = reloc_counts.bss = + reloc_counts.other = 0; #endif if (WC_SIG_IGNORE_BEGIN() >= 0) { @@ -647,7 +705,7 @@ static int wolfssl_init(void) #if defined(WC_SYM_RELOC_TABLES) && defined(DEBUG_LINUXKM_PIE_SUPPORT) pr_info("FIPS-bounded relocation normalizations: text=%d, rodata=%d, rwdata=%d, bss=%d, other=%d\n", - total_text_r, total_rodata_r, total_rwdata_r, total_bss_r, total_other_r); + reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, reloc_counts.bss, reloc_counts.other); #endif ret = wolfCrypt_GetStatus_fips(); @@ -729,11 +787,15 @@ static int wolfssl_init(void) #endif /* HAVE_FIPS && FIPS_VERSION3_GT(5,2,0) */ #ifdef FIPS_OPTEST + #ifdef HAVE_WC_FIPS_OPTEST_CONTESTFAILURE_EXPORT + conTestFailure_ptr = &wc_fips_optest_conTestFailure; + #else conTestFailure_ptr = (wolfSSL_Atomic_Int *)my_kallsyms_lookup_name("conTestFailure"); if (conTestFailure_ptr == NULL) { pr_err("ERROR: couldn't obtain conTestFailure_ptr.\n"); return -ECANCELED; } + #endif ret = linuxkm_lkcapi_sysfs_install_node(&FIPS_optest_trig_attr, &installed_sysfs_FIPS_optest_trig_files); if (ret != 0) { @@ -742,7 +804,25 @@ static int wolfssl_init(void) } #ifdef FIPS_OPTEST_FULL_RUN_AT_MODULE_INIT + + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at call to linuxkm_op_test_wrapper(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + #endif + (void)linuxkm_op_test_wrapper(); + + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by linuxkm_op_test_wrapper(): %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } + #endif + WOLFSSL_ATOMIC_STORE(*conTestFailure_ptr, 0); for (i = 0; i < FIPS_CAST_COUNT; ++i) fipsCastStatus_put(i, FIPS_CAST_STATE_INIT); @@ -765,7 +845,21 @@ static int wolfssl_init(void) #endif /* FIPS_OPTEST */ #ifndef NO_CRYPT_TEST - ret = wolfcrypt_test(NULL); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at call to wolfcrypt_test(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + #endif + ret = wolfcrypt_test(NULL); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by wolfcrypt_test(): %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } + #endif if (ret < 0) { pr_err("ERROR: wolfcrypt self-test failed with return code %d.\n", ret); (void)libwolfssl_cleanup(); @@ -791,7 +885,21 @@ static int wolfssl_init(void) return -ECANCELED; } #else /* !LINUXKM_LKCAPI_REGISTER_ONLY_ON_COMMAND */ - ret = linuxkm_lkcapi_register(); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at call to linuxkm_lkcapi_register(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + #endif + ret = linuxkm_lkcapi_register(); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by linuxkm_lkcapi_register(): %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } + #endif if (ret) { pr_err("ERROR: linuxkm_lkcapi_register() failed with return code %d.\n", ret); @@ -808,7 +916,21 @@ static int wolfssl_init(void) #endif #ifdef WOLFSSL_LINUXKM_BENCHMARKS - wolfcrypt_benchmark_main(0, (char**)NULL); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at call to wolfcrypt_benchmark_main(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + #endif + wolfcrypt_benchmark_main(0, (char**)NULL); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by wolfcrypt_benchmark_main(): %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } + #endif #endif #ifdef WOLFCRYPT_ONLY @@ -892,96 +1014,47 @@ MODULE_VERSION(LIBWOLFSSL_VERSION_STRING); #ifdef WC_SYM_RELOC_TABLES -static const struct reloc_layout_ent { - const char *name; - word64 mask; - word64 width; - word64 is_signed:1; - word64 is_relative:1; - word64 is_pages:1; - word64 is_pair_lo:1; - word64 is_pair_hi:1; -} reloc_layouts[] = { - [WC_R_X86_64_32] = { "R_X86_64_32", ~0UL, 32, .is_signed = 0, .is_relative = 0 }, - [WC_R_X86_64_32S] = { "R_X86_64_32S", ~0UL, 32, .is_signed = 1, .is_relative = 0 }, - [WC_R_X86_64_64] = { "R_X86_64_64", ~0UL, 64, .is_signed = 0, .is_relative = 0 }, - [WC_R_X86_64_PC32] = { "R_X86_64_PC32", ~0UL, 32, .is_signed = 1, .is_relative = 1 }, - [WC_R_X86_64_PLT32] = { "R_X86_64_PLT32", ~0UL, 32, .is_signed = 1, .is_relative = 1 }, - [WC_R_AARCH64_ABS32] = { "R_AARCH64_ABS32", ~0UL, 32, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_AARCH64_ABS64] = { "R_AARCH64_ABS64", ~0UL, 64, .is_signed = 1, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_AARCH64_ADD_ABS_LO12_NC] = { "R_AARCH64_ADD_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, - [WC_R_AARCH64_ADR_PREL_PG_HI21] = { "R_AARCH64_ADR_PREL_PG_HI21", 0b01100000111111111111111111100000, 32, .is_signed = 1, .is_relative = 1, .is_pages = 1, .is_pair_lo = 0, .is_pair_hi = 1 }, - [WC_R_AARCH64_CALL26] = { "R_AARCH64_CALL26", 0b00000011111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_AARCH64_JUMP26] = { "R_AARCH64_JUMP26", 0b00000011111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_AARCH64_LDST8_ABS_LO12_NC] = { "R_AARCH64_LDST8_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, - [WC_R_AARCH64_LDST16_ABS_LO12_NC] = { "R_AARCH64_LDST16_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, - [WC_R_AARCH64_LDST32_ABS_LO12_NC] = { "R_AARCH64_LDST32_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, - [WC_R_AARCH64_LDST64_ABS_LO12_NC] = { "R_AARCH64_LDST64_ABS_LO12_NC", 0b00000000001111111111110000000000, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 }, - [WC_R_AARCH64_PREL32] = { "R_AARCH64_PREL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_ABS32] = { "R_ARM_ABS32", ~0UL, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_PREL31] = { "R_ARM_PREL31", 0b01111111111111111111111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_REL32] = { "R_ARM_REL32", ~0UL, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_THM_CALL] = { "R_ARM_THM_CALL", 0b00000111111111110010111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_THM_JUMP24] = { "R_ARM_THM_JUMP24", 0b00000111111111110010111111111111, 32, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_THM_JUMP11] = { "R_ARM_THM_JUMP11", 0b00000000000000000000011111111111, 16, .is_signed = 1, .is_relative = 1, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 0 }, - [WC_R_ARM_THM_MOVT_ABS] = { "R_ARM_THM_MOVT_ABS", 0b00000100000011110111000011111111, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 0, .is_pair_hi = 1 }, - [WC_R_ARM_THM_MOVW_ABS_NC] = { "R_ARM_THM_MOVW_ABS_NC", 0b00000100000011110111000011111111, 32, .is_signed = 0, .is_relative = 0, .is_pages = 0, .is_pair_lo = 1, .is_pair_hi = 0 } - }; - -static inline long find_reloc_tab_offset(size_t text_in_offset) { - long ret; - unsigned long hop; - if (wc_linuxkm_pie_reloc_tab_length <= 1) { -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_err("ERROR: %s failed at L %d.\n", __FUNCTION__, __LINE__); -#endif - return -1; - } - if (text_in_offset >= (size_t)((uintptr_t)__wc_text_end - (uintptr_t)__wc_text_start)) { -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_err("ERROR: %s failed at L %d.\n", __FUNCTION__, __LINE__); -#endif - return -1; - } - if (text_in_offset >= (size_t)wc_linuxkm_pie_reloc_tab[wc_linuxkm_pie_reloc_tab_length - 1].offset) { -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_err("ERROR: %s failed at L %d.\n", __FUNCTION__, __LINE__); -#endif - return -1; - } - for (ret = 0, - hop = wc_linuxkm_pie_reloc_tab_length >> 1; - hop; - hop >>= 1) - { - if (text_in_offset == (size_t)wc_linuxkm_pie_reloc_tab[ret].offset) - break; - else if (text_in_offset > (size_t)wc_linuxkm_pie_reloc_tab[ret].offset) - ret += hop; - else if (ret) - ret -= hop; - } - - while ((ret < (long)wc_linuxkm_pie_reloc_tab_length - 1) && - ((size_t)wc_linuxkm_pie_reloc_tab[ret].offset < text_in_offset)) - ++ret; - - while ((ret > 0) && - ((size_t)wc_linuxkm_pie_reloc_tab[ret - 1].offset >= text_in_offset)) - --ret; - -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - if (ret < 0) - pr_err("ERROR: %s returning %ld at L %d.\n", __FUNCTION__, ret, __LINE__); +extern const struct wc_reloc_table_ent wc_linuxkm_pie_reloc_tab[]; +extern const unsigned int wc_linuxkm_pie_reloc_tab_length; + +static const struct wc_reloc_table_segments seg_map = { + .start = 0, .end = 0, + .text_start = (size_t)(uintptr_t)__wc_text_start, + .text_end = (size_t)(uintptr_t)__wc_text_end, + .reloc_tab_start = (size_t)(uintptr_t)wc_linuxkm_pie_reloc_tab, + .reloc_tab_end = 0, + .reloc_tab_len_start = (size_t)(uintptr_t)&wc_linuxkm_pie_reloc_tab_length, + .reloc_tab_len_end = 0, +#ifdef HAVE_FIPS +#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS + .fips_text_start = (size_t)(uintptr_t)__wc_text_start, + .fips_text_end = (size_t)(uintptr_t)__wc_text_end, +#else + .fips_text_start = (size_t)(uintptr_t)wolfCrypt_FIPS_first, + .fips_text_end = (size_t)(uintptr_t)wolfCrypt_FIPS_last, #endif - return ret; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -#include +#endif /* HAVE_FIPS */ + .rodata_start = (size_t)(uintptr_t)__wc_rodata_start, + .rodata_end = (size_t)(uintptr_t)__wc_rodata_end, +#ifdef HAVE_FIPS +#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS + .fips_rodata_start = (size_t)(uintptr_t)__wc_rodata_start, + .fips_rodata_end = (size_t)(uintptr_t)__wc_rodata_end, #else -#include + .fips_rodata_start = (size_t)(uintptr_t)wolfCrypt_FIPS_ro_start, + .fips_rodata_end = (size_t)(uintptr_t)wolfCrypt_FIPS_ro_end, #endif + #if FIPS_VERSION3_GE(6,0,0) || defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE) + .verifyCore_start = (uintptr_t)verifyCore, + .verifyCore_end = (uintptr_t)verifyCore + FIPS_IN_CORE_DIGEST_SIZE*2 + 1, + #endif +#endif /* HAVE_FIPS */ + .data_start = (size_t)(uintptr_t)__wc_rwdata_start, + .data_end = (size_t)(uintptr_t)__wc_rwdata_end, + .bss_start = (size_t)(uintptr_t)__wc_bss_start, + .bss_end = (size_t)(uintptr_t)__wc_bss_end, + .text_is_live = 1 +}; ssize_t wc_linuxkm_normalize_relocations( const u8 *text_in, @@ -989,390 +1062,42 @@ ssize_t wc_linuxkm_normalize_relocations( u8 *text_out, ssize_t *cur_index_p) { - ssize_t i; - size_t text_in_offset; - const struct wc_linuxkm_pie_reloc_tab_ent *last_reloc; /* for error-checking order in wc_linuxkm_pie_reloc_tab[] */ -#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, n_oob_r = 0; -#endif - - if ((text_in_len == 0) || - ((uintptr_t)text_in < (uintptr_t)__wc_text_start) || - ((uintptr_t)(text_in + text_in_len) > (uintptr_t)__wc_text_end)) - { -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_err("ERROR: %s returning -1 at L %d with span %llx-%llx versus segment %llx-%llx.\n", - __FUNCTION__, __LINE__, - (unsigned long long)(uintptr_t)text_in, - (unsigned long long)(uintptr_t)(text_in + text_in_len), - (unsigned long long)(uintptr_t)__wc_text_start, - (unsigned long long)(uintptr_t)__wc_text_end); -#endif - return -1; - } - - text_in_offset = (uintptr_t)text_in - (uintptr_t)__wc_text_start; - - if (cur_index_p) - i = *cur_index_p; - else - i = -1; - - 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 (last_reloc = &wc_linuxkm_pie_reloc_tab[i > 0 ? i-1 : 0]; - (size_t)i < wc_linuxkm_pie_reloc_tab_length - 1; - ++i) - { - const struct wc_linuxkm_pie_reloc_tab_ent *next_reloc = &wc_linuxkm_pie_reloc_tab[i]; - enum wc_reloc_dest_segment dest_seg; - uintptr_t seg_beg; + return wc_reloc_normalize_text(text_in, text_in_len, text_out, cur_index_p, &seg_map, #ifdef DEBUG_LINUXKM_PIE_SUPPORT - uintptr_t seg_end; - const char *seg_name; -#endif - word64 reloc_buf = 0; - const struct reloc_layout_ent *layout; - unsigned int next_reloc_rel; - - if (next_reloc->dest_segment == WC_R_SEG_NONE) { - pr_err("BUG: missing dest segment for relocation at wc_linuxkm_pie_reloc_tab[%zd]\n", i); - continue; - } - - if (last_reloc->offset > next_reloc->offset) { - pr_err("BUG: out-of-order offset found at wc_linuxkm_pie_reloc_tab[%zd]: %u > %u\n", - i, last_reloc->offset, next_reloc->offset); - return -1; - } - - last_reloc = next_reloc; - - if (next_reloc->reloc_type >= (sizeof reloc_layouts / sizeof reloc_layouts[0])) { - pr_err("BUG: unknown relocation type %u found at wc_linuxkm_pie_reloc_tab[%zd]\n", - next_reloc->reloc_type, i); - return -1; - } - - layout = &reloc_layouts[next_reloc->reloc_type]; - - switch (layout->width) { - case 32: - case 64: - case 16: - break; - default: - pr_err("BUG: unexpected relocation width %llu found at wc_linuxkm_pie_reloc_tab[%lld], reloc type %u\n", - (unsigned long long)layout->width, (long long)i, next_reloc->reloc_type); - return -1; - } - - /* provisionally assign the destination segment from the reloc record -- - * it may wind up getting overridden later if things don't go as - * expected. - */ - dest_seg = next_reloc->dest_segment; - - /* next_reloc_rel is the offset of the relocation relative to the start - * of the current text chunk (text_in). i.e., text_in + next_reloc_rel - * is the start of the relocation. - */ - next_reloc_rel = next_reloc->offset - text_in_offset; - - if (next_reloc_rel >= text_in_len) { - /* no more relocations in this buffer. */ - break; - } - - if (next_reloc_rel > text_in_len - layout->width) { - /* relocation straddles buffer at end -- caller will try again with - * that relocation at the start. - */ - text_in_len = next_reloc_rel; - break; - } - - /* set reloc_buf to the address bits from the live text segment. on - * ARM, this will often also pull in some opcode bits, which we mask out. - */ - if (layout->is_signed) { - /* Note, the intermediate cast to sword64 is necessary to - * sign-extend the value to 64 bits before unsigned - * reinterpretation. Normalization later relies on this. - */ - switch (layout->width) { - case 32: - reloc_buf = (word64)(sword64)(get_unaligned((sword32 *)&text_out[next_reloc_rel]) & (sword32)layout->mask); - break; - case 64: - reloc_buf = (word64)(sword64)(get_unaligned((sword64 *)&text_out[next_reloc_rel]) & (sword64)layout->mask); - break; - case 16: - reloc_buf = (word64)(sword64)(get_unaligned((sword16 *)&text_out[next_reloc_rel]) & (sword16)layout->mask); - break; - } - } - else { - switch (layout->width) { - case 32: - reloc_buf = (word64)(get_unaligned((word32 *)&text_out[next_reloc_rel]) & (word32)layout->mask); - break; - case 64: - reloc_buf = (word64)(get_unaligned((word64 *)&text_out[next_reloc_rel]) & layout->mask); - break; - case 16: - reloc_buf = (word64)(get_unaligned((word16 *)&text_out[next_reloc_rel]) & (word16)layout->mask); - break; - } - } - - switch (dest_seg) { - case WC_R_SEG_TEXT: - seg_beg = (uintptr_t)__wc_text_start; - #ifdef DEBUG_LINUXKM_PIE_SUPPORT - seg_end = (uintptr_t)__wc_text_end; - seg_name = "text"; - ++n_text_r; - #endif - break; - case WC_R_SEG_RODATA: - seg_beg = (uintptr_t)__wc_rodata_start; - #ifdef DEBUG_LINUXKM_PIE_SUPPORT - seg_end = (uintptr_t)__wc_rodata_end; - seg_name = "rodata"; - ++n_rodata_r; - #endif - break; - case WC_R_SEG_RWDATA: - seg_beg = (uintptr_t)__wc_rwdata_start; - #ifdef DEBUG_LINUXKM_PIE_SUPPORT - seg_end = (uintptr_t)__wc_rwdata_end; - seg_name = "data"; - ++n_rwdata_r; - #endif - break; - case WC_R_SEG_BSS: - seg_beg = (uintptr_t)__wc_bss_start; - #ifdef DEBUG_LINUXKM_PIE_SUPPORT - seg_end = (uintptr_t)__wc_bss_end; - seg_name = "bss"; - ++n_bss_r; - #endif - break; - default: - case WC_R_SEG_NONE: - dest_seg = WC_R_SEG_OTHER; - FALL_THROUGH; - case WC_R_SEG_OTHER: - seg_beg = 0; - #ifdef DEBUG_LINUXKM_PIE_SUPPORT - seg_end = 0; - seg_name = "other"; - #endif - break; - } - -#ifdef CONFIG_X86 - { - switch (next_reloc->reloc_type) { - case WC_R_X86_64_PC32: - case WC_R_X86_64_PLT32: - case WC_R_X86_64_32: - case WC_R_X86_64_32S: - case WC_R_X86_64_64: - break; - default: -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_notice("BUG: unexpected non-x86 relocation type %u in reloc record %zu, text offset 0x%x\n", - (unsigned)next_reloc->reloc_type, i, wc_linuxkm_pie_reloc_tab[i].offset); - ++n_oob_r; -#endif - dest_seg = WC_R_SEG_OTHER; - } - - if (dest_seg != WC_R_SEG_OTHER) { -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - word64 raw_dest_addr = reloc_buf; -#endif - - if (layout->is_relative) { - /* the +(layout->width / 8) (i.e. 4) accounts for the disp32 - * field size, as RIP points to the next instruction byte - * per the x86_64 ABI. - */ - reloc_buf = reloc_buf + (uintptr_t)next_reloc->offset + ((uintptr_t)layout->width / 8UL) - (seg_beg - (uintptr_t)__wc_text_start); - } - else - reloc_buf -= seg_beg; - -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - /* For the various data segments, recognize dest addrs a few bytes - * outside the segment -- the compiler occasionally generates these, - * e.g. __wc_rwdata_start - 1 in DoInCoreCheck() in kernel 6.1 build - * of FIPS v5, __wc_bss_start - 4 in kernel 4.4, and __wc_rodata_end - * + 26 in kernel 6.18. - */ - #ifndef LINUXKM_PIE_DATA_SLOP_MARGIN - #define LINUXKM_PIE_DATA_SLOP_MARGIN 0x20 - #endif - - if ((dest_seg == WC_R_SEG_TEXT) ? - (reloc_buf > seg_end - seg_beg) : - (reloc_buf + LINUXKM_PIE_DATA_SLOP_MARGIN > (seg_end - seg_beg) + (LINUXKM_PIE_DATA_SLOP_MARGIN * 2))) - { - ++n_oob_r; - pr_notice("WARNING: normalized value is out of bounds (%s0x%lx) at index %ld, text offset 0x%x, reloc type %s, " - "dest seg .%s_wolfcrypt, offset from text to dest segment %s0x%lx, raw dest addr %s0x%lx, " - "seg span 0x%lx - 0x%lx, seg size 0x%lx, text base 0x%lx\n", - (sword64)reloc_buf < 0 ? "-" : "", - (sword64)reloc_buf < 0 ? -reloc_buf : reloc_buf, - i, - next_reloc->offset, - layout->name, - seg_name, - seg_beg < (uintptr_t)__wc_text_start ? "-" : "+", - seg_beg < (uintptr_t)__wc_text_start ? (word64)(uintptr_t)__wc_text_start - seg_beg : seg_beg - (word64)(uintptr_t)__wc_text_start, - (layout->is_signed && ((sword64)raw_dest_addr < 0)) ? "-" : "", - (layout->is_signed && ((sword64)raw_dest_addr < 0)) ? (word64)-(sword64)raw_dest_addr : raw_dest_addr, - (word64)seg_beg, - (word64)seg_end, - (word64)(seg_end - seg_beg), - (word64)(uintptr_t)__wc_text_start); - } -#endif - } - } /* end CONFIG_X86 */ - -#elif defined(CONFIG_ARM) - { - switch (next_reloc->reloc_type) { - case WC_R_ARM_ABS32: - case WC_R_ARM_PREL31: - case WC_R_ARM_REL32: - case WC_R_ARM_THM_CALL: - case WC_R_ARM_THM_JUMP11: - case WC_R_ARM_THM_JUMP24: - case WC_R_ARM_THM_MOVT_ABS: - case WC_R_ARM_THM_MOVW_ABS_NC: - break; - default: -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_notice("BUG: unexpected non-ARM32 relocation type %u in reloc record %u, text offset 0x%x\n", - (unsigned)next_reloc->reloc_type, i, wc_linuxkm_pie_reloc_tab[i].offset); - ++n_oob_r; -#endif - dest_seg = WC_R_SEG_OTHER; - } - - /* Don't attempt to reconstruct ARM destination addresses -- just - * normalize to zero. They can be reconstructed using the - * parameters in reloc_layouts[] but it's very fidgety. - */ - reloc_buf = 0; - } /* end CONFIG_ARM */ - -#elif defined(CONFIG_ARM64) - { - switch (next_reloc->reloc_type) { - case WC_R_AARCH64_ABS32: - case WC_R_AARCH64_ABS64: - case WC_R_AARCH64_ADD_ABS_LO12_NC: - case WC_R_AARCH64_ADR_PREL_PG_HI21: - case WC_R_AARCH64_CALL26: - case WC_R_AARCH64_JUMP26: - case WC_R_AARCH64_LDST16_ABS_LO12_NC: - case WC_R_AARCH64_LDST32_ABS_LO12_NC: - case WC_R_AARCH64_LDST64_ABS_LO12_NC: - case WC_R_AARCH64_LDST8_ABS_LO12_NC: - case WC_R_AARCH64_PREL32: - break; - default: -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - pr_notice("BUG: unexpected non-ARM64 relocation type %u in reloc record %ld, text offset 0x%x\n", - (unsigned)next_reloc->reloc_type, i, wc_linuxkm_pie_reloc_tab[i].offset); - ++n_oob_r; -#endif - dest_seg = WC_R_SEG_OTHER; - } - - /* Don't attempt to reconstruct ARM destination addresses -- just - * normalize to zero. They can be reconstructed using the - * parameters in reloc_layouts[] but it's very fidgety. - */ - reloc_buf = 0; - } /* end CONFIG_ARM64 */ - + &reloc_counts #else - - #warning Building WC_SYM_RELOC_TABLES without arch-specific handling. - - dest_seg = WC_R_SEG_OTHER; - reloc_buf = 0; - + NULL #endif + ); +} - if (dest_seg == WC_R_SEG_OTHER) { - /* relocation referring to non-wolfcrypt segment -- these can only - * be stabilized by zeroing them. - */ - reloc_buf = 0; +#elif defined(HAVE_FIPS) -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - ++n_other_r; - /* we're currently only handling 32 bit relocations (R_X86_64_PLT32 - * and R_X86_64_PC32) so the top half of the word64 is padding we - * can lop off for rendering. - */ - pr_notice("found non-wolfcrypt relocation at index %lld, text offset 0x%x.\n", - (long long)i, wc_linuxkm_pie_reloc_tab[i].offset); +static const struct wc_reloc_table_segments seg_map = { + .start = 0, .end = 0, +#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS + .fips_text_start = (size_t)(uintptr_t)__wc_text_start, + .fips_text_end = (size_t)(uintptr_t)__wc_text_end, +#else + .fips_text_start = (size_t)(uintptr_t)wolfCrypt_FIPS_first, + .fips_text_end = (size_t)(uintptr_t)wolfCrypt_FIPS_last, #endif - } - - /* xor in a label identifying the dest segment and reloc type. */ - reloc_buf ^= dest_seg << (layout->width - WC_RELOC_DEST_SEGMENT_BITS); - reloc_buf ^= next_reloc->reloc_type << (layout->width - (WC_RELOC_DEST_SEGMENT_BITS + WC_RELOC_TYPE_BITS)); - - /* write the modified reloc_buf to the destination buffer. */ - switch (layout->width) { - case 32: - put_unaligned((u32)reloc_buf, (u32 *)&text_out[next_reloc_rel]); - break; - case 64: - put_unaligned(reloc_buf, (u64 *)&text_out[next_reloc_rel]); - break; - case 16: - put_unaligned((u16)reloc_buf, (u16 *)&text_out[next_reloc_rel]); - break; - } - } - -#ifdef DEBUG_LINUXKM_PIE_SUPPORT - total_text_r += n_text_r; - total_rodata_r += n_rodata_r; - total_rwdata_r += n_rwdata_r; - total_bss_r += n_bss_r; - total_other_r += n_other_r; - - if ((n_other_r > 0) || (n_oob_r > 0)) - pr_notice("text_in=%llx relocs=%d/%d/%d/%d/%d/%d ret = %llu\n", - (unsigned long long)(uintptr_t)text_in, n_text_r, n_rodata_r, - n_rwdata_r, n_bss_r, n_other_r, n_oob_r, - (unsigned long long)text_in_len); +#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS + .fips_rodata_start = (size_t)(uintptr_t)__wc_rodata_start, + .fips_rodata_end = (size_t)(uintptr_t)__wc_rodata_end, +#else + .fips_rodata_start = (size_t)(uintptr_t)wolfCrypt_FIPS_ro_start, + .fips_rodata_end = (size_t)(uintptr_t)wolfCrypt_FIPS_ro_end, #endif + #if FIPS_VERSION3_GE(6,0,0) || defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE) + .verifyCore_start = (uintptr_t)verifyCore, + .verifyCore_end = (uintptr_t)verifyCore + FIPS_IN_CORE_DIGEST_SIZE*2 + 1 + #endif +}; - if (cur_index_p) - *cur_index_p = i; +#endif /* !WC_SYM_RELOC_TABLES && HAVE_FIPS */ - return (ssize_t)text_in_len; -} +#ifdef WC_SYM_RELOC_TABLES /* get_current() is an inline or macro, depending on the target -- sidestep the * whole issue with a wrapper func. @@ -1395,8 +1120,10 @@ static int set_up_wolfssl_linuxkm_pie_redirect_table(void) { 0, sizeof wolfssl_linuxkm_pie_redirect_table); +#ifdef HAVE_FIPS wolfssl_linuxkm_pie_redirect_table.wc_linuxkm_normalize_relocations = wc_linuxkm_normalize_relocations; +#endif #ifndef __ARCH_MEMCMP_NO_REDIRECT wolfssl_linuxkm_pie_redirect_table.memcmp = memcmp; @@ -1730,6 +1457,11 @@ static int set_up_wolfssl_linuxkm_pie_redirect_table(void) { wolfssl_linuxkm_pie_redirect_table.wc_linuxkm_check_for_intr_signals = wc_linuxkm_check_for_intr_signals; wolfssl_linuxkm_pie_redirect_table.wc_linuxkm_relax_long_loop = wc_linuxkm_relax_long_loop; +#ifdef CONFIG_KASAN + wolfssl_linuxkm_pie_redirect_table.kasan_disable_current = kasan_disable_current; + wolfssl_linuxkm_pie_redirect_table.kasan_enable_current = kasan_enable_current; +#endif + /* runtime assert that the table has no null slots after initialization. */ { unsigned long *i; @@ -1762,93 +1494,110 @@ PRAGMA_GCC("GCC diagnostic ignored \"-Wunused-parameter\"") #include PRAGMA_GCC_DIAG_POP -extern const char coreKey[WC_SHA256_DIGEST_SIZE*2 + 1]; +/* failsafe definitions for FIPS <5.3 */ +#ifndef FIPS_IN_CORE_DIGEST_SIZE + #ifndef NO_SHA256 + #define FIPS_IN_CORE_DIGEST_SIZE 32 + #define FIPS_IN_CORE_HASH_TYPE WC_SHA256 + #define FIPS_IN_CORE_KEY_SZ 32 + #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_KEY_SZ + #elif defined(WOLFSSL_SHA384) + #define FIPS_IN_CORE_DIGEST_SIZE 48 + #define FIPS_IN_CORE_HASH_TYPE WC_SHA384 + #define FIPS_IN_CORE_KEY_SZ 48 + #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_KEY_SZ + #else + #error Unsupported FIPS hash alg. + #endif +#endif + +extern const char coreKey[FIPS_IN_CORE_KEY_SZ*2 + 1]; extern const unsigned int wolfCrypt_FIPS_ro_start[]; extern const unsigned int wolfCrypt_FIPS_ro_end[]; -#define FIPS_IN_CORE_KEY_SZ 32 -#define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_KEY_SZ -typedef int (*fips_address_function)(void); -#define MAX_FIPS_DATA_SZ 10000000 -#define MAX_FIPS_CODE_SZ 10000000 -extern int GenBase16_Hash(const byte* in, int length, char* out, int outSz); +static int linux_fips_hmac_setkey(struct shash_desc *desc, const byte *key, word32 key_len) { + int ret = crypto_shash_setkey(desc->tfm, key, key_len); + if (ret) { + pr_err("ERROR: crypto_ahash_setkey failed: err %d\n", ret); + return BAD_STATE_E; + } -static int updateFipsHash(void) -{ - struct crypto_shash *tfm = NULL; - struct shash_desc *desc = NULL; - word32 verifySz = FIPS_IN_CORE_VERIFY_SZ; - word32 binCoreSz = FIPS_IN_CORE_KEY_SZ; + ret = crypto_shash_init(desc); + if (ret) { + pr_err("ERROR: crypto_shash_init failed: err %d\n", ret); + return BAD_STATE_E; + } + return 0; +} +static int linux_fips_hmac_update(struct shash_desc *desc, const byte *in, word32 in_len) { + int ret = crypto_shash_update(desc, in, in_len); + if (ret) { + pr_err("ERROR: crypto_shash_update failed: err %d\n", ret); + return BAD_STATE_E; + } + else + return 0; +} +static int linux_fips_hmac_final(struct shash_desc *desc, byte *out, word32 out_sz) { int ret; - byte *hash = NULL; - char *base16_hash = NULL; - byte *binCoreKey = NULL; - byte *binVerify = NULL; -#ifdef WC_USE_PIE_FENCEPOSTS_FOR_FIPS - fips_address_function first = __wc_text_start; - fips_address_function last = __wc_text_end; - char* start = (char*)__wc_rodata_start; - char* end = (char*)__wc_rodata_end; -#else - fips_address_function first = wolfCrypt_FIPS_first; - fips_address_function last = wolfCrypt_FIPS_last; - char* start = (char*)wolfCrypt_FIPS_ro_start; - char* end = (char*)wolfCrypt_FIPS_ro_end; -#endif - - unsigned long code_sz = (unsigned long)last - (unsigned long)first; - unsigned long data_sz = (unsigned long)end - (unsigned long)start; - - if (data_sz == 0 || data_sz > MAX_FIPS_DATA_SZ) - return BAD_FUNC_ARG; /* bad fips data size */ + (void)out_sz; - if (code_sz == 0 || code_sz > MAX_FIPS_CODE_SZ) - return BAD_FUNC_ARG; /* bad fips code size */ - - hash = XMALLOC(WC_SHA256_DIGEST_SIZE, 0, DYNAMIC_TYPE_TMP_BUFFER); - if (hash == NULL) { - ret = MEMORY_E; - goto out; - } - base16_hash = XMALLOC(WC_SHA256_DIGEST_SIZE*2 + 1, 0, DYNAMIC_TYPE_TMP_BUFFER); - if (base16_hash == NULL) { - ret = MEMORY_E; - goto out; - } - binCoreKey = XMALLOC(binCoreSz, 0, DYNAMIC_TYPE_TMP_BUFFER); - if (binCoreKey == NULL) { - ret = MEMORY_E; - goto out; - } - binVerify = XMALLOC(verifySz, 0, DYNAMIC_TYPE_TMP_BUFFER); - if (binVerify == NULL) { - ret = MEMORY_E; - goto out; + ret = crypto_shash_final(desc, out); + if (ret) { + pr_err("ERROR: crypto_shash_final failed: err %d\n", ret); + return BAD_STATE_E; } + else + return 0; +} - { - word32 base16_out_len = binCoreSz; - ret = Base16_Decode((const byte *)coreKey, sizeof coreKey - 1, binCoreKey, &base16_out_len); - if (ret != 0) { - pr_err("ERROR: Base16_Decode for coreKey: %s\n", wc_GetErrorString(ret)); - goto out; - } - if (base16_out_len != binCoreSz) { - pr_err("ERROR: unexpected output length %u for coreKey from Base16_Decode.\n",base16_out_len); - ret = BAD_STATE_E; - goto out; - } +static int updateFipsHash(void) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *desc = NULL; + int ret; + word32 verifyCore_size = XSTRLEN(verifyCore) + 1; + char *old_verifyCore = XMALLOC(verifyCore_size, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + if (old_verifyCore == NULL) + return MEMORY_E; + XMEMCPY(old_verifyCore, verifyCore, verifyCore_size); + + wc_static_assert(((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA256) || + ((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA384) || + ((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA512) || + ((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA3_256) || + ((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA3_384) || + ((unsigned)FIPS_IN_CORE_HASH_TYPE == (unsigned)WC_SHA3_512)); + + switch ((unsigned)FIPS_IN_CORE_HASH_TYPE) { + case (unsigned)WC_SHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case (unsigned)WC_SHA384: + tfm = crypto_alloc_shash("hmac(sha384)", 0, 0); + break; + case (unsigned)WC_SHA512: + tfm = crypto_alloc_shash("hmac(sha512)", 0, 0); + break; + case (unsigned)WC_SHA3_256: + tfm = crypto_alloc_shash("hmac(sha3-256)", 0, 0); + break; + case (unsigned)WC_SHA3_384: + tfm = crypto_alloc_shash("hmac(sha3-384)", 0, 0); + break; + case (unsigned)WC_SHA3_512: + tfm = crypto_alloc_shash("hmac(sha3-512)", 0, 0); + break; } - tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOMEM) { pr_err("ERROR: crypto_alloc_shash failed: out of memory\n"); ret = MEMORY_E; } else if (PTR_ERR(tfm) == -ENOENT) { - pr_err("ERROR: crypto_alloc_shash failed: kernel is missing hmac(sha256) implementation\n"); - pr_err("ERROR: check for CONFIG_CRYPTO_SHA256 and CONFIG_CRYPTO_HMAC.\n"); + pr_err("ERROR: crypto_alloc_shash failed: target kernel is missing algorithm implementation for hash type %u\n", FIPS_IN_CORE_HASH_TYPE); ret = NOT_COMPILED_IN; } else { pr_err("ERROR: crypto_alloc_shash failed with ret %ld\n",PTR_ERR(tfm)); @@ -1869,124 +1618,38 @@ static int updateFipsHash(void) XMEMSET(desc, 0, desc_size); } - ret = crypto_shash_setkey(tfm, binCoreKey, binCoreSz); - if (ret) { - pr_err("ERROR: crypto_ahash_setkey failed: err %d\n", ret); - ret = BAD_STATE_E; - goto out; - } - desc->tfm = tfm; - ret = crypto_shash_init(desc); - if (ret) { - pr_err("ERROR: crypto_shash_init failed: err %d\n", ret); - ret = BAD_STATE_E; - goto out; - } - -#if defined(WOLFSSL_LINUXKM) && defined(WC_SYM_RELOC_TABLES) - { - 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(); + ret = wc_fips_generate_hash( + &seg_map, + FIPS_IN_CORE_DIGEST_SIZE, + coreKey, + desc, + (wc_fips_verifyCore_hmac_setkey_fn)linux_fips_hmac_setkey, + (wc_fips_verifyCore_hmac_update_fn)linux_fips_hmac_update, + (wc_fips_verifyCore_hmac_final_fn)linux_fips_hmac_final, + verifyCore, + &verifyCore_size, +#if defined(DEBUG_LINUXKM_PIE_SUPPORT) && defined(WC_SYM_RELOC_TABLES) + &reloc_counts #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; - } - - /* don't hash verifyCore or changing verifyCore will change hash */ - if (verifyCore >= start && verifyCore < end) { - data_sz = (unsigned long)verifyCore - (unsigned long)start; - ret = crypto_shash_update(desc, (byte*)start, (word32)data_sz); - if (ret) { - pr_err("ERROR: crypto_shash_update failed: err %d\n", ret); - ret = BAD_STATE_E; - goto out; - } - start = (char*)verifyCore + sizeof(verifyCore); - data_sz = (unsigned long)end - (unsigned long)start; - } - ret = crypto_shash_update(desc, (byte*)start, (word32)data_sz); - if (ret) { - pr_err("ERROR: crypto_shash_update failed: err %d\n", ret); - ret = BAD_STATE_E; - WC_SANITIZE_ENABLE(); - goto out; - } - - WC_SANITIZE_ENABLE(); - - ret = crypto_shash_final(desc, hash); - if (ret) { - pr_err("ERROR: crypto_shash_final failed: err %d\n", ret); - ret = BAD_STATE_E; - goto out; - } + NULL +#endif + ); - ret = GenBase16_Hash(hash, WC_SHA256_DIGEST_SIZE, base16_hash, WC_SHA256_DIGEST_SIZE*2 + 1); - if (ret != 0) { - pr_err("ERROR: GenBase16_Hash failed: %s\n", wc_GetErrorString(ret)); + if (ret < 0) goto out; - } - { - word32 base16_out_len = verifySz; - ret = Base16_Decode((const byte *)verifyCore, sizeof verifyCore - 1, binVerify, &base16_out_len); - if (ret != 0) { - pr_err("ERROR: Base16_Decode for verifyCore: %s\n", wc_GetErrorString(ret)); - goto out; - } - if (base16_out_len != binCoreSz) { - pr_err("ERROR: unexpected output length %u for verifyCore from Base16_Decode.\n",base16_out_len); - ret = BAD_STATE_E; - goto out; - } - } - - if (XMEMCMP(hash, binVerify, WC_SHA256_DIGEST_SIZE) == 0) { + if (strcmp(old_verifyCore, verifyCore) == 0) { #if defined(DEBUG_LINUXKM_PIE_SUPPORT) || defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG) pr_info("updateFipsHash: verifyCore already matches [%s]\n", verifyCore); #else pr_info("updateFipsHash: verifyCore already matches.\n"); #endif - } else { - XMEMCPY(verifyCore, base16_hash, WC_SHA256_DIGEST_SIZE*2 + 1); + } + else { #if defined(DEBUG_LINUXKM_PIE_SUPPORT) || defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG) - pr_info("updateFipsHash: verifyCore updated [%s].\n", base16_hash); + pr_info("updateFipsHash: verifyCore updated [%s].\n", verifyCore); #else pr_info("updateFipsHash: verifyCore updated.\n"); #endif @@ -1999,10 +1662,7 @@ static int updateFipsHash(void) if (tfm != NULL) crypto_free_shash(tfm); XFREE(desc, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(base16_hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(binCoreKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(binVerify, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(old_verifyCore, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -2135,8 +1795,24 @@ static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attrib printf("OK, testing code %s\n", code_buf); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + { + unsigned long stack_usage; + stack_usage = wc_linuxkm_stack_current(); + pr_info("STACK INFO: usage at call to linuxkm_op_test_1(): %lu of %lu total\n", stack_usage, THREAD_SIZE); + wc_linuxkm_stack_hwm_prepare(0xee); + #endif + ret = linuxkm_op_test_1(argc, &argv[0]); + #ifdef WC_LINUXKM_HAVE_STACK_DEBUG + stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee); + pr_info("STACK INFO: rel usage by linuxkm_op_test_1(): %lu\n", stack_usage); + /* shush up false stack HWM reading by kernel: */ + wc_linuxkm_stack_hwm_prepare(0); + } + #endif + printf("ret of op_test = %d\n", ret); /* reload the library in memory and re-init state */ diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 2fa561ae389..fea45db55e8 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -2285,7 +2285,7 @@ static const char* bench_result_words2[][6] = { #warning Large/Unalligned AuthSz could result in errors with /dev/crypto #endif -/* use kB instead of mB for embedded benchmarking */ +/* use kB instead of MB for embedded benchmarking */ #ifdef BENCH_EMBEDDED #ifndef BENCH_NTIMES #define BENCH_NTIMES 2 @@ -9089,6 +9089,10 @@ void bench_cmac(int useDeviceID) #ifdef HAVE_SCRYPT +#ifdef WOLFSSL_KERNEL_MODE + #error wc_scrypt benchmarking with cost 14 is not kernel-compatible (requires 16 MB contiguous allocation) +#endif + void bench_scrypt(void) { byte derived[64]; diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index baf9d203e89..316c8506700 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -1827,6 +1827,7 @@ WOLFSSL_LOCAL int SAVE_VECTOR_REGISTERS2_fuzzer(void) { #endif /* DEBUG_VECTOR_REGISTER_ACCESS_FUZZING */ -#ifdef WOLFSSL_LINUXKM - #include "../../linuxkm/linuxkm_memory.c" +#if defined(WOLFSSL_LINUXKM) || defined(WC_SYM_RELOC_TABLES) || \ + defined(WC_SYM_RELOC_TABLES_SUPPORT) + #include "linuxkm/linuxkm_memory.c" #endif diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 1abe00059b1..268a233f0af 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -538,6 +538,11 @@ WOLFSSL_LOCAL int wc_debug_CipherLifecycleFree(void **CipherLifecycleTag, #endif #endif +#if defined(WOLFSSL_LINUXKM) || defined(WC_SYM_RELOC_TABLES) || \ + defined(WC_SYM_RELOC_TABLES_SUPPORT) + #include "linuxkm/linuxkm_memory.h" +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 0020a05a339..f5186c802e3 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3956,6 +3956,11 @@ extern void uITRON4_free(void *p) ; #if !defined(WC_NO_VERBOSE_RNG) && !defined(WC_VERBOSE_RNG) #define WC_VERBOSE_RNG #endif + + #if WOLFSSL_GENERAL_ALIGNMENT < SIZEOF_LONG + #undef WOLFSSL_GENERAL_ALIGNMENT + #define WOLFSSL_GENERAL_ALIGNMENT SIZEOF_LONG + #endif #endif /* WOLFSSL_KERNEL_MODE */ #if defined(WC_SYM_RELOC_TABLES) && defined(HAVE_FIPS) && \ @@ -4271,6 +4276,10 @@ extern void uITRON4_free(void *p) ; #define WOLFSSL_BASE64_DECODE #endif +#if defined(WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE) && !defined(WOLFSSL_BASE16) + #define WOLFSSL_BASE16 +#endif + #if defined(FORTRESS) && !defined(HAVE_EX_DATA) #define HAVE_EX_DATA #endif From db7a04a626f5cf12fa47f864bbc588cca31b71b0 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 19 Feb 2026 17:42:01 -0600 Subject: [PATCH 2/7] improvements spurred by peer review for 20260204-linuxkm-fips-hash: configure.ac: add --enable-kernel-verbose-debug and --enable-kernel-stack-debug; linuxkm/Makefile: * add QFLAG and VFLAG setup, and pass their values appropriately; * add missing `@set -e` and `-Wall -Wextra` to the linuxkm-fips-hash recipe; * use +$(MAKE), not @$(MAKE), for proper dry run recursion. linuxkm/README.md: update to reflect new goodies, and generally revise+extend remarks. linuxkm/linuxkm-fips-hash-wrapper.sh: add copyright header; pass through extra caller arguments to ./linuxkm-fips-hash. linuxkm/linuxkm-fips-hash.c: * add copyright header; * fix code around user_coreKey; * add explicit wolfCrypt_Cleanup() and cleanup of mod_fd and mod_map at end; * remove unused reloc_tab_len * fix a couple -Wsign-compares; * add missing fprintf arguments * properly set ret = -1 in a couple failure paths. linuxkm/linuxkm_wc_port.h: set WOLFSSL_LINUXKM_VERBOSE_DEBUG when WOLFSSL_KERNEL_VERBOSE_DEBUG, and recognize WOLFSSL_KERNEL_STACK_DEBUG as a synonym for WC_LINUXKM_STACK_DEBUG. linuxkm/linuxkm_memory.c and linuxkm/linuxkm_memory.h: add brief explanatory comments. --- configure.ac | 22 +++++- linuxkm/Makefile | 23 ++++-- linuxkm/README.md | 110 ++++++++++++++++++++------- linuxkm/linuxkm-fips-hash-wrapper.sh | 24 +++++- linuxkm/linuxkm-fips-hash.c | 59 ++++++++++++-- linuxkm/linuxkm_memory.c | 19 +++++ linuxkm/linuxkm_memory.h | 6 ++ linuxkm/linuxkm_wc_port.h | 6 +- linuxkm/module_hooks.c | 2 +- 9 files changed, 224 insertions(+), 47 deletions(-) diff --git a/configure.ac b/configure.ac index 76eac743feb..9ce19133978 100644 --- a/configure.ac +++ b/configure.ac @@ -131,16 +131,34 @@ then fi # Kernel module benchmark -ENABLED_KERNEL_BENCHMARKS="" AC_ARG_ENABLE([kernel-benchmarks], [AS_HELP_STRING([--enable-kernel-benchmarks],[Enable crypto benchmarking autorun at module load time for kernel module (default: disabled)])], - [ENABLED_KERNEL_BENCHMARKS=$enableval]) + [ENABLED_KERNEL_BENCHMARKS=$enableval], + [ENABLED_KERNEL_BENCHMARKS="no"]) if test "$ENABLED_KERNEL_BENCHMARKS" = "yes" then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KERNEL_BENCHMARKS" fi AC_SUBST([ENABLED_KERNEL_BENCHMARKS]) +AC_ARG_ENABLE([kernel-verbose-debug], + [AS_HELP_STRING([--enable-kernel-verbose-debug],[Enable supplementary runtime debugging messages for kernel module (default: disabled)])], + [ENABLED_KERNEL_VERBOSE_DEBUG=$enableval], + [ENABLED_KERNEL_VERBOSE_DEBUG="no"]) +if test "$ENABLED_KERNEL_VERBOSE_DEBUG" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KERNEL_VERBOSE_DEBUG" +fi + +AC_ARG_ENABLE([kernel-stack-debug], + [AS_HELP_STRING([--enable-kernel-stack-debug],[Enable runtime reporting of stack usage in kernel module (default: disabled)])], + [ENABLED_KERNEL_STACK_DEBUG=$enableval], + [ENABLED_KERNEL_STACK_DEBUG="no"]) +if test "$ENABLED_KERNEL_STACK_DEBUG" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KERNEL_STACK_DEBUG" +fi + # Linux Kernel Module options (more options later) AC_ARG_ENABLE([linuxkm], [AS_HELP_STRING([--enable-linuxkm],[Enable Linux Kernel Module (default: disabled)])], diff --git a/linuxkm/Makefile b/linuxkm/Makefile index e5524599a5f..93586956a27 100644 --- a/linuxkm/Makefile +++ b/linuxkm/Makefile @@ -21,6 +21,14 @@ .ONESHELL: SHELL=bash +ifeq "$(quiet)" "silent_" + QFLAG := --quiet +else ifeq "$(V)" "1" + VFLAG := --verbose +else + QFLAG := --quiet +endif + ifndef LIBWOLFSSL_NAME LIBWOLFSSL_NAME := libwolfssl endif @@ -355,34 +363,35 @@ libwolfssl-user-build/src/.libs/libwolfssl.so: @echo > user_settings_asm.h @echo -n 'Configuring libwolfssl.so...' @unset WOLFSSL_CFLAGS WOLFCRYPT_PIE_FILES ASFLAGS_FPUSIMD_ENABLE ASFLAGS_FPU_DISABLE_SIMD_ENABLE src_libwolfssl_la_OBJECTS WOLFSSL_ASFLAGS AM_CFLAGS WOLFSSL_OBJ_FILES ENABLED_LINUXKM_LKCAPI_REGISTER EXTRA_LDFLAGS CC LD - @./configure --quiet --disable-jobserver --enable-cryptonly --enable-fips="$$FIPS_FLAVOR" CFLAGS='-DWC_SYM_RELOC_TABLES_SUPPORT -DWOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE -DWOLFSSL_USER_SETTINGS -DWOLFSSL_USER_SETTINGS_ASM' + @./configure $(QFLAG) $(VFLAG) --disable-jobserver --enable-cryptonly --enable-fips="$$FIPS_FLAVOR" CFLAGS='-DWC_SYM_RELOC_TABLES_SUPPORT -DWOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE -DWOLFSSL_USER_SETTINGS -DWOLFSSL_USER_SETTINGS_ASM' @echo ' done.' @echo -n 'Compiling and linking libwolfssl.so...' - @$(MAKE) >/dev/null + +$(MAKE) $(QFLAG) >/dev/null @echo ' done.' @echo -n 'Fixing FIPS hash...' @userhash=$$(wolfcrypt/test/testwolfcrypt 2>&1 | sed -n -E 's/^hash = (.+)$$/\1/p') @if [[ -z "$$userhash" ]]; then echo ' FIPS hash not found!' >&2; exit 1; fi @find wolfcrypt/src -name '*fips_test*o' -delete - @$(MAKE) EXTRA_CFLAGS=-DWOLFCRYPT_FIPS_CORE_HASH_VALUE="$$userhash" + +$(MAKE) $(QFLAG) EXTRA_CFLAGS=-DWOLFCRYPT_FIPS_CORE_HASH_VALUE="$$userhash" @echo ' done.' linuxkm-fips-hash: libwolfssl-user-build/src/.libs/libwolfssl.so linuxkm-fips-hash.c + @set -e @echo -n 'Compiling linuxkm-fips-hash...' # note direct invocation of cc -- we are compiling for the build host, not the target host. - @cc -I'$(MODULE_TOP)/libwolfssl-user-build' -o linuxkm-fips-hash linuxkm/linuxkm-fips-hash.c -L '$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath-link='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -lwolfssl + @cc -Wall -Wextra -O2 -I'$(MODULE_TOP)/libwolfssl-user-build' -o linuxkm-fips-hash linuxkm/linuxkm-fips-hash.c -L '$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath-link='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -Wl,-rpath='$(MODULE_TOP)/libwolfssl-user-build/src/.libs' -lwolfssl @echo ' done.' .PHONY: module-with-matching-fips-hash module-with-matching-fips-hash: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash @set -e - ./linuxkm-fips-hash-wrapper.sh "$<" - $(MAKE) -C . '$(LIBWOLFSSL_NAME).ko.signed' + @./linuxkm-fips-hash-wrapper.sh "$<" $(QFLAG) $(VFLAG) + +$(MAKE) $(QFLAG) -C . '$(LIBWOLFSSL_NAME).ko.signed' .PHONY: module-with-matching-fips-hash-no-sign module-with-matching-fips-hash-no-sign: $(LIBWOLFSSL_NAME).ko linuxkm-fips-hash @set -e - ./linuxkm-fips-hash-wrapper.sh "$<" + @./linuxkm-fips-hash-wrapper.sh "$<" $(LIBWOLFSSL_NAME).ko.signed: $(LIBWOLFSSL_NAME).ko ifdef FORCE_NO_MODULE_SIG diff --git a/linuxkm/README.md b/linuxkm/README.md index 35739a89e1c..b4e69abd59f 100644 --- a/linuxkm/README.md +++ b/linuxkm/README.md @@ -12,6 +12,7 @@ when wolfCrypt-FIPS is used, this provides a simple recipe for FIPS-compliant kernels. Supported features: + - crypto acceleration: AES-NI, AVX, etc. - kernel crypto API registration (wolfCrypt algs appear as drivers in `/proc/crypto`.). - `CONFIG_CRYPTO_FIPS`, and crypto-manager self-tests. @@ -22,16 +23,24 @@ Supported features: ## Building and Installing -Build linuxkm with: +Build `libwolfssl.ko` with: ```sh $ ./configure --enable-linuxkm --with-linux-source=/usr/src/linux $ make -j module ``` -note: replace `/usr/src/linux` with a path to your fully configured and built +Note: Replace `/usr/src/linux` with a path to your fully configured and built target kernel source tree. +If building from a FIPS kernel module bundle, build `libwolfssl.ko` with: +```sh +$ ./configure --enable-fips=fips_flavor --enable-linuxkm --with-linux-source=/usr/src/linux +$ make -j module-with-matching-fips-hash +``` + +Note: Replace `fips_flavor` with the correct value. + Assuming you are targeting your native system, install with: ```sh @@ -39,54 +48,99 @@ $ sudo make install $ sudo modprobe libwolfssl ``` -### options +### Key additional Linux kernel module configuration options + +| option | description | +| :------------------------------- | :----------------------------------------- | +| `--enable-linuxkm-lkcapi-register` | Register wolfcrypt algs with linux kernel crypto API.
Optional value is 'all', 'all-kconfig', 'none', or a comma separated list of algs. | +| `--enable-all-crypto` | Enable extra crypto algorithms | +| `--enable-intelasm` | x86/amd64 crypto acceleration | +| `--enable-cryptonly` | Omit TLS/DTLS implementation (normally recommended) | + +### Additional configuration options for verification, performance evaluation, and troubleshooting + +| option | description | +| :------------------------------- | :----------------------------------------- | +| `--enable-crypttests` | Run `wolfcrypt_test()` at module load (not recommended for production) | +| `--enable-kernel-benchmarks` | Run crypto benchmark at module load (_not appropriate for production_) | +| `--enable-kernel-verbose-debug` | Extra runtime diagnostic and informational messages | +| `--enable-kernel-stack-debug` | Report stack usage during module startup | +| `--enable-debug-trace-errcodes` | Profuse debug logging (_not appropriate for production_) | +| `--enable-debug-trace-errcodes=backtrace` | Even more profuse debug logging (_not appropriate for production_) | -| linuxkm option | description | -| :------------------------------- | :--------------------------------------- | -| --enable-linuxkm-lkcapi-register | Register wolfcrypt algs with linux kernel
crypto API. Options are 'all', 'none', or
comma separated list of algs. | -| --enable-linuxkm-pie | Enable relocatable object build of module| -| --enable-linuxkm-benchmarks | Run crypto benchmark at module load | ## Kernel Patches -The dir `linuxkm/patches` contains a patch to the linux kernel CRNG. The +The `linuxkm/patches` directory in the source distribution contains a patch to the linux kernel CRNG. The CRNG provides the implementation for `/dev/random`, `/dev/urandom`, and -`getrandom()`. +`getrandom()`, and for internal RNG APIs such as `get_random_bytes()`, +`get_random_u32()`, etc. + +The patch applies to these two sources: -The patch updates these two sources - `drivers/char/random.c` - `include/linux/random.h` +It adds a callback facility to the core kernel code that allows `libwolfssl.ko` +to register FIPS-compliant algorithms in place of the native implementation +(which is based on non-FIPS ChaCha20 and blake2s algorithms). When `libwolfssl.ko` is configured with +`--enable-linuxkm-lkcapi-register` and loaded into a patched kernel, it +automatically registers the FIPS callbacks. At startup, the module will report -to use FIPS-compliant algorithms, instead of chacha and blake2s. +``` +libwolfssl: kernel global random_bytes handlers installed. +``` -Patches are provided for several kernel versions, ranging from `5.10.x` to -`6.15`. +Additionally, `/proc/crypto` will advertise that the FIPS DRBG is installed at +highest priority "-with-global-replace": +```ini +name : stdrng +driver : sha2-256-drbg-nopr-wolfcrypt-fips-140-3-with-global-replace +module : libwolfssl +priority : 100000 +refcnt : 2 +selftest : passed +internal : no +fips : yes +type : rng +seedsize : 0 +``` -### patch procedure -1. Ensure kernel src tree is clean before patching: +Patches are provided for several kernel versions, ranging from `5.10.x` to +`6.15`, with the most recent patchset tested nightly with the latest Linux +release and RC kernels, and with the latest linux-next snapshot. Use the +patchset with the most recent target kernel version not greater than that of the +kernel you're targeting. -```sh -cd ~/kernelsrc/ -make mrproper -``` +### Patch procedure -2. Verify patches will apply clean with a dry run check: +1. Verify that the patcheset applies cleanly, using a dry run: -```sh -patch -p1 --dry-run <~/wolfssl-5.8.2/linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch +```console +$ cd ~/kernelsrc/ +$ patch -p1 --dry-run < ~/wolfssl-5.8.2/linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch checking file drivers/char/random.c checking file include/linux/random.h ``` -3. Finally patch the kernel: +2. Optionally, clean the kernel src tree before patching: -```sh -patch -p1 <~/wolfssl-5.8.2/linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch +```console +$ make mrproper +``` + +3. Patch the kernel: + +```console +$ patch -p1 < ~/wolfssl-5.8.2/linuxkm/patches/6.12/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-6v12.patch patching file drivers/char/random.c patching file include/linux/random.h ``` -4. Build kernel. - +4. Build and optionally install the patched kernel: +```console +$ make -j +# make modules_install +# make install +``` diff --git a/linuxkm/linuxkm-fips-hash-wrapper.sh b/linuxkm/linuxkm-fips-hash-wrapper.sh index 49af5994b94..7244ff5e069 100755 --- a/linuxkm/linuxkm-fips-hash-wrapper.sh +++ b/linuxkm/linuxkm-fips-hash-wrapper.sh @@ -1,8 +1,30 @@ #!/bin/bash +# linuxkm-fips-hash-wrapper.sh -- Wrapper for linuxkm-fips-hash -- looks up the +# fencepost values using readelf, and assembles the argument list from them. +# +# Copyright (C) 2006-2026 wolfSSL Inc. +# +# This file is part of wolfSSL. +# +# wolfSSL is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# wolfSSL is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + set -o noclobber -o nounset -o pipefail -o errexit mod_path=$1 +shift readarray -t fenceposts < <(readelf --wide --sections --symbols "$mod_path" | awk ' BEGIN { @@ -64,4 +86,4 @@ BEGIN { } }') -./linuxkm-fips-hash "${fenceposts[@]}" --mod-path "$mod_path" --in-place --quiet +./linuxkm-fips-hash "${fenceposts[@]}" --mod-path "$mod_path" --in-place "$@" diff --git a/linuxkm/linuxkm-fips-hash.c b/linuxkm/linuxkm-fips-hash.c index fa09a97ba7c..64c19f2eeeb 100644 --- a/linuxkm/linuxkm-fips-hash.c +++ b/linuxkm/linuxkm-fips-hash.c @@ -1,3 +1,29 @@ +/* linuxkm-fips-hash.c + * + * A utility to compute the correct FIPS integrity hash for a fully linked + * libwolfssl.ko kernel module, and to optionally update the module in place. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* See linuxkm-fips-hash-wrapper.sh for argument setup example. */ + #include #include #include @@ -31,7 +57,6 @@ #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_DIGEST_SIZE #endif -static const char *user_coreKey; #ifdef WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE extern const char coreKey[FIPS_IN_CORE_KEY_SZ*2 + 1]; #endif @@ -67,10 +92,10 @@ int main(int argc, char **argv) char new_verifyCore[new_verifyCore_size]; const char *progname = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; const char *mod_path = NULL; + const char *user_coreKey = NULL; int mod_fd; struct stat st; - byte *mod_map = NULL; - word32 reloc_tab_len; + byte *mod_map = MAP_FAILED; struct wc_reloc_counts reloc_counts; int inplace = 0; int quiet = 0; @@ -248,11 +273,11 @@ int main(int argc, char **argv) } if ((seg_map.reloc_tab_start >= seg_map.reloc_tab_end) || - (seg_map.reloc_tab_end >= st.st_size) || + (seg_map.reloc_tab_end >= (unsigned long)st.st_size) || (seg_map.reloc_tab_len_start >= seg_map.reloc_tab_len_end) || - (seg_map.reloc_tab_len_end >= st.st_size)) + (seg_map.reloc_tab_len_end >= (unsigned long)st.st_size)) { - fprintf(stderr, "%s: supplied reloc_tab fencepost(s) are out of bounds.\n", progname, mod_path, st.st_size); + fprintf(stderr, "%s: supplied reloc_tab fencepost(s) are out of bounds for supplied module %s with length %lu.\n", progname, mod_path, (unsigned long)st.st_size); exit(1); } @@ -294,6 +319,7 @@ int main(int argc, char **argv) if (seg_map.verifyCore_end - seg_map.verifyCore_start != new_verifyCore_size) { fprintf(stderr, "%s: unexpected verifyCore length %zu.\n", progname, (size_t)(seg_map.verifyCore_end - seg_map.verifyCore_start)); + ret = -1; goto out; } @@ -302,7 +328,7 @@ int main(int argc, char **argv) ret = wc_fips_generate_hash( &seg_map, FIPS_IN_CORE_DIGEST_SIZE, - coreKey, + user_coreKey, &hmac, (wc_fips_verifyCore_hmac_setkey_fn)hmac_setkey_cb, (wc_fips_verifyCore_hmac_update_fn)hmac_update_cb, @@ -322,6 +348,7 @@ int main(int argc, char **argv) if (new_verifyCore_size < sizeof new_verifyCore) { fprintf(stderr, "%s: wc_fips_generate_hash() returned unexpected verifyCore length %u.\n", progname, new_verifyCore_size); + ret = -1; goto out; } @@ -338,17 +365,35 @@ int main(int argc, char **argv) fprintf(stderr, "%s: munmap: %m\n", progname); exit(1); } + mod_map = MAP_FAILED; ret = close(mod_fd); if (ret < 0) { fprintf(stderr, "%s: close: %m\n", progname); exit(1); } + mod_fd = -1; printf("FIPS integrity hash updated successfully.\n"); } out: wc_HmacFree(&hmac); + wolfCrypt_Cleanup(); + + if (mod_map != MAP_FAILED) { + if (munmap(mod_map, st.st_size) < 0) { + fprintf(stderr, "%s: munmap: %m\n", progname); + if (ret == 0) + ret = -1; + } + } + if (mod_fd >= 0) { + if (close(mod_fd) < 0) { + fprintf(stderr, "%s: close: %m\n", progname); + if (ret == 0) + ret = -1; + } + } if (ret) exit(1); diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c index 3aeeaa99c4e..2e16f19ccad 100644 --- a/linuxkm/linuxkm_memory.c +++ b/linuxkm/linuxkm_memory.c @@ -513,6 +513,25 @@ ssize_t wc_reloc_normalize_text( #define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_DIGEST_SIZE #endif +/* wc_fips_generate_hash() is the high level entry point to the supplementary + * FIPS integrity hash calculation facility, used for offline hash calculation + * (particularly for kernel module builds), and for the + * WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE mechanism in the kernel module. + * + * The seg_map describes the layout of the module, including its precomputed + * relocation table and its FIPS fenceposts. For + * WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE, seg_map is a static const in + * module_hooks.c, but for offline calculation, readelf is used in + * linuxkm-fips-hash-wrapper.sh to extract the values to pass to + * linuxkm-fips-hash. + * + * The HMAC callback pointers are generic, but have wolfCrypt-like argument + * structure -- for live WOLFCRYPT_FIPS_CORE_DYNAMIC_HASH_VALUE calls, they + * point to wrappers around native Linux kernel implementations, but for + * linuxkm-fips-hash, they point to wrappers around native wolfCrypt + * implementations. + */ + int wc_fips_generate_hash( const struct wc_reloc_table_segments *seg_map, word32 digest_size, diff --git a/linuxkm/linuxkm_memory.h b/linuxkm/linuxkm_memory.h index 86bb5933ea8..054b454e7c8 100644 --- a/linuxkm/linuxkm_memory.h +++ b/linuxkm/linuxkm_memory.h @@ -78,6 +78,8 @@ struct __attribute__((packed)) wc_reloc_table_ent { #if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) +/* full ELF fencepost representation, to allow wc_reloc_normalize_text() */ + struct wc_reloc_table_segments { unsigned long start; unsigned long end; @@ -164,6 +166,10 @@ struct wc_reloc_counts { #elif defined(HAVE_FIPS) +/* barebones FIPS fencepost representation -- no provision for + * wc_reloc_normalize_text() + */ + struct wc_reloc_table_segments { unsigned long start; unsigned long end; diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 9aa743b2cc3..791ce2e804c 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -24,6 +24,10 @@ #ifndef LINUXKM_WC_PORT_H #define LINUXKM_WC_PORT_H + #if defined(WOLFSSL_KERNEL_VERBOSE_DEBUG) && !defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG) + #define WOLFSSL_LINUXKM_VERBOSE_DEBUG + #endif + #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) @@ -1439,7 +1443,7 @@ #endif /* WC_SYM_RELOC_TABLES */ -#ifdef WC_LINUXKM_STACK_DEBUG +#if defined(WOLFSSL_KERNEL_STACK_DEBUG) || defined(WC_LINUXKM_STACK_DEBUG) #ifndef CONFIG_THREAD_INFO_IN_TASK #error WC_LINUXKM_STACK_DEBUG requires CONFIG_THREAD_INFO_IN_TASK diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index f278857b0a1..a116bd2ee00 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -1518,7 +1518,7 @@ extern const unsigned int wolfCrypt_FIPS_ro_end[]; static int linux_fips_hmac_setkey(struct shash_desc *desc, const byte *key, word32 key_len) { int ret = crypto_shash_setkey(desc->tfm, key, key_len); if (ret) { - pr_err("ERROR: crypto_ahash_setkey failed: err %d\n", ret); + pr_err("ERROR: crypto_shash_setkey failed: err %d\n", ret); return BAD_STATE_E; } From 9443f59db1612ca0b7271b9d6db9359dd5170e5d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 19 Feb 2026 17:59:21 -0600 Subject: [PATCH 3/7] linuxkm/linuxkm-fips-hash.c: fix overlong lines; .wolfssl_known_macro_extras: fix lexical order. --- .wolfssl_known_macro_extras | 2 +- linuxkm/linuxkm-fips-hash.c | 51 ++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index dd128d06612..97decd7acb9 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1064,8 +1064,8 @@ __SDCC_VERSION_MAJOR __SDCC_VERSION_MINOR __SDCC_VERSION_PATCH __SIZEOF_INT128__ -__SIZEOF_LONG__ __SIZEOF_LONG_LONG__ +__SIZEOF_LONG__ __STDC_NO_ATOMICS__ __STDC_VERSION__ __STDC__ diff --git a/linuxkm/linuxkm-fips-hash.c b/linuxkm/linuxkm-fips-hash.c index 64c19f2eeeb..4223a932192 100644 --- a/linuxkm/linuxkm-fips-hash.c +++ b/linuxkm/linuxkm-fips-hash.c @@ -103,7 +103,10 @@ int main(int argc, char **argv) static const struct option long_options[] = { #define FENCEPOST_OPT_FLAG (1U << 30) -#define FENCEPOST_OPT(x) { .name = #x, .has_arg = required_argument, .flag = NULL, .val = FENCEPOST_OPT_FLAG | offsetof(typeof(seg_map), x) } +#define FENCEPOST_OPT(x) { .name = #x, \ + .has_arg = required_argument, \ + .flag = NULL, \ + .val = FENCEPOST_OPT_FLAG | offsetof(typeof(seg_map), x) } FENCEPOST_OPT(text_start), FENCEPOST_OPT(text_end), FENCEPOST_OPT(reloc_tab_start), @@ -133,7 +136,8 @@ int main(int argc, char **argv) ret = wolfCrypt_Init(); if (ret < 0) { - fprintf(stderr, "%s: wolfCrypt_Init() failed: %s.\n", progname, wc_GetErrorString(ret)); + fprintf(stderr, "%s: wolfCrypt_Init() failed: %s.\n", + progname, wc_GetErrorString(ret)); exit(1); } @@ -148,7 +152,8 @@ int main(int argc, char **argv) c &= ~FENCEPOST_OPT_FLAG; *((unsigned long *)((byte *)&seg_map + c)) = strtoul(optarg, &eptr, 0); if (*eptr != '\0') { - fprintf(stderr, "%s: %s: supplied arg \"%s\" isn't a valid number.\n", progname, long_options[option_index].name, optarg); + fprintf(stderr, "%s: %s: supplied arg \"%s\" isn't a valid number.\n", + progname, long_options[option_index].name, optarg); exit(1); } continue; @@ -223,7 +228,8 @@ int main(int argc, char **argv) (seg_map.fips_rodata_start != ~0UL) || (seg_map.fips_rodata_end != ~0UL)) { - fprintf(stderr, "%s: note, ignoring explicit FIPS fenceposts because WC_USE_PIE_FENCEPOSTS_FOR_FIPS.\n", progname); + fprintf(stderr, "%s: note, ignoring explicit FIPS fenceposts " + "because WC_USE_PIE_FENCEPOSTS_FOR_FIPS.\n", progname); } seg_map.fips_text_start = seg_map.text_start; @@ -251,7 +257,8 @@ int main(int argc, char **argv) (seg_map.bss_start == ~0UL) || (seg_map.bss_end == ~0UL)) { - fprintf(stderr, "%s: segment fencepost(s) missing. Try --help.\n", progname); + fprintf(stderr, "%s: segment fencepost(s) missing. Try --help.\n", + progname); exit(1); } @@ -277,13 +284,18 @@ int main(int argc, char **argv) (seg_map.reloc_tab_len_start >= seg_map.reloc_tab_len_end) || (seg_map.reloc_tab_len_end >= (unsigned long)st.st_size)) { - fprintf(stderr, "%s: supplied reloc_tab fencepost(s) are out of bounds for supplied module %s with length %lu.\n", progname, mod_path, (unsigned long)st.st_size); + fprintf(stderr, "%s: supplied reloc_tab fencepost(s) are out of bounds " + "for supplied module %s with length %lu.\n", + progname, mod_path, (unsigned long)st.st_size); exit(1); } - mod_map = (byte *)mmap(NULL, st.st_size, inplace ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED | MAP_POPULATE, mod_fd, 0); + mod_map = (byte *)mmap(NULL, st.st_size, + inplace ? PROT_READ | PROT_WRITE : PROT_READ, + MAP_SHARED | MAP_POPULATE, mod_fd, 0); if (mod_map == MAP_FAILED) { - fprintf(stderr, "%s: mmap() of %s, length %zu: %m.\n", progname, mod_path, st.st_size); + fprintf(stderr, "%s: mmap() of %s, length %zu: %m.\n", + progname, mod_path, st.st_size); exit(1); } @@ -313,12 +325,14 @@ int main(int argc, char **argv) ret = wc_HmacInit(&hmac, NULL, INVALID_DEVID); if (ret != 0) { - fprintf(stderr, "%s: wc_HmacInit() failed: %s.\n", progname, wc_GetErrorString(ret)); + fprintf(stderr, "%s: wc_HmacInit() failed: %s.\n", + progname, wc_GetErrorString(ret)); exit(1); } if (seg_map.verifyCore_end - seg_map.verifyCore_start != new_verifyCore_size) { - fprintf(stderr, "%s: unexpected verifyCore length %zu.\n", progname, (size_t)(seg_map.verifyCore_end - seg_map.verifyCore_start)); + fprintf(stderr, "%s: unexpected verifyCore length %zu.\n", + progname, (size_t)(seg_map.verifyCore_end - seg_map.verifyCore_start)); ret = -1; goto out; } @@ -338,16 +352,22 @@ int main(int argc, char **argv) &reloc_counts); if (ret < 0) { - fprintf(stderr, "%s: wc_fips_generate_hash() failed: %s.\n", progname, wc_GetErrorString(ret)); + fprintf(stderr, "%s: wc_fips_generate_hash() failed: %s.\n", + progname, wc_GetErrorString(ret)); goto out; } if (verbose) - fprintf(inplace ? stdout : stderr, "FIPS-bounded relocation normalizations: text=%d, rodata=%d, rwdata=%d, bss=%d, other=%d\n", - reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, reloc_counts.bss, reloc_counts.other); + fprintf(inplace ? stdout : stderr, + "FIPS-bounded relocation normalizations: text=%d, rodata=%d, " + "rwdata=%d, bss=%d, other=%d\n", + reloc_counts.text, reloc_counts.rodata, reloc_counts.rwdata, + reloc_counts.bss, reloc_counts.other); if (new_verifyCore_size < sizeof new_verifyCore) { - fprintf(stderr, "%s: wc_fips_generate_hash() returned unexpected verifyCore length %u.\n", progname, new_verifyCore_size); + fprintf(stderr, "%s: wc_fips_generate_hash() returned unexpected " + "verifyCore length %u.\n", + progname, new_verifyCore_size); ret = -1; goto out; } @@ -359,7 +379,8 @@ int main(int argc, char **argv) fprintf(stderr, "%s: note, verifyCore already matches.\n", progname); } else if (inplace) { - XMEMCPY((void *)seg_map.verifyCore_start, new_verifyCore, new_verifyCore_size); + XMEMCPY((void *)seg_map.verifyCore_start, new_verifyCore, + new_verifyCore_size); ret = munmap(mod_map, st.st_size); if (ret < 0) { fprintf(stderr, "%s: munmap: %m\n", progname); From 70aa3dc5b18b23f5502d8e3aa45deed16ac204bd Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 20 Feb 2026 11:08:11 -0600 Subject: [PATCH 4/7] 20260204-linuxkm-fips-hash: more fixes+improvements from peer and AI review: linuxkm/linuxkm_memory.c: * fix straddle check in wc_reloc_normalize_text(); * fix seg_map bounds checks in wc_fips_generate_hash(); linuxkm/linuxkm_memory.h: fix initializer for wc_reloc_table_segments.bss_end; wolfssl/wolfcrypt/settings.h: add WC_BITS_TO_BYTES() and WC_BITS_FULL_BYTES() and deploy opportunistically to wolfssl/internal.h, wolfssl/wolfcrypt/{asn.h,dh.h,rsa.h,types.h}, wolfcrypt/src/sakke.c, and wolfcrypt/test/test.c. --- linuxkm/linuxkm_memory.c | 47 +++++++++++++++++++++++------------- linuxkm/linuxkm_memory.h | 4 +-- wolfcrypt/src/sakke.c | 8 +++--- wolfcrypt/test/test.c | 10 ++++---- wolfssl/internal.h | 2 +- wolfssl/wolfcrypt/asn.h | 2 +- wolfssl/wolfcrypt/dh.h | 2 +- wolfssl/wolfcrypt/rsa.h | 2 +- wolfssl/wolfcrypt/settings.h | 5 +++- wolfssl/wolfcrypt/types.h | 2 +- 10 files changed, 50 insertions(+), 34 deletions(-) diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c index 2e16f19ccad..ecda60f590a 100644 --- a/linuxkm/linuxkm_memory.c +++ b/linuxkm/linuxkm_memory.c @@ -237,7 +237,9 @@ ssize_t wc_reloc_normalize_text( break; } - if (next_reloc_rel > text_in_len - layout->width) { + if ((text_in_len < WC_BITS_TO_BYTES(layout->width)) || + (next_reloc_rel > text_in_len - WC_BITS_TO_BYTES(layout->width))) + { /* relocation straddles buffer at end -- caller will try again with * that relocation at the start. */ @@ -586,29 +588,40 @@ int wc_fips_generate_hash( return BAD_FUNC_ARG; } - if (seg_map->end > 0) { + if (seg_map->start > 0) { if ((seg_map->fips_text_start < seg_map->start) || - (seg_map->fips_text_end >= seg_map->end) || (seg_map->fips_rodata_start < seg_map->start) || - (seg_map->fips_rodata_end > seg_map->end) || - (seg_map->verifyCore_start < seg_map->start) || - (seg_map->verifyCore_end >= seg_map->end) + (seg_map->verifyCore_start < seg_map->start) #if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) || - ((seg_map->reloc_tab_end != 0) && - ((seg_map->reloc_tab_start < seg_map->start) || - (seg_map->reloc_tab_end >= seg_map->end))) || - ((seg_map->reloc_tab_len_end != 0) && - ((seg_map->reloc_tab_len_start < seg_map->start) || - (seg_map->reloc_tab_len_end >= seg_map->end))) || + (seg_map->reloc_tab_start < seg_map->start) || + (seg_map->reloc_tab_len_start < seg_map->start) || (seg_map->text_start < seg_map->start) || - (seg_map->text_end >= seg_map->end) || (seg_map->rodata_start < seg_map->start) || - (seg_map->rodata_end >= seg_map->end) || (seg_map->data_start < seg_map->start) || - (seg_map->data_end >= seg_map->end) || - (seg_map->bss_start < seg_map->start) || - (seg_map->bss_end >= seg_map->end) + (seg_map->bss_start < seg_map->start) +#endif + ) + { + RELOC_DEBUG_PRINTF("assert failed.\n"); + return BUFFER_E; + } + } + + if (seg_map->end > 0) { + if ((seg_map->fips_text_end > seg_map->end) || + (seg_map->fips_rodata_end > seg_map->end) || + (seg_map->verifyCore_end > seg_map->end) +#if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) + || + ((seg_map->reloc_tab_end != 0) && + (seg_map->reloc_tab_end > seg_map->end)) || + ((seg_map->reloc_tab_len_end != 0) && + (seg_map->reloc_tab_len_end > seg_map->end)) || + (seg_map->text_end > seg_map->end) || + (seg_map->rodata_end > seg_map->end) || + (seg_map->data_end > seg_map->end) || + (seg_map->bss_end > seg_map->end) #endif ) { diff --git a/linuxkm/linuxkm_memory.h b/linuxkm/linuxkm_memory.h index 054b454e7c8..011b2ee4578 100644 --- a/linuxkm/linuxkm_memory.h +++ b/linuxkm/linuxkm_memory.h @@ -130,7 +130,7 @@ struct wc_reloc_table_segments { .data_start = ~0UL, \ .data_end = ~0UL, \ .bss_start = ~0UL, \ - .bss_end = 0, \ + .bss_end = ~0UL, \ .text_is_live = 0 \ } @@ -150,7 +150,7 @@ struct wc_reloc_table_segments { .data_start = ~0UL, \ .data_end = ~0UL, \ .bss_start = ~0UL, \ - .bss_end = 0, \ + .bss_end = ~0UL, \ .text_is_live = 0 \ } diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index 87977646156..e59943506b7 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -2510,7 +2510,7 @@ int wc_GetSakkeAuthSize(SakkeKey* key, word16* authSz) err = sakke_load_params(key); } if (err == 0) { - word16 n = (word16)((mp_count_bits(&key->params.prime) + 7) / 8); + word16 n = (word16)WC_BITS_TO_BYTES(mp_count_bits(&key->params.prime)); *authSz = (word16)(1 + 2 * n); } @@ -6709,7 +6709,7 @@ int wc_MakeSakkeEncapsulatedSSV(SakkeKey* key, enum wc_HashType hashType, err = sakke_load_params(key); } if (err == 0) { - n = (word16)((mp_count_bits(&key->params.prime) + 7) / 8); + n = (word16)WC_BITS_TO_BYTES(mp_count_bits(&key->params.prime)); /* Uncompressed point */ outSz = (word16)(1 + 2 * n); @@ -6807,7 +6807,7 @@ int wc_GenerateSakkeSSV(SakkeKey* key, WC_RNG* rng, byte* ssv, word16* ssvSz) err = sakke_load_params(key); } if (err == 0) { - n = (word16)((mp_count_bits(&key->params.prime) + 7) / 8); + n = (word16)WC_BITS_TO_BYTES(mp_count_bits(&key->params.prime)); if ((ssv != NULL) && (*ssvSz > n)) { err = BAD_FUNC_ARG; @@ -6886,7 +6886,7 @@ int wc_DeriveSakkeSSV(SakkeKey* key, enum wc_HashType hashType, byte* ssv, err = sakke_load_params(key); } if (err == 0) { - n = (word16)((mp_count_bits(&key->params.prime) + 7) / 8); + n = (word16)WC_BITS_TO_BYTES(mp_count_bits(&key->params.prime)); if (authSz != 2 * n + 1) { err = BAD_FUNC_ARG; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index fa49d76bca2..a98e826af23 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -60083,7 +60083,7 @@ static wc_test_ret_t mp_test_shbd(mp_int* a, mp_int* b, WC_RNG* rng) #ifndef WOLFSSL_SP_MATH for (i = 0; i < 10; i++) { - for (j = 1; j < (DIGIT_BIT + 7) / 8 * 3; j++) { + for (j = 1; j < WC_BITS_TO_BYTES(DIGIT_BIT) * 3; j++) { ret = randNum(a, j, rng, NULL); if (ret != MP_OKAY) return WC_TEST_RET_ENC_EC(ret); @@ -60104,7 +60104,7 @@ static wc_test_ret_t mp_test_shbd(mp_int* a, mp_int* b, WC_RNG* rng) #endif for (i = 0; i < 10; i++) { - for (j = 1; j < (DIGIT_BIT + 7) / 8 * 3; j++) { + for (j = 1; j < WC_BITS_TO_BYTES(DIGIT_BIT) * 3; j++) { ret = randNum(a, j, rng, NULL); if (ret != MP_OKAY) return WC_TEST_RET_ENC_EC(ret); @@ -60182,11 +60182,11 @@ static wc_test_ret_t mp_test_div(mp_int* a, mp_int* d, mp_int* r, mp_int* rem, return WC_TEST_RET_ENC_EC(ret); for (i = 0; i < 100; i++) { - for (j = 1; j < (DIGIT_BIT + 7) / 8 * 2; j++) { + for (j = 1; j < WC_BITS_TO_BYTES(DIGIT_BIT) * 2; j++) { ret = randNum(d, j, rng, NULL); if (ret != MP_OKAY) return WC_TEST_RET_ENC_EC(ret); - for (k = 1; k < (DIGIT_BIT + 7) / 8 * 2 + 1; k++) { + for (k = 1; k < WC_BITS_TO_BYTES(DIGIT_BIT) * 2 + 1; k++) { ret = randNum(a, k, rng, NULL); if (ret != MP_OKAY) return WC_TEST_RET_ENC_EC(ret); @@ -60210,7 +60210,7 @@ static wc_test_ret_t mp_test_div(mp_int* a, mp_int* d, mp_int* r, mp_int* rem, } } - ret = randNum(d, (DIGIT_BIT + 7) / 8 * 2, rng, NULL); + ret = randNum(d, WC_BITS_TO_BYTES(DIGIT_BIT) * 2, rng, NULL); if (ret != MP_OKAY) return WC_TEST_RET_ENC_EC(ret); mp_add(d, d, a); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 294c69be411..7ebe3d7b6c0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1260,7 +1260,7 @@ enum { #elif (defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH)) && \ defined(SP_INT_BITS) /* SP implementation supports numbers of SP_INT_BITS bits. */ - #define WOLFSSL_MAX_DHKEY_BITS (((SP_INT_BITS + 7) / 8) * 8) + #define WOLFSSL_MAX_DHKEY_BITS WC_BITS_FULL_BYTES(SP_INT_BITS) #else #define WOLFSSL_MAX_DHKEY_BITS 4096 #endif diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 4af64812056..69e8f04bae0 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -855,7 +855,7 @@ extern const WOLFSSL_ObjectInfo wolfssl_object_info[]; #define WC_MAX_RSA_BITS (FP_MAX_BITS / 2) #elif defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH) /* SP implementation supports numbers of SP_INT_BITS bits. */ - #define WC_MAX_RSA_BITS (((SP_INT_BITS + 7) / 8) * 8) + #define WC_MAX_RSA_BITS WC_BITS_FULL_BYTES(SP_INT_BITS) #else /* Integer maths is dynamic but we only go up to 4096 bits. */ #define WC_MAX_RSA_BITS 4096 diff --git a/wolfssl/wolfcrypt/dh.h b/wolfssl/wolfcrypt/dh.h index 31ec83ecf5c..f61025d36db 100644 --- a/wolfssl/wolfcrypt/dh.h +++ b/wolfssl/wolfcrypt/dh.h @@ -104,7 +104,7 @@ enum { #endif #elif defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH) /* SP implementation supports numbers of SP_INT_BITS bits. */ - #define DH_MAX_SIZE (((SP_INT_BITS + 7) / 8) * 8) + #define DH_MAX_SIZE WC_BITS_FULL_BYTES(SP_INT_BITS) #if defined(WOLFSSL_MYSQL_COMPATIBLE) && DH_MAX_SIZE < 8192 #error "MySQL needs SP_INT_BITS at least at 8192" #endif diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 19f963a1815..77911fc9543 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -119,7 +119,7 @@ RSA keys can be used to encrypt, decrypt, sign and verify data. #endif #elif defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH) /* SP implementation supports numbers of SP_INT_BITS bits. */ - #define RSA_MAX_SIZE (((SP_INT_BITS + 7) / 8) * 8) + #define RSA_MAX_SIZE WC_BITS_FULL_BYTES(SP_INT_BITS) #if defined(WOLFSSL_MYSQL_COMPATIBLE) && RSA_MAX_SIZE < 8192 #error "MySQL needs SP_INT_BITS at least at 8192" #endif diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index f5186c802e3..503fade1d97 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -63,6 +63,9 @@ #include #endif +#define WC_BITS_TO_BYTES(x) (((x) + 7) >> 3) +#define WC_BITS_FULL_BYTES(x) (WC_BITS_TO_BYTES(x) << 3) + /* Uncomment next line if using IPHONE */ /* #define IPHONE */ @@ -4391,7 +4394,7 @@ extern void uITRON4_free(void *p) ; #endif #elif defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH) /* SP implementation supports numbers of SP_INT_BITS bits. */ - #define DH_MAX_SIZE (((SP_INT_BITS + 7) / 8) * 8) + #define DH_MAX_SIZE WC_BITS_FULL_BYTES(SP_INT_BITS) #if defined(WOLFSSL_MYSQL_COMPATIBLE) && DH_MAX_SIZE < 8192 #error "MySQL needs SP_INT_BITS at least at 8192" #endif diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index c0cd8bd847d..28bb12fc17b 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2300,7 +2300,7 @@ enum Max_ASN { MAX_ENCODED_SIG_SZ = FP_MAX_BITS / 8, #elif (defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH)) && \ defined(SP_INT_BITS) - MAX_ENCODED_SIG_SZ = (SP_INT_BITS + 7) / 8, + MAX_ENCODED_SIG_SZ = WC_BITS_TO_BYTES(SP_INT_BITS), #elif defined(WOLFSSL_HAPROXY) MAX_ENCODED_SIG_SZ = 1024, /* Supports 8192 bit keys */ #else From f84377ed694a8ae4dbdf3a33152aeb1f63ebce07 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 20 Feb 2026 11:35:47 -0600 Subject: [PATCH 5/7] linuxkm/linuxkm-fips-hash-wrapper.sh: make sure awk is gawk. --- linuxkm/linuxkm-fips-hash-wrapper.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/linuxkm/linuxkm-fips-hash-wrapper.sh b/linuxkm/linuxkm-fips-hash-wrapper.sh index 7244ff5e069..57a2f02be04 100755 --- a/linuxkm/linuxkm-fips-hash-wrapper.sh +++ b/linuxkm/linuxkm-fips-hash-wrapper.sh @@ -26,7 +26,27 @@ set -o noclobber -o nounset -o pipefail -o errexit mod_path=$1 shift -readarray -t fenceposts < <(readelf --wide --sections --symbols "$mod_path" | awk ' +# require Gnu Awk, for strtonum(). + +if [[ -v AWK ]] && ! "$AWK" --version 2>&1 | grep -F -q 'GNU Awk'; then + unset AWK +fi + +if [[ ! -v AWK ]]; then + if command -v gawk >/dev/null; then + AWK='gawk' + else + AWK='awk' + fi +fi + +if ! "$AWK" --version 2>&1 | grep -F -q 'GNU Awk'; then + echo "Couldn't find required GNU Awk executable." >&2 + exit 1 +fi + +# shellcheck disable=SC2016 # using $AWK instead of awk confuses shellcheck. +readarray -t fenceposts < <(readelf --wide --sections --symbols "$mod_path" | "$AWK" ' BEGIN { fips_fenceposts["wc_linuxkm_pie_reloc_tab"] = "reloc_tab_start"; fips_fenceposts["wc_linuxkm_pie_reloc_tab_length"] = "reloc_tab_len_start"; From 10ba02fe40920df89b27ab94d70925da169894bb Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 20 Feb 2026 15:44:54 -0600 Subject: [PATCH 6/7] linuxkm/lkcapi_sha_glue.c: add -wolfentropy and/or -rdseed to WOLFKM_STDRNG_DRIVER to advertise the seed source. --- linuxkm/lkcapi_sha_glue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 9851985c133..a8ace24a7a7 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -110,12 +110,29 @@ * exhaustion. A caller that really needs PR can pass in seed data in its call * to our rng_alg.generate() implementation. */ + +#ifdef HAVE_ENTROPY_MEMUSE + #define WOLFKM_STDRNG_WOLFENTROPY "-wolfentropy" +#else + #define WOLFKM_STDRNG_WOLFENTROPY "" +#endif + +#if defined(HAVE_INTEL_RDSEED) || defined(HAVE_AMD_RDSEED) + #define WOLFKM_STDRNG_RDSEED "-rdseed" +#else + #define WOLFKM_STDRNG_RDSEED "" +#endif + #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES #define WOLFKM_STDRNG_DRIVER ("sha2-256-drbg-nopr" \ + WOLFKM_STDRNG_WOLFENTROPY \ + WOLFKM_STDRNG_RDSEED \ WOLFKM_DRIVER_SUFFIX_BASE \ "-with-global-replace") #else #define WOLFKM_STDRNG_DRIVER ("sha2-256-drbg-nopr" \ + WOLFKM_STDRNG_WOLFENTROPY \ + WOLFKM_STDRNG_RDSEED \ WOLFKM_DRIVER_SUFFIX_BASE) #endif From 9ca32e23d408f4f9dc76b81e6bdfd530ccecfca3 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 20 Feb 2026 15:45:27 -0600 Subject: [PATCH 7/7] linuxkm/linuxkm-fips-hash.c: cosmetic bikeshedding (AI-prompted); linuxkm/linuxkm_memory.c and linuxkm/linuxkm_memory.h: set up and use WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ. --- linuxkm/linuxkm-fips-hash.c | 26 +++++++++++--------------- linuxkm/linuxkm_memory.c | 4 ++-- linuxkm/linuxkm_memory.h | 4 ++++ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/linuxkm/linuxkm-fips-hash.c b/linuxkm/linuxkm-fips-hash.c index 4223a932192..fc53a1619fd 100644 --- a/linuxkm/linuxkm-fips-hash.c +++ b/linuxkm/linuxkm-fips-hash.c @@ -62,12 +62,7 @@ extern const char coreKey[FIPS_IN_CORE_KEY_SZ*2 + 1]; #endif static int hmac_setkey_cb(Hmac *hmac, const byte *key, word32 key_len) { - int ret; - - ret = wc_HmacSetKey(hmac, FIPS_IN_CORE_HASH_TYPE, key, key_len); - if (ret != 0) - return ret; - return 0; + return wc_HmacSetKey(hmac, FIPS_IN_CORE_HASH_TYPE, key, key_len); } static int hmac_update_cb(Hmac *hmac, const byte *in, word32 in_len) { @@ -89,7 +84,7 @@ int main(int argc, char **argv) int ret; struct wc_reloc_table_segments seg_map = WC_RELOC_TABLE_SEGMENTS_INITIALIZER; word32 new_verifyCore_size = FIPS_IN_CORE_DIGEST_SIZE*2 + 1; - char new_verifyCore[new_verifyCore_size]; + char new_verifyCore[FIPS_IN_CORE_DIGEST_SIZE*2 + 1]; const char *progname = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; const char *mod_path = NULL; const char *user_coreKey = NULL; @@ -134,13 +129,6 @@ int main(int argc, char **argv) { } }; - ret = wolfCrypt_Init(); - if (ret < 0) { - fprintf(stderr, "%s: wolfCrypt_Init() failed: %s.\n", - progname, wc_GetErrorString(ret)); - exit(1); - } - for (;;) { int option_index = 0; int c = getopt_long(argc, argv, "f:ik:qvh", long_options, &option_index); @@ -292,7 +280,8 @@ int main(int argc, char **argv) mod_map = (byte *)mmap(NULL, st.st_size, inplace ? PROT_READ | PROT_WRITE : PROT_READ, - MAP_SHARED | MAP_POPULATE, mod_fd, 0); + (inplace ? MAP_SHARED : MAP_PRIVATE) | MAP_POPULATE, + mod_fd, 0); if (mod_map == MAP_FAILED) { fprintf(stderr, "%s: mmap() of %s, length %zu: %m.\n", progname, mod_path, st.st_size); @@ -323,6 +312,13 @@ int main(int argc, char **argv) seg_map.bss_start += (unsigned long)mod_map; seg_map.bss_end += (unsigned long)mod_map; + ret = wolfCrypt_Init(); + if (ret < 0) { + fprintf(stderr, "%s: wolfCrypt_Init() failed: %s.\n", + progname, wc_GetErrorString(ret)); + exit(1); + } + ret = wc_HmacInit(&hmac, NULL, INVALID_DEVID); if (ret != 0) { fprintf(stderr, "%s: wc_HmacInit() failed: %s.\n", diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c index ecda60f590a..542933c2a23 100644 --- a/linuxkm/linuxkm_memory.c +++ b/linuxkm/linuxkm_memory.c @@ -730,7 +730,7 @@ int wc_fips_generate_hash( { ssize_t cur_reloc_index = -1; const byte *text_p = (const byte *)seg_map->fips_text_start; - byte *buf = XMALLOC(8192, NULL, DYNAMIC_TYPE_TMP_BUFFER); + byte *buf = XMALLOC(WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (! buf) { ret = MEMORY_E; @@ -744,7 +744,7 @@ int wc_fips_generate_hash( */ ssize_t progress = wc_reloc_normalize_text( text_p, - min(8192, (word32)((const byte *)seg_map->fips_text_end - text_p)), + min(WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ, (word32)((const byte *)seg_map->fips_text_end - text_p)), buf, &cur_reloc_index, seg_map, diff --git a/linuxkm/linuxkm_memory.h b/linuxkm/linuxkm_memory.h index 011b2ee4578..695a82aafae 100644 --- a/linuxkm/linuxkm_memory.h +++ b/linuxkm/linuxkm_memory.h @@ -200,6 +200,10 @@ struct wc_reloc_counts { #if defined(WC_SYM_RELOC_TABLES) || defined(WC_SYM_RELOC_TABLES_SUPPORT) +#ifndef WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ + #define WOLFSSL_TEXT_SEGMENT_CANONICALIZER_BUFSIZ 8192 +#endif + WOLFSSL_API ssize_t wc_reloc_normalize_text( const byte *text_in, size_t text_in_len,