From bdea01a9313f8b02281df8168ee3379552a069c6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 27 Feb 2026 23:39:38 -0600 Subject: [PATCH 1/5] src/x509.c: in loadX509orX509REQFromPemBio(), fix an identicalInnerCondition. --- src/x509.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/x509.c b/src/x509.c index fd46e89c40c..c147d28feed 100644 --- a/src/x509.c +++ b/src/x509.c @@ -13039,9 +13039,7 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, if (i < pemSz && pem[i-1] == '\r') { /* found \r , Windows line ending is \r\n so try to read one * more byte for \n, ignoring return value */ - if (i < pemSz) { - (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); - } + (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); } } break; From 616a6a57890c19d35513f1044720f562d718b514 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 27 Feb 2026 23:39:57 -0600 Subject: [PATCH 2/5] wolfcrypt/src/ecc.c: in wc_ecc_import_point_der_ex() and wc_ecc_import_x963_ex2(), add missing retval capture for sp_ecc_uncompress_sm2_256() (Fenrir M-68). --- wolfcrypt/src/ecc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 721c52a1d5e..d30151e9b00 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -9497,7 +9497,7 @@ int wc_ecc_import_point_der_ex(const byte* in, word32 inLen, #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SP_SM2) if (curve_idx != ECC_CUSTOM_IDX && ecc_sets[curve_idx].id == ECC_SM2P256V1) { - sp_ecc_uncompress_sm2_256(point->x, pointType, point->y); + err = sp_ecc_uncompress_sm2_256(point->x, pointType, point->y); } else #endif @@ -10854,7 +10854,7 @@ int wc_ecc_import_x963_ex2(const byte* in, word32 inLen, ecc_key* key, #endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SP_SM2) if (key->dp->id == ECC_SM2P256V1) { - sp_ecc_uncompress_sm2_256(key->pubkey.x, pointType, key->pubkey.y); + err = sp_ecc_uncompress_sm2_256(key->pubkey.x, pointType, key->pubkey.y); } else #endif From 41108878713f98cfeefeb4748d6c28038ea538ea Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 27 Feb 2026 23:40:19 -0600 Subject: [PATCH 3/5] wolfcrypt/src/aes.c: in AesSivCipher(), burn sivTmp before return (Fenrir M-69). --- wolfcrypt/src/aes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 0713c49bc16..ca8e47c703c 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -16531,6 +16531,8 @@ static WARN_UNUSED_RESULT int AesSivCipher( wc_AesFree(aes); #endif + ForceZero(sivTmp, sizeof(sivTmp)); + return ret; } From 76bc6e337b24ed76c4539eda357d593867da7ad2 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 27 Feb 2026 23:40:37 -0600 Subject: [PATCH 4/5] wolfcrypt/src/hpke.c, wolfssl/wolfcrypt/error-crypt.h, wolfcrypt/src/error.c: implement RFC 9180 overflow checks on context->seq in wc_HpkeContextSealBase() and wc_HpkeContextOpenBase(), and add SEQ_OVERFLOW_E to wolfCrypt_ErrorCodes (Fenrir M-70). --- wolfcrypt/src/error.c | 3 +++ wolfcrypt/src/hpke.c | 10 ++++++++++ wolfssl/wolfcrypt/error-crypt.h | 5 +++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index c74879f8e0d..431a3c996c0 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -665,6 +665,9 @@ const char* wc_GetErrorString(int error) case ALREADY_E: return "Operation was redundant or preempted"; + case SEQ_OVERFLOW_E: + return "Sequence counter would overflow"; + case MAX_CODE_E: case WC_SPAN1_MIN_CODE_E: case MIN_CODE_E: diff --git a/wolfcrypt/src/hpke.c b/wolfcrypt/src/hpke.c index e7b15db0a44..ea49be60dee 100644 --- a/wolfcrypt/src/hpke.c +++ b/wolfcrypt/src/hpke.c @@ -865,6 +865,11 @@ int wc_HpkeContextSealBase(Hpke* hpke, HpkeBaseContext* context, plaintext == NULL || out == NULL) { return BAD_FUNC_ARG; } + + /* RFC 9180 requires error on sequence overflow. */ + if (context->seq == WC_MAX_SINT_OF(int)) + return SEQ_OVERFLOW_E; + WC_ALLOC_VAR_EX(aes, Aes, 1, hpke->heap, DYNAMIC_TYPE_AES, return MEMORY_E); ret = wc_AesInit(aes, hpke->heap, INVALID_DEVID); @@ -1097,6 +1102,11 @@ int wc_HpkeContextOpenBase(Hpke* hpke, HpkeBaseContext* context, byte* aad, if (hpke == NULL) { return BAD_FUNC_ARG; } + + /* RFC 9180 requires error on sequence overflow. */ + if (context->seq == WC_MAX_SINT_OF(int)) + return SEQ_OVERFLOW_E; + XMEMSET(nonce, 0, sizeof(nonce)); WC_ALLOC_VAR_EX(aes, Aes, 1, hpke->heap, DYNAMIC_TYPE_AES, return MEMORY_E); diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index cc7d3679ff1..f879aabc3bb 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -312,8 +312,9 @@ enum wolfCrypt_ErrorCodes { BUSY_E = -1006, /* Object is busy */ ALREADY_E = -1007, /* Operation was redundant or preempted */ - WC_SPAN2_LAST_E = -1007, /* Update to indicate last used error code */ - WC_LAST_E = -1007, /* the last code used either here or in + SEQ_OVERFLOW_E = -1008, /* Sequence counter would overflow */ + WC_SPAN2_LAST_E = -1008, /* Update to indicate last used error code */ + WC_LAST_E = -1008, /* the last code used either here or in * error-ssl.h */ WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */ From d22175ae371c4bcf3b380b63d39b4f7022d8d22c Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sat, 28 Feb 2026 00:25:06 -0600 Subject: [PATCH 5/5] Makefile.am: for linuxkm module target, pass through "module" target as such, for compatibility with alt LIBWOLFSSL_NAME. linuxkm/Makefile: * don't use `readarray -d` -- it's a recent bashism; * rework libwolfssl-user-build/src/.libs/libwolfssl.so recipe to better isolate sub-build settings. * add support for HOSTCC and HOSTCFLAGS in libwolfssl.so build. * deploy $(QFLAG) --no-print-directory --no-silent in several submakes for neatness and resilience. * tweak $(LIBWOLFSSL_NAME).ko.signed recipe to add a "skipping" message and some consistency checking. linuxkm/README.md: update FIPS DRBG /proc/crypto content to show seed source. linuxkm/linuxkm_memory.c: fixes for format character portability in a RELOC_DEBUG_PRINTF() in wc_reloc_normalize_text). linuxkm/linuxkm_wc_port.h: pull in linux/moduleparam.h, and if WC_LINUXKM_SUPPORT_DUMP_TO_FILE, pull in linux/fs.h and linux/uaccess.h. linuxkm/module_hooks.c: implement WC_LINUXKM_SUPPORT_DUMP_TO_FILE: dump_to_file() and module args text_dump_path=... and rodata_dump_path=... linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch: add to accommodate patch-breaking change in Linux 7dff99b354. --- .wolfssl_known_macro_extras | 1 + Makefile.am | 2 +- linuxkm/Makefile | 101 ++-- linuxkm/README.md | 4 +- linuxkm/linuxkm_memory.c | 26 +- linuxkm/linuxkm_wc_port.h | 5 + linuxkm/module_hooks.c | 89 ++++ ...INUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch | 484 ++++++++++++++++++ 8 files changed, 660 insertions(+), 52 deletions(-) create mode 100644 linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 50527e10f69..ea4c8eaaa93 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -637,6 +637,7 @@ WC_DILITHIUM_FIXED_ARRAY WC_DISABLE_RADIX_ZERO_PAD WC_FLAG_DONT_USE_AESNI WC_FORCE_LINUXKM_FORTIFY_SOURCE +WC_LINUXKM_SUPPORT_DUMP_TO_FILE WC_LMS_FULL_HASH WC_NO_ASYNC_SLEEP WC_NO_RNG_SIMPLE diff --git a/Makefile.am b/Makefile.am index ea86253f1b7..fce812babf5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -229,7 +229,7 @@ if BUILD_LINUXKM FIPS_FLAVOR module: - +$(MAKE) -C linuxkm libwolfssl.ko + +$(MAKE) -C linuxkm module module-update-fips-hash: +$(MAKE) -C linuxkm module-update-fips-hash diff --git a/linuxkm/Makefile b/linuxkm/Makefile index 93586956a27..9438506273b 100644 --- a/linuxkm/Makefile +++ b/linuxkm/Makefile @@ -33,6 +33,8 @@ ifndef LIBWOLFSSL_NAME LIBWOLFSSL_NAME := libwolfssl endif +module: $(LIBWOLFSSL_NAME).ko + all: $(LIBWOLFSSL_NAME).ko $(LIBWOLFSSL_NAME).ko.signed ifndef MODULE_TOP @@ -249,6 +251,7 @@ $(LIBWOLFSSL_NAME).ko: echo 'CPPFLAGS = "$(CPPFLAGS)"'; echo 'AM_CFLAGS = "$(AM_CFLAGS)"'; echo 'CFLAGS = "$(CFLAGS)"'; + echo 'HOSTCFLAGS = "$(HOSTCFLAGS)"'; echo 'KERNEL_EXTRA_CFLAGS = "$(KERNEL_EXTRA_CFLAGS)"'; echo 'FIPS_OPTEST = "$(FIPS_OPTEST)"'; echo 'AM_CCASFLAGS = "$(AM_CCASFLAGS)"'; @@ -261,6 +264,7 @@ $(LIBWOLFSSL_NAME).ko: echo 'host_triplet = "$(host_triplet)"'; echo 'build_triplet = "$(build_triplet)"'; echo 'CC = "$(CC)"'; + echo 'HOSTCC = "$(HOSTCC)"'; echo 'AS = "$(AS)"'; echo 'LD = "$(LD)"'; echo 'READELF = "$(READELF)"'; @@ -302,7 +306,8 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes" @RELOC_TMP=$$(mktemp "$(MAKE_TMPDIR)/wc_linuxkm_pie_reloc_tab.c.XXXXXX") @trap 'rm "$$RELOC_TMP"' EXIT @if [[ -f "$@" ]]; then touch -r "$@" "$$RELOC_TMP"; fi - +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= + # --no-silent works around make bug that otherwise leads to "No rule to make target 's'. Stop." (due to a bug around $(MAKEFLAGS)) in --quiet builds. + +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= # if the above make didn't build a fresh libwolfssl.ko, then the module is already up to date and we leave it untouched, assuring stability for purposes of module-update-fips-hash. @if [[ ! "$@" -nt "$$RELOC_TMP" ]]; then echo ' Module already up-to-date.'; exit 0; fi @SECTION_MAP=$$(mktemp) @@ -310,13 +315,16 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes" @export SECTION_MAP @$(READELF) --wide --sections --symbols "$@" | $(GENERATE_SECTION_MAP) @$(READELF) --wide --relocs "$@" | $(GENERATE_RELOC_TAB) >| '$(MODULE_TOP)/linuxkm/wc_linuxkm_pie_reloc_tab.c' - +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= + +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) CC_FLAGS_FTRACE= @$(READELF) --wide --relocs "$@" | $(GENERATE_RELOC_TAB) >| "$$RELOC_TMP" @if diff '$(MODULE_TOP)/linuxkm/wc_linuxkm_pie_reloc_tab.c' "$$RELOC_TMP"; then echo " Relocation table is stable."; else echo "PIE failed: relocation table is unstable." 1>&2; exit 1; fi else - +$(MAKE) ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) + # --no-silent works around make bug that otherwise leads to "No rule to make target 's'. Stop." (due to a bug around $(MAKEFLAGS)) in --quiet builds. + +$(MAKE) $(QFLAG) --no-print-directory --no-silent ARCH='$(KERNEL_ARCH)' $(OVERRIDE_PATHS) $(CROSS_COMPILE) -C '$(KERNEL_ROOT)' M='$(MODULE_TOP)' $(KBUILD_EXTRA_FLAGS) endif +$(MODULE_TOP)/$(LIBWOLFSSL_NAME).ko: $(LIBWOLFSSL_NAME).ko + .PHONY: module-update-fips-hash module-update-fips-hash: $(LIBWOLFSSL_NAME).ko @set -e @@ -335,7 +343,7 @@ module-update-fips-hash: $(LIBWOLFSSL_NAME).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_NAME).ko.signed' ]]; then $(MAKE) -C . '$(LIBWOLFSSL_NAME).ko.signed'; fi + if [[ -f '$(LIBWOLFSSL_NAME).ko.signed' ]]; then $(MAKE) $(QFLAG) --no-print-directory --no-silent -C . '$(LIBWOLFSSL_NAME).ko.signed'; fi # linuxkm-fips-hash implements offline (no-load) FIPS hash calculation and graft-in. @@ -348,45 +356,61 @@ module-update-fips-hash: $(LIBWOLFSSL_NAME).ko # 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' +FRESH_MAKEFLAGS := $(patsubst -j%,-j %,$(filter -%,$(filter-out -- --jobserver-auth=%,$(MAKEFLAGS)))) +FRESH_ENV := env -i HOME="$$HOME" PATH="/usr/local/bin:/usr/bin:/bin:$$PATH" LANG="$${LANG-C.UTF-8}" LC_ALL="$${LC_ALL-C.UTF-8}" TERM="$${TERM-dumb}" + +.PHONY: $(MODULE_TOP)/libwolfssl-user-build/src/.libs/libwolfssl.so +$(MODULE_TOP)/libwolfssl-user-build/src/.libs/libwolfssl.so: $(LIBWOLFSSL_NAME).ko + @set -o errexit -o pipefail + @if [[ '$(SRC_TOP)/configure' -nt '$@' ]]; then + @ echo 'Purging stale libwolfssl-user-build tree.' + @ $(RM) -rf '$(MODULE_TOP)/libwolfssl-user-build' + @fi + @mkdir -p '$(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 $(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' + @if [[ ! -e '$(MODULE_TOP)/libwolfssl-user-build/configure' ]]; then + @ pushd '$(SRC_TOP)' >/dev/null + @ echo -n 'Populating tree of symlinks for user libwolfssl.so build...' + @ readarray -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/\* \) -print) + @ 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.' + @fi + @if [[ ! -f user_settings.h ]]; then + @ echo '__attribute__ ((visibility("default"))) extern const char coreKey[];' > user_settings.h + @ echo > user_settings_asm.h + @fi + @if [[ -f Makefile ]]; then + @ echo 'Using existing Makefile for libwolfssl.so.' + @else + @ echo -n 'Configuring user libwolfssl.so...' + @ $(FRESH_ENV) ./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' '$(if $(HOSTCC),CC=$(HOSTCC))' + @ echo ' done.' + @fi + @echo -n 'Building user libwolfssl.so...' + @$(FRESH_ENV) $(MAKE) $(QFLAG) $(FRESH_MAKEFLAGS) >/dev/null @echo ' done.' - @echo -n 'Compiling and linking libwolfssl.so...' - +$(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) $(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 + @echo -n 'Fixing FIPS hash in user libwolfssl.so...' + @if ! userhash=$$(wolfcrypt/test/testwolfcrypt 2>&1 | sed -n -E 's/^hash = (.+)$$/\1/p'); then + @ if [[ -z "$$userhash" ]]; then echo ' FIPS hash not found!' >&2; exit 1; fi + @ find wolfcrypt/src -name '*fips_test*o' -delete + @ $(FRESH_ENV) $(MAKE) $(QFLAG) $(FRESH_MAKEFLAGS) EXTRA_CFLAGS=-DWOLFCRYPT_FIPS_CORE_HASH_VALUE="$$userhash" >/dev/null + @ echo ' done.' + @else + @ @echo ' already matches (no update needed).' + @fi + +linuxkm-fips-hash: $(MODULE_TOP)/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 -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 + @$(or $(HOSTCC),cc) $(or $(HOSTCFLAGS),-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 "$<" $(QFLAG) $(VFLAG) - +$(MAKE) $(QFLAG) -C . '$(LIBWOLFSSL_NAME).ko.signed' + +$(MAKE) $(QFLAG) --no-print-directory --no-silent -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 @@ -406,7 +430,9 @@ else ;; esac done < .config - if [[ "$${CONFIG_MODULE_SIG}" = "y" && -n "$${CONFIG_MODULE_SIG_KEY}" && \ + if [[ "$${CONFIG_MODULE_SIG}" != "y" ]]; then + echo ' [skipping $@ -- CONFIG_MODULE_SIG is unset in target kernel]' + elif [[ -n "$${CONFIG_MODULE_SIG_KEY}" && \ -n "$${CONFIG_MODULE_SIG_HASH}" && ( ! -f '$(MODULE_TOP)/$@' || \ '$(MODULE_TOP)/$<' -nt '$(MODULE_TOP)/$@' ) ]]; then CONFIG_MODULE_SIG_KEY="$${CONFIG_MODULE_SIG_KEY#\"}" @@ -426,19 +452,22 @@ else if [[ "$(quiet)" != "silent_" ]]; then echo " Module $@ signed by $${CONFIG_MODULE_SIG_KEY}." fi + elif [[ ! -f '$(MODULE_TOP)/$@' || '$(MODULE_TOP)/$<' -nt '$(MODULE_TOP)/$@' ]]; then + echo 'Unable to generate $@ from $<: CONFIG_MODULE_SIG_KEY and/or CONFIG_MODULE_SIG_HASH is missing.' >&2 + exit 1 fi endif .PHONY: install modules_install install modules_install: - +$(MAKE) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install + +$(MAKE) $(QFLAG) --no-silent -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install .PHONY: clean # note, must supply $(MODULE_TOP) as the src value for clean so that Kbuild is included, else # the top Makefile (which is not for the kernel build) would be included here. clean: - +$(MAKE) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(MODULE_TOP) clean + +$(MAKE) $(QFLAG) --no-silent -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(MODULE_TOP) clean $(RM) -rf '$(MODULE_TOP)/linuxkm' $(RM) -rf '$(MODULE_TOP)/wolfcrypt' $(RM) -rf '$(MODULE_TOP)/src' diff --git a/linuxkm/README.md b/linuxkm/README.md index b4e69abd59f..dd65cec87d7 100644 --- a/linuxkm/README.md +++ b/linuxkm/README.md @@ -92,10 +92,10 @@ libwolfssl: kernel global random_bytes handlers installed. ``` Additionally, `/proc/crypto` will advertise that the FIPS DRBG is installed at -highest priority "-with-global-replace": +highest priority, with "-wolfentropy" and/or "-rdseed", and "-with-global-replace": ```ini name : stdrng -driver : sha2-256-drbg-nopr-wolfcrypt-fips-140-3-with-global-replace +driver : sha2-256-drbg-nopr-wolfentropy-wolfcrypt-fips-140-3-with-global-replace module : libwolfssl priority : 100000 refcnt : 2 diff --git a/linuxkm/linuxkm_memory.c b/linuxkm/linuxkm_memory.c index 542933c2a23..3448cc6de8f 100644 --- a/linuxkm/linuxkm_memory.c +++ b/linuxkm/linuxkm_memory.c @@ -354,23 +354,23 @@ ssize_t wc_reloc_normalize_text( #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, + RELOC_DEBUG_PRINTF("WARNING: normalized value is out of bounds (%s0x%llx) at index %lld, text offset 0x%x, reloc type %s, " + "dest seg .%s_wolfcrypt, offset from text to dest segment %s0x%llx, raw dest addr %s0x%llx, " + "seg span 0x%llx - 0x%llx, seg size 0x%llx, text base 0x%llx\n", + (long long)reloc_buf < 0 ? "-" : "", + (long long)reloc_buf < 0 ? -(long long)reloc_buf : (long long)reloc_buf, + (long long)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); + seg_beg < seg_map->text_start ? (unsigned long long)seg_map->text_start - seg_beg : seg_beg - (unsigned long long)seg_map->text_start, + (layout->is_signed && ((long long)raw_dest_addr < 0)) ? "-" : "", + (layout->is_signed && ((long long)raw_dest_addr < 0)) ? (unsigned long long)-(long long)raw_dest_addr : raw_dest_addr, + (unsigned long long)seg_beg, + (unsigned long long)seg_end, + (unsigned long long)(seg_end - seg_beg), + (unsigned long long)seg_map->text_start); } #endif } diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index 791ce2e804c..9061308fb5f 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -459,6 +459,7 @@ #ifndef WC_CONTAINERIZE_THIS #include #include + #include #include #endif @@ -508,6 +509,10 @@ #endif /* !WOLFCRYPT_ONLY */ #endif /* !WC_CONTAINERIZE_THIS */ + #ifdef WC_LINUXKM_SUPPORT_DUMP_TO_FILE + #include + #include + #endif #include #include #if __has_include() diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index a116bd2ee00..44e8a35dc78 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -239,6 +239,74 @@ WC_MAYBE_UNUSED static int linuxkm_lkcapi_sysfs_deinstall_node(struct kobj_attri return 0; } +#ifdef WC_LINUXKM_SUPPORT_DUMP_TO_FILE +static ssize_t dump_to_file(const char *path, const u8 *buf, size_t buf_len) +{ + loff_t pos = 0; + struct file *fp; + ssize_t ret; + char tmp; + + if (buf_len == 0) { + pr_info("libwolfssl: dump_to_file() called with buf_len == 0. Not dumping.\n"); + return 0; + } + + WC_SANITIZE_DISABLE(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0) + ret = probe_kernel_read(&tmp, buf, 1); + if (ret == 0) + ret = probe_kernel_read(&tmp, buf + buf_len - 1, 1); +#else + ret = copy_from_kernel_nofault(&tmp, buf, 1); + if (ret == 0) + ret = copy_from_kernel_nofault(&tmp, buf + buf_len - 1, 1); +#endif + WC_SANITIZE_ENABLE(); + if (ret != 0) { + pr_err("libwolfssl: cannot safely read from buffer %px: %d\n", buf, (int)ret); + return ret; + } + + fp = filp_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("libwolfssl: cannot open %s: %ld\n", path, PTR_ERR(fp)); + return PTR_ERR(fp); + } + + WC_SANITIZE_DISABLE(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + /* kernel_write() exported by 7bb307e894d51 */ + ret = kernel_write(fp, buf, buf_len, &pos); +#else + ret = vfs_write(fp, buf, buf_len, &pos); +#endif + WC_SANITIZE_ENABLE(); + + filp_close(fp, NULL); + + if (ret < 0) + pr_err("libwolfssl: write to %s failed: %zd\n", path, ret); + else if ((size_t)ret != buf_len) + pr_warn("libwolfssl: short write to %s: %zd of %zu bytes\n", path, ret, buf_len); + + return ret; +} + +static char *text_dump_path; +static char *rodata_dump_path; + + /* indent these so they don't look like flush-left function calls. */ + module_param(text_dump_path, charp, 0444); + module_param(rodata_dump_path, charp, 0444); + +MODULE_PARM_DESC(text_dump_path, + "Path to dump live .wolfcrypt_text section to (e.g. /tmp/wc_text.bin)"); +MODULE_PARM_DESC(rodata_dump_path, + "Path to dump live .wolfcrypt_rodata section to (e.g. /tmp/wc_rodata.bin)"); + +#endif /* WC_LINUXKM_SUPPORT_DUMP_TO_FILE */ + #ifdef HAVE_FIPS static ssize_t FIPS_rerun_self_test_handler(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); @@ -503,6 +571,27 @@ static int wolfssl_init(void) return ret; #endif +#ifdef WC_LINUXKM_SUPPORT_DUMP_TO_FILE + +#ifdef WC_SYM_RELOC_TABLES + if (text_dump_path) { + if (dump_to_file(text_dump_path, (u8 *)__wc_text_start, (size_t)((uintptr_t)__wc_text_end - (uintptr_t)__wc_text_start)) == 0) + pr_info("libwolfssl: dumped .wolfcrypt_text (%zu bytes) to %s.\n", (size_t)((uintptr_t)__wc_text_end - (uintptr_t)__wc_text_start), text_dump_path); + } + if (rodata_dump_path) { + if (dump_to_file(rodata_dump_path, (u8 *)__wc_rodata_start, (size_t)(__wc_rodata_end - __wc_rodata_start)) == 0) + pr_info("libwolfssl: dumped .wolfcrypt_rodata (%zu bytes) to %s.\n", (size_t)((uintptr_t)__wc_rodata_end - (uintptr_t)__wc_rodata_start), text_dump_path); + } +#else + if ((text_dump_path != NULL) || + (rodata_dump_path != NULL)) + { + pr_info("libwolfssl: ignoring module dump path argument(s) -- module lacks WC_SYM_RELOC_TABLES.\n"); + } +#endif + +#endif /* WC_LINUXKM_SUPPORT_DUMP_TO_FILE */ + #ifdef WC_LINUXKM_TEST_INET_PTON { const char *src; diff --git a/linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch b/linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch new file mode 100644 index 00000000000..5c421d68a99 --- /dev/null +++ b/linuxkm/patches/7.0/WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS-7v0.patch @@ -0,0 +1,484 @@ +--- linux-next-9999/drivers/char/random.c.dist 2026-02-27 20:38:28.210284927 +0000 ++++ linux-next-9999/drivers/char/random.c 2026-02-27 20:39:23.770059819 +0000 +@@ -67,6 +67,265 @@ + #include + #include + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ ++#include ++ ++static atomic_long_t random_bytes_cb_owner = ++ ATOMIC_INIT((long)NULL); ++static atomic_t random_bytes_cb_refcnt = ++ ATOMIC_INIT(0); /* 0 if unregistered, 1 if no calls in flight. */ ++static _get_random_bytes_cb_t _get_random_bytes_cb; ++static get_random_bytes_user_cb_t get_random_bytes_user_cb; ++static crng_ready_cb_t crng_ready_cb; ++static mix_pool_bytes_cb_t mix_pool_bytes_cb; ++static credit_init_bits_cb_t credit_init_bits_cb; ++static crng_reseed_cb_t crng_reseed_cb; ++ ++int wolfssl_linuxkm_register_random_bytes_handlers( ++ struct module *new_random_bytes_cb_owner, ++ const struct wolfssl_linuxkm_random_bytes_handlers *handlers) ++{ ++ if ((new_random_bytes_cb_owner == NULL) || ++ (handlers == NULL) || ++ (handlers->_get_random_bytes == NULL) || ++ (handlers->get_random_bytes_user == NULL)) { ++ return -EINVAL; ++ } ++ ++ /* random_bytes_cb_owner is used to enforce serialization of ++ * wolfssl_register_random_bytes_handlers() and ++ * wolfssl_unregister_random_bytes_handlers(). ++ */ ++ if (atomic_long_cmpxchg(&random_bytes_cb_owner, ++ (long)NULL, ++ (long)new_random_bytes_cb_owner) ++ != (long)NULL) { ++ return -EBUSY; ++ } ++ ++ { ++ int current_random_bytes_cb_refcnt = atomic_read(&random_bytes_cb_refcnt); ++ ++ if (current_random_bytes_cb_refcnt) { ++ pr_err("BUG: random_bytes_cb_refcnt == %d with null random_bytes_cb_owner", ++ current_random_bytes_cb_refcnt); ++ atomic_long_set(&random_bytes_cb_owner, (long)NULL); ++ return -EFAULT; ++ } ++ } ++ ++ if (!try_module_get(new_random_bytes_cb_owner)) { ++ atomic_long_set(&random_bytes_cb_owner, (long)NULL); ++ return -ENODEV; ++ } ++ ++ _get_random_bytes_cb = handlers->_get_random_bytes; ++ get_random_bytes_user_cb = handlers->get_random_bytes_user; ++ crng_ready_cb = handlers->crng_ready; ++ mix_pool_bytes_cb = handlers->mix_pool_bytes; ++ credit_init_bits_cb = handlers->credit_init_bits; ++ crng_reseed_cb = handlers->crng_reseed; ++ ++ barrier(); ++ atomic_set_release(&random_bytes_cb_refcnt, 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(wolfssl_linuxkm_register_random_bytes_handlers); ++ ++int wolfssl_linuxkm_unregister_random_bytes_handlers(void) ++{ ++ int current_random_bytes_cb_refcnt; ++ int n_tries; ++ ++ if (atomic_long_read(&random_bytes_cb_owner) == 0) ++ return -ENODEV; ++ ++ /* we're racing the kernel at large to try to catch random_bytes_cb_refcnt ++ * with no callers in flight -- retry and relax up to 100 times. ++ */ ++ for (n_tries = 0; n_tries < 100; ++n_tries) { ++ current_random_bytes_cb_refcnt = atomic_cmpxchg(&random_bytes_cb_refcnt, 1, 0); ++ if (current_random_bytes_cb_refcnt == 1) ++ break; ++ if (current_random_bytes_cb_refcnt < 0) { ++ pr_err("BUG: random_bytes_cb_refcnt is %d in %s.", ++ current_random_bytes_cb_refcnt, __func__); ++ break; ++ } ++ if (msleep_interruptible(10) != 0) ++ return -EINTR; ++ } ++ if (current_random_bytes_cb_refcnt != 1) { ++ pr_warn("WARNING: %s called with random_bytes_cb_refcnt == %d", __func__, ++ current_random_bytes_cb_refcnt); ++ return -EBUSY; ++ } ++ ++ _get_random_bytes_cb = NULL; ++ get_random_bytes_user_cb = NULL; ++ crng_ready_cb = NULL; ++ mix_pool_bytes_cb = NULL; ++ credit_init_bits_cb = NULL; ++ crng_reseed_cb = NULL; ++ ++ module_put((struct module *)atomic_long_read(&random_bytes_cb_owner)); ++ barrier(); ++ atomic_long_set(&random_bytes_cb_owner, (long)NULL); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(wolfssl_linuxkm_unregister_random_bytes_handlers); ++ ++static __always_inline int reserve_random_bytes_cb(void) ++{ ++ int current_random_bytes_cb_refcnt = ++ atomic_read_acquire(&random_bytes_cb_refcnt); ++ ++ if (current_random_bytes_cb_refcnt == 0) ++ return -ENODEV; ++ ++ if (current_random_bytes_cb_refcnt < 0) { ++ pr_err("BUG: random_bytes_cb_refcnt is %d in %s.", ++ current_random_bytes_cb_refcnt, __func__); ++ return -EFAULT; ++ } ++ ++ for (;;) { ++ int orig_random_bytes_cb_refcnt = ++ atomic_cmpxchg( ++ &random_bytes_cb_refcnt, ++ current_random_bytes_cb_refcnt, ++ current_random_bytes_cb_refcnt + 1); ++ if (orig_random_bytes_cb_refcnt == current_random_bytes_cb_refcnt) ++ return 0; ++ else if (orig_random_bytes_cb_refcnt == 0) ++ return -ENODEV; ++ current_random_bytes_cb_refcnt = orig_random_bytes_cb_refcnt; ++ } ++ ++ __builtin_unreachable(); ++} ++ ++static __always_inline void release_random_bytes_cb(void) ++{ ++ atomic_dec(&random_bytes_cb_refcnt); ++} ++ ++static inline int call__get_random_bytes_cb(void *buf, size_t len) ++{ ++ int ret; ++ ++ if (_get_random_bytes_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = _get_random_bytes_cb(buf, len); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline ssize_t call_get_random_bytes_user_cb(struct iov_iter *iter) ++{ ++ ssize_t ret; ++ ++ if (get_random_bytes_user_cb == NULL) ++ return -ECANCELED; ++ ++ ret = (ssize_t)reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = get_random_bytes_user_cb(iter); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline bool call_crng_ready_cb(void) ++{ ++ bool ret; ++ ++ /* Null crng_ready_cb signifies that the DRBG is always ready, i.e. that if ++ * called, it will always have or obtain sufficient entropy to fulfill the ++ * call. ++ */ ++ if (crng_ready_cb == NULL) ++ return 1; ++ ++ if (reserve_random_bytes_cb() != 0) ++ return 0; ++ ++ ret = crng_ready_cb(); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_mix_pool_bytes_cb(const void *buf, size_t len) ++{ ++ int ret; ++ ++ if (mix_pool_bytes_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = mix_pool_bytes_cb(buf, len); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_credit_init_bits_cb(size_t bits) ++{ ++ int ret; ++ ++ if (credit_init_bits_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = credit_init_bits_cb(bits); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++static inline int call_crng_reseed_cb(void) ++{ ++ int ret; ++ ++ if (crng_reseed_cb == NULL) ++ return -ENODEV; ++ ++ ret = reserve_random_bytes_cb(); ++ if (ret) ++ return ret; ++ ++ ret = crng_reseed_cb(); ++ ++ release_random_bytes_cb(); ++ ++ return ret; ++} ++ ++#endif /* WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS */ ++ + /********************************************************************* + * + * Initialization and readiness waiting. +@@ -87,7 +346,16 @@ static enum { + CRNG_READY = 2 /* Fully initialized with POOL_READY_BITS collected */ + } crng_init __read_mostly = CRNG_EMPTY; + static DEFINE_STATIC_KEY_FALSE(crng_is_ready); ++ + #define crng_ready() (static_branch_likely(&crng_is_ready) || crng_init >= CRNG_READY) ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define crng_ready_by_cb() (atomic_read(&random_bytes_cb_refcnt) && call_crng_ready_cb()) ++ #define crng_ready_maybe_cb() (atomic_read(&random_bytes_cb_refcnt) ? \ ++ (call_crng_ready_cb() || crng_ready()) : crng_ready()) ++#else ++ #define crng_ready_maybe_cb() crng_ready() ++#endif ++ + /* Various types of waiters for crng_init->CRNG_READY transition. */ + static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait); + static struct fasync_struct *fasync; +@@ -111,7 +379,7 @@ MODULE_PARM_DESC(ratelimit_disable, "Dis + */ + bool rng_is_initialized(void) + { +- return crng_ready(); ++ return crng_ready_maybe_cb(); + } + EXPORT_SYMBOL(rng_is_initialized); + +@@ -135,11 +403,11 @@ static void try_to_generate_entropy(void + */ + int wait_for_random_bytes(void) + { +- while (!crng_ready()) { ++ while (!crng_ready_maybe_cb()) { + int ret; + + try_to_generate_entropy(); +- ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready(), HZ); ++ ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready_maybe_cb(), HZ); + if (ret) + return ret > 0 ? 0 : ret; + } +@@ -159,7 +427,7 @@ int __cold execute_with_initialized_rng( + int ret = 0; + + spin_lock_irqsave(&random_ready_notifier.lock, flags); +- if (crng_ready()) ++ if (crng_ready_maybe_cb()) + nb->notifier_call(nb, 0, NULL); + else + ret = raw_notifier_chain_register((struct raw_notifier_head *)&random_ready_notifier.head, nb); +@@ -395,6 +663,14 @@ static void _get_random_bytes(void *buf, + if (!len) + return; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ /* If call__get_random_bytes_cb() doesn't succeed, flow continues to ++ * the native implementation. _get_random_bytes() must succeed. ++ */ ++ if (call__get_random_bytes_cb(buf, len) == 0) ++ return; ++#endif ++ + first_block_len = min_t(size_t, 32, len); + crng_make_state(&chacha_state, buf, first_block_len); + len -= first_block_len; +@@ -440,6 +716,18 @@ static ssize_t get_random_bytes_user(str + if (unlikely(!iov_iter_count(iter))) + return 0; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ { ++ ssize_t cb_ret = call_get_random_bytes_user_cb(iter); ++ /* If the callback returns -ECANCELED, that signals that iter is ++ * still intact, and flow can safely continue to the native ++ * implementation. ++ */ ++ if (cb_ret != -ECANCELED) ++ return cb_ret; ++ } ++#endif ++ + /* + * Immediately overwrite the ChaCha key at index 4 with random + * bytes, in case userspace causes copy_to_iter() below to sleep +@@ -515,7 +803,7 @@ type get_random_ ##type(void) \ + struct batch_ ##type *batch; \ + unsigned long next_gen; \ + \ +- if (!crng_ready()) { \ ++ if (!crng_ready_maybe_cb()) { \ + _get_random_bytes(&ret, sizeof(ret)); \ + return ret; \ + } \ +@@ -651,6 +939,11 @@ static void mix_pool_bytes(const void *b + { + unsigned long flags; + ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ (void)call_mix_pool_bytes_cb(buf, len); ++ /* continue to mix into native pool too. */ ++#endif ++ + spin_lock_irqsave(&input_pool.lock, flags); + _mix_pool_bytes(buf, len); + spin_unlock_irqrestore(&input_pool.lock, flags); +@@ -710,7 +1003,13 @@ static void extract_entropy(void *buf, s + memzero_explicit(&block, sizeof(block)); + } + +-#define credit_init_bits(bits) if (!crng_ready()) _credit_init_bits(bits) ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define credit_init_bits(bits) do { (void)call_credit_init_bits_cb(bits); \ ++ if (!crng_ready()) \ ++ _credit_init_bits(bits); } while (0) ++#else ++ #define credit_init_bits(bits) do { if (!crng_ready()) _credit_init_bits(bits); } while (0) ++#endif + + static void __cold _credit_init_bits(size_t bits) + { +@@ -1396,7 +1695,7 @@ SYSCALL_DEFINE3(getrandom, char __user * + if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM)) + return -EINVAL; + +- if (!crng_ready() && !(flags & GRND_INSECURE)) { ++ if (!crng_ready_maybe_cb() && !(flags & GRND_INSECURE)) { + if (flags & GRND_NONBLOCK) + return -EAGAIN; + ret = wait_for_random_bytes(); +@@ -1412,6 +1711,10 @@ SYSCALL_DEFINE3(getrandom, char __user * + + static __poll_t random_poll(struct file *file, poll_table *wait) + { ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ if (crng_ready_by_cb()) ++ return EPOLLIN | EPOLLRDNORM; ++#endif + poll_wait(file, &crng_init_wait, wait); + return crng_ready() ? EPOLLIN | EPOLLRDNORM : EPOLLOUT | EPOLLWRNORM; + } +@@ -1457,10 +1760,10 @@ static ssize_t urandom_read_iter(struct + * Opportunistically attempt to initialize the RNG on platforms that + * have fast cycle counters, but don't (for now) require it to succeed. + */ +- if (!crng_ready()) ++ if (!crng_ready_maybe_cb()) + try_to_generate_entropy(); + +- if (!crng_ready()) { ++ if (!crng_ready_maybe_cb()) { + if (!ratelimit_disable && maxwarn <= 0) + ratelimit_state_inc_miss(&urandom_warning); + else if (ratelimit_disable || __ratelimit(&urandom_warning)) { +@@ -1477,7 +1780,7 @@ static ssize_t random_read_iter(struct k + { + int ret; + +- if (!crng_ready() && ++ if (!crng_ready_by_cb() && + ((kiocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) || + (kiocb->ki_filp->f_flags & O_NONBLOCK))) + return -EAGAIN; +@@ -1542,6 +1845,14 @@ static long random_ioctl(struct file *f, + case RNDRESEEDCRNG: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; ++#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ /* continue to reseed native crng too. */ ++ if (call_crng_reseed_cb() == 0) { ++ if (crng_ready()) ++ crng_reseed(NULL); ++ return 0; ++ } ++#endif + if (!crng_ready()) + return -ENODATA; + crng_reseed(NULL); +--- linux-next-9999/include/linux/random.h.dist 2026-02-27 20:38:33.018159190 +0000 ++++ linux-next-9999/include/linux/random.h 2026-02-27 20:39:23.770406607 +0000 +@@ -139,4 +139,37 @@ int random_online_cpu(unsigned int cpu); + extern const struct file_operations random_fops, urandom_fops; + #endif + ++#ifndef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS ++ #define WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS 1 ++#endif ++ ++typedef int (*_get_random_bytes_cb_t)(void *buf, size_t len); ++struct iov_iter; ++/* kernels >= 5.17.0 use get_random_bytes_user() */ ++typedef ssize_t (*get_random_bytes_user_cb_t)(struct iov_iter *iter); ++/* kernels < 5.17.0 use extract_crng_user(), though some LTS kernels, ++ * e.g. 5.10.236, have the 5.17+ architecture backported. ++ */ ++typedef ssize_t (*extract_crng_user_cb_t)(void __user *buf, size_t nbytes); ++typedef bool (*crng_ready_cb_t)(void); ++typedef int (*mix_pool_bytes_cb_t)(const void *buf, size_t len); ++typedef int (*credit_init_bits_cb_t)(size_t bits); ++typedef int (*crng_reseed_cb_t)(void); ++ ++struct wolfssl_linuxkm_random_bytes_handlers { ++ _get_random_bytes_cb_t _get_random_bytes; ++ get_random_bytes_user_cb_t get_random_bytes_user; ++ extract_crng_user_cb_t extract_crng_user; ++ crng_ready_cb_t crng_ready; ++ mix_pool_bytes_cb_t mix_pool_bytes; ++ credit_init_bits_cb_t credit_init_bits; ++ crng_reseed_cb_t crng_reseed; ++}; ++ ++int wolfssl_linuxkm_register_random_bytes_handlers( ++ struct module *new_random_bytes_cb_owner, ++ const struct wolfssl_linuxkm_random_bytes_handlers *handlers); ++ ++int wolfssl_linuxkm_unregister_random_bytes_handlers(void); ++ + #endif /* _LINUX_RANDOM_H */