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.
This commit is contained in:
Daniel Pouzzner
2026-02-28 00:25:06 -06:00
parent 76bc6e337b
commit d22175ae37
8 changed files with 661 additions and 53 deletions
+1
View File
@@ -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
+1 -1
View File
@@ -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
+66 -37
View File
@@ -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'
@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'
@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.'
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}"
linuxkm-fips-hash: libwolfssl-user-build/src/.libs/libwolfssl.so linuxkm-fips-hash.c
.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'
@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 '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'
+2 -2
View File
@@ -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
+13 -13
View File
@@ -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
}
+5
View File
@@ -459,6 +459,7 @@
#ifndef WC_CONTAINERIZE_THIS
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#endif
@@ -508,6 +509,10 @@
#endif /* !WOLFCRYPT_ONLY */
#endif /* !WC_CONTAINERIZE_THIS */
#ifdef WC_LINUXKM_SUPPORT_DUMP_TO_FILE
#include <linux/fs.h>
#include <linux/uaccess.h>
#endif
#include <linux/slab.h>
#include <linux/sched.h>
#if __has_include(<linux/sched/task_stack.h>)
+89
View File
@@ -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;
@@ -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 <asm/irq_regs.h>
#include <asm/io.h>
+#ifdef WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS
+
+#include <linux/delay.h>
+
+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 */