Browse Source

--enable-linuxkm-pie (FIPS Linux kernel module) (#4276)

* Adds `--enable-linuxkm-pie` and associated infrastructure, to support FIPS mode in the Linux kernel module.
* Adds `tests/api.c` missing (void) arglist to `test_SSL_CIPHER_get_xxx()`.
Daniel Pouzzner 2 years ago
parent
commit
3226e69649

+ 5 - 5
Makefile.am

@@ -207,20 +207,20 @@ if BUILD_LINUXKM
     DIST_SUBDIRS_OPT += linuxkm
 
     export KERNEL_ROOT KERNEL_ARCH KERNEL_EXTRA_CFLAGS AM_CFLAGS CFLAGS AM_CCASFLAGS CCASFLAGS \
-        src_libwolfssl_la_OBJECTS ENABLED_CRYPT_TESTS ENABLED_ASM CFLAGS_FPU_DISABLE \
-        CFLAGS_FPU_ENABLE CFLAGS_SIMD_DISABLE CFLAGS_SIMD_ENABLE \
+        src_libwolfssl_la_OBJECTS ENABLED_CRYPT_TESTS ENABLED_LINUXKM_PIE ENABLED_ASM \
+        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
 
 module:
-	+make -C linuxkm libwolfssl.ko
+	+$(MAKE) -C linuxkm libwolfssl.ko
 
 clean_module:
-	+make -C linuxkm clean
+	+$(MAKE) -C linuxkm clean
 
 install_module modules_install:
-	+make -C linuxkm modules_install
+	+$(MAKE) -C linuxkm modules_install
 
 endif
 

+ 17 - 5
configure.ac

@@ -241,6 +241,21 @@ AC_ARG_ENABLE([linuxkm-defaults],
     [ENABLED_LINUXKM_DEFAULTS=$ENABLED_LINUXKM]
     )
 
+AC_ARG_ENABLE([linuxkm-pie],
+    [AS_HELP_STRING([--enable-linuxkm-pie],[Enable relocatable object build of Linux kernel module (default: disabled)])],
+    [ENABLED_LINUXKM_PIE=$enableval],
+    [ENABLED_LINUXKM_PIE=$ENABLED_FIPS]
+    )
+if test "$ENABLED_LINUXKM_PIE" = "yes"
+then
+    AM_CFLAGS="$AM_CFLAGS -DHAVE_LINUXKM_PIE_SUPPORT"
+elif test "$ENABLED_FIPS" = yes
+then
+    AC_MSG_ERROR([FIPS requires linuxkm-pie.])
+fi
+AC_SUBST([ENABLED_LINUXKM_PIE])
+
+
 if test "$ENABLED_LINUXKM_DEFAULTS" = "yes"
 then
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DH_CONST -DWOLFSSL_SP_MOD_WORD_RP -DWOLFSSL_OLD_PRIME_CHECK -DWOLFSSL_SP_DIV_64 -DWOLFSSL_SP_DIV_WORD_HALF -DWOLFSSL_SMALL_STACK_STATIC -DWOLFSSL_TEST_SUBROUTINE=static"
@@ -6628,10 +6643,6 @@ if test "x$ENABLED_LINUXKM" = "xyes"; then
     if test "$ENABLED_IOPOOL" = "yes"; then
         AC_MSG_ERROR([--enable-iopool is incompatible with --enable-linuxkm.])
     fi
-    #FIPS currently depends on thread-local storage
-    if test "$ENABLED_FIPS" = "yes"; then
-        AC_MSG_ERROR([--enable-fips is incompatible with --enable-linuxkm.])
-    fi
     if test "$ENABLED_EXAMPLES" = "yes"; then
         AC_MSG_ERROR([--enable-examples is incompatible with --enable-linuxkm.])
     fi
@@ -6953,8 +6964,9 @@ echo "   * Auto-vectorize C flags:     $CFLAGS_AUTO_VECTORIZE_ENABLE" && \
 echo "   * SIMD enable as flags:       $ASFLAGS_FPU_DISABLE_SIMD_ENABLE" && \
 echo "   * FPU enable as flags:        $ASFLAGS_FPU_ENABLE_SIMD_DISABLE" && \
 echo "   * SIMD+FPU disable as flags:  $ASFLAGS_FPUSIMD_DISABLE" && \
+echo "   * SIMD+FPU enable as flags:   $ASFLAGS_FPUSIMD_ENABLE" && \
+echo "   * Linux kernel module PIE:    $ENABLED_LINUXKM_PIE"
 
-echo "   * SIMD+FPU enable as flags:   $ASFLAGS_FPUSIMD_ENABLE"
 echo "   * Debug enabled:              $ax_enable_debug"
 echo "   * Coverage enabled:           $ax_enable_coverage"
 echo "   * Warnings as failure:        $ac_cv_warnings_as_errors"

+ 81 - 5
linuxkm/Kbuild

@@ -29,11 +29,19 @@ ifeq "$(WOLFSSL_CFLAGS)" ""
     $(error $$WOLFSSL_CFLAGS is unset.)
 endif
 
-WOLFSSL_CFLAGS += -Wframe-larger-than=$(MAX_STACK_FRAME_SIZE) -mpreferred-stack-boundary=4
+WOLFSSL_CFLAGS += -ffreestanding -Wframe-larger-than=$(MAX_STACK_FRAME_SIZE)
+
+ifeq "$(KERNEL_ARCH)" "x86"
+    WOLFSSL_CFLAGS += -mpreferred-stack-boundary=4
+endif
 
 obj-m := libwolfssl.o
 
-WOLFSSL_OBJ_TARGETS=$(patsubst %, $(obj)/%, $(WOLFSSL_OBJ_FILES))
+WOLFSSL_OBJ_TARGETS := $(patsubst %, $(obj)/%, $(WOLFSSL_OBJ_FILES))
+
+ifeq "$(ENABLED_LINUXKM_PIE)" "yes"
+    WOLFCRYPT_PIE_FILES := $(patsubst %, $(obj)/%, $(WOLFCRYPT_PIE_FILES))
+endif
 
 $(obj)/linuxkm/module_exports.o: $(WOLFSSL_OBJ_TARGETS)
 
@@ -58,17 +66,33 @@ else
     WOLFSSL_CFLAGS_YES_VECTOR_INSNS := $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS)
 endif
 
-ccflags-y = $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS)
+ccflags-y := $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS)
 
 $(obj)/libwolfssl.mod.o: ccflags-y :=
 $(obj)/wolfcrypt/test/test.o: ccflags-y += -DNO_MAIN_DRIVER
 
 $(obj)/wolfcrypt/src/aes.o: ccflags-y = $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_YES_VECTOR_INSNS)
 
+ifeq "$(ENABLED_LINUXKM_PIE)" "yes"
+    PIE_FLAGS := -fPIE -fno-stack-protector -fno-toplevel-reorder
+    PIE_SUPPORT_FLAGS := -DUSE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE
+    ifeq "$(KERNEL_ARCH)" "x86"
+        PIE_FLAGS += -mcmodel=small -mindirect-branch=keep -mfunction-return=keep
+    endif
+    ifeq "$(KERNEL_ARCH)" "mips"
+        PIE_FLAGS += -mabicalls
+    endif
+    $(WOLFCRYPT_PIE_FILES): ccflags-y += $(PIE_SUPPORT_FLAGS) $(PIE_FLAGS)
+    $(WOLFCRYPT_PIE_FILES): ccflags-remove-y += -pg
+    # disabling retpoline generation leads to profuse warnings without this:
+    $(WOLFCRYPT_PIE_FILES): OBJECT_FILES_NON_STANDARD := y
+    $(obj)/linuxkm/module_hooks.o: ccflags-y += $(PIE_SUPPORT_FLAGS)
+endif
+
 asflags-y := $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPUSIMD_DISABLE)
 
-# after the C wrapper for a vectorized algorithm has been equipped with {SAVE,RESTORE}_VECTOR_REGISTERS(),
-# it can be safely included here:
+# vectorized algorithms equipped with {SAVE,RESTORE}_VECTOR_REGISTERS()
+# can be safely included here:
 $(obj)/wolfcrypt/src/aes_asm.o: asflags-y = $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE)
 $(obj)/wolfcrypt/src/aes_gcm_asm.o: asflags-y = $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU_DISABLE_SIMD_ENABLE)
 
@@ -76,6 +100,58 @@ $(obj)/wolfcrypt/src/aes_gcm_asm.o: asflags-y = $(WOLFSSL_ASFLAGS) $(ASFLAGS_FPU
 $(obj)/wolfcrypt/src/aes_asm.o: OBJECT_FILES_NON_STANDARD := y
 $(obj)/wolfcrypt/src/aes_gcm_asm.o: OBJECT_FILES_NON_STANDARD := y
 
+ifeq "$(ENABLED_LINUXKM_PIE)" "yes"
+
+rename-pie-text-and-data-sections: $(WOLFSSL_OBJ_TARGETS)
+
+ifndef NM
+    NM := nm
+endif
+
+ifndef READELF
+    READELF := readelf
+endif
+
+ifndef OBJCOPY
+    OBJCOPY := objcopy
+endif
+
+.PHONY: rename-pie-text-and-data-sections
+rename-pie-text-and-data-sections:
+ifneq "$(quiet)" "silent_"
+	@echo -n '  Checking wolfCrypt for unresolved symbols and forbidden relocations... '
+endif
+	@cd "$(obj)" || exit $$?; \
+	$(LD) -relocatable -o wolfcrypt_test_link.o $(WOLFCRYPT_PIE_FILES) || exit $$?; \
+	undefined=$$($(NM) --undefined-only wolfcrypt_test_link.o) || exit $$?; \
+	GOT_relocs=$$($(READELF) --relocs --wide wolfcrypt_test_link.o | egrep '^[^ ]+ +[^ ]+ +[^ ]*GOT[^ ]* ') || [ $$? = 1 ] || exit 2; \
+	rm wolfcrypt_test_link.o; \
+	if [ -n "$$undefined" ]; then \
+	    echo "wolfCrypt container has unresolved symbols:" 1>&2; \
+	    echo "$$undefined" 1>&2; \
+	    exit 1; \
+	fi; \
+	if [ -n "$$GOT_relocs" ]; then \
+	    echo "wolfCrypt container has GOT relocations (non-local function address used as operand?):" 1>&2; \
+	    echo "$$GOT_relocs" 1>&2; \
+	    exit 1; \
+	fi
+ifneq "$(quiet)" "silent_"
+	@echo 'OK.'
+endif
+	@cd "$(obj)" || exit $$?; \
+	for file in $(WOLFCRYPT_PIE_FILES); do \
+	    $(OBJCOPY) --rename-section .text=.text.wolfcrypt --rename-section .data=.data.wolfcrypt "$$file" || exit $$?; \
+	done
+ifneq "$(quiet)" "silent_"
+	@echo '  wolfCrypt .{text,data} sections containerized to .{text,data}.wolfcrypt'
+endif
+
+$(src)/linuxkm/module_exports.c: rename-pie-text-and-data-sections
+
+endif
+
+
 # auto-generate the exported symbol list, leveraging the WOLFSSL_API visibility tags.
 # exclude symbols that don't match wc_* or wolf*.
 $(src)/linuxkm/module_exports.c: $(src)/linuxkm/module_exports.c.template $(WOLFSSL_OBJ_TARGETS)

+ 10 - 5
linuxkm/Makefile

@@ -33,7 +33,7 @@ ifndef SRC_TOP
     SRC_TOP=$(shell dirname $(MODULE_TOP))
 endif
 
-WOLFSSL_CFLAGS=-DHAVE_CONFIG_H -I$(SRC_TOP) -DBUILDING_WOLFSSL $(AM_CFLAGS) $(CFLAGS) -Wno-declaration-after-statement -Wno-redundant-decls -ffreestanding
+WOLFSSL_CFLAGS=-DHAVE_CONFIG_H -I$(SRC_TOP) -DBUILDING_WOLFSSL $(AM_CFLAGS) $(CFLAGS) -Wno-declaration-after-statement -Wno-redundant-decls
 ifdef KERNEL_EXTRA_CFLAGS
     WOLFSSL_CFLAGS += $(KERNEL_EXTRA_CFLAGS)
 endif
@@ -48,7 +48,12 @@ else
     WOLFSSL_CFLAGS+=-DNO_CRYPT_TEST
 endif
 
-export WOLFSSL_CFLAGS WOLFSSL_ASFLAGS WOLFSSL_OBJ_FILES
+ifeq "$(ENABLED_LINUXKM_PIE)" "yes"
+    WOLFCRYPT_PIE_FILES := linuxkm/pie_first.o $(filter wolfcrypt/%,$(WOLFSSL_OBJ_FILES)) linuxkm/pie_redirect_table.o linuxkm/pie_last.o
+    WOLFSSL_OBJ_FILES := $(WOLFCRYPT_PIE_FILES) $(filter-out $(WOLFCRYPT_PIE_FILES),$(WOLFSSL_OBJ_FILES))
+endif
+
+export WOLFSSL_CFLAGS WOLFSSL_ASFLAGS WOLFSSL_OBJ_FILES WOLFCRYPT_PIE_FILES
 
 libwolfssl.ko:
 	@if test -z "$(KERNEL_ROOT)"; then echo '$$KERNEL_ROOT is unset' >&2; exit 1; fi
@@ -56,15 +61,15 @@ libwolfssl.ko:
 	@if test -z "$(src_libwolfssl_la_OBJECTS)"; then echo '$$src_libwolfssl_la_OBJECTS is unset.' >&2; exit 1; fi
 	@mkdir -p linuxkm src wolfcrypt/src wolfcrypt/test
 	@if test ! -h $(SRC_TOP)/Kbuild; then ln -s $(MODULE_TOP)/Kbuild $(SRC_TOP)/Kbuild; fi
-	+make -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP)
+	+$(MAKE) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP)
 
 .PHONY: install modules_install
 install modules_install:
-	+make -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install
+	+$(MAKE) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) INSTALL_MOD_DIR=wolfssl modules_install
 
 .PHONY: clean
 clean:
-	+make -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) clean
+	+$(MAKE) -C $(KERNEL_ROOT) M=$(MODULE_TOP) src=$(SRC_TOP) clean
 
 .PHONY: dist
 dist:

+ 4 - 1
linuxkm/include.am

@@ -7,4 +7,7 @@ EXTRA_DIST += m4/ax_linuxkm.m4 \
 	      linuxkm/Makefile \
 	      linuxkm/get_thread_size.c \
 	      linuxkm/module_hooks.c \
-	      linuxkm/module_exports.c.template
+	      linuxkm/module_exports.c.template \
+	      linuxkm/pie_first.c \
+	      linuxkm/pie_redirect_table.c \
+	      linuxkm/pie_last.c

+ 251 - 3
linuxkm/module_hooks.c

@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
+#define FIPS_NO_WRAPPERS
+
 #ifdef HAVE_CONFIG_H
     #include <config.h>
 #endif
@@ -26,6 +28,9 @@
 #include <wolfssl/wolfcrypt/settings.h>
 #include <wolfssl/wolfcrypt/error-crypt.h>
 #include <wolfssl/ssl.h>
+#ifdef HAVE_FIPS
+#include <wolfssl/wolfcrypt/fips_test.h>
+#endif
 #ifndef NO_CRYPT_TEST
 #include <wolfcrypt/test/test.h>
 #include <linux/delay.h>
@@ -50,6 +55,46 @@ static int libwolfssl_cleanup(void) {
     return ret;
 }
 
+#ifdef HAVE_LINUXKM_PIE_SUPPORT
+
+extern int wolfCrypt_PIE_first_function(void);
+extern int wolfCrypt_PIE_last_function(void);
+extern const unsigned int wolfCrypt_PIE_rodata_start[];
+extern const unsigned int wolfCrypt_PIE_rodata_end[];
+
+/* cheap portable ad-hoc hash function to confirm bitwise stability of the PIE
+ * binary image.
+ */
+static unsigned int hash_span(char *start, char *end) {
+    unsigned int sum = 1;
+    while (start < end) {
+        unsigned int rotate_by;
+        sum ^= *start++;
+        rotate_by = (sum ^ (sum >> 5)) & 31;
+        sum = (sum << rotate_by) | (sum >> (32 - rotate_by));
+    }
+    return sum;
+}
+
+#ifdef USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE
+extern struct wolfssl_linuxkm_pie_redirect_table wolfssl_linuxkm_pie_redirect_table;
+static int set_up_wolfssl_linuxkm_pie_redirect_table(void);
+#endif /* USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE */
+
+#endif /* HAVE_LINUXKM_PIE_SUPPORT */
+
+#ifdef HAVE_FIPS
+static void lkmFipsCb(int ok, int err, const char* hash)
+{
+    if ((! ok) || (err != 0))
+        pr_err("libwolfssl FIPS error: %s\n", wc_GetErrorString(err));
+    if (err == IN_CORE_FIPS_E) {
+        pr_err("In-core integrity hash check failure.\n");
+        pr_err("Update verifyCore[] in fips_test.c with new hash \"%s\" and rebuild.\n",
+               hash ? hash : "<null>");
+    }
+}
+#endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
 static int __init wolfssl_init(void)
@@ -58,17 +103,109 @@ static int wolfssl_init(void)
 #endif
 {
     int ret;
+
+#ifdef USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE
+    ret = set_up_wolfssl_linuxkm_pie_redirect_table();
+    if (ret < 0)
+        return ret;
+
+#endif
+
+#ifdef HAVE_LINUXKM_PIE_SUPPORT
+    {
+        char *pie_text_start = (char *)wolfCrypt_PIE_first_function;
+        char *pie_text_end = (char *)wolfCrypt_PIE_last_function;
+        char *pie_rodata_start = (char *)wolfCrypt_PIE_rodata_start;
+        char *pie_rodata_end = (char *)wolfCrypt_PIE_rodata_end;
+        unsigned int text_hash, rodata_hash;
+
+        if ((pie_text_start < pie_text_end) &&
+            (pie_text_start >= (char *)(THIS_MODULE->core_layout.base)) &&
+            (pie_text_end - (char *)(THIS_MODULE->core_layout.base) <= THIS_MODULE->core_layout.text_size))
+        {
+            text_hash = hash_span(pie_text_start, pie_text_end);
+        } else {
+            pr_info("out-of-bounds PIE fenceposts! pie_text_start=%px pie_text_end=%px (span=%lu)"
+                    " core_layout.base=%px text_end=%px\n",
+                    pie_text_start,
+                    pie_text_end,
+                    pie_text_end-pie_text_start,
+                    THIS_MODULE->core_layout.base,
+                    (char *)(THIS_MODULE->core_layout.base) + THIS_MODULE->core_layout.text_size);
+            text_hash = 0;
+        }
+
+        if ((pie_rodata_start < pie_rodata_end) &&
+            (pie_rodata_start >= (char *)(THIS_MODULE->core_layout.base) + THIS_MODULE->core_layout.text_size) &&
+            (pie_rodata_end - (char *)(THIS_MODULE->core_layout.base) <= THIS_MODULE->core_layout.ro_size))
+        {
+            rodata_hash = hash_span(pie_rodata_start, pie_rodata_end);
+        } else {
+            pr_info("out-of-bounds PIE fenceposts! pie_rodata_start=%px pie_rodata_end=%px (span=%lu)"
+                    " core_layout.base+core_layout.text_size=%px rodata_end=%px\n",
+                    pie_rodata_start,
+                    pie_rodata_end,
+                    pie_rodata_end-pie_rodata_start,
+                    (char *)(THIS_MODULE->core_layout.base) + THIS_MODULE->core_layout.text_size,
+                    (char *)(THIS_MODULE->core_layout.base) + THIS_MODULE->core_layout.ro_size);
+            rodata_hash = 0;
+        }
+
+        /* note, "%pK" conceals the actual layout information.  "%px" exposes
+         * the true module start address, which is potentially useful to an
+         * attacker.
+         */
+        pr_info("wolfCrypt container hashes (spans): %x (%lu) %x (%lu), module base %pK\n",
+                text_hash, pie_text_end-pie_text_start,
+                rodata_hash, pie_rodata_end-pie_rodata_start,
+                THIS_MODULE->core_layout.base);
+    }
+#endif /* HAVE_LINUXKM_PIE_SUPPORT */
+
+#ifdef HAVE_FIPS
+    ret = wolfCrypt_SetCb_fips(lkmFipsCb);
+    if (ret != 0) {
+        pr_err("wolfCrypt_SetCb_fips() failed: %s", wc_GetErrorString(ret));
+        return -ECANCELED;
+    }
+    fipsEntry();
+    ret = wolfCrypt_GetStatus_fips();
+    if (ret != 0) {
+        pr_err("wolfCrypt_GetStatus_fips() failed: %s", wc_GetErrorString(ret));
+        if (ret == IN_CORE_FIPS_E) {
+            const char *newhash = wolfCrypt_GetCoreHash_fips();
+            pr_err("Update verifyCore[] in fips_test.c with new hash \"%s\" and rebuild.\n",
+                   newhash ? newhash : "<null>");
+        }
+        return -ECANCELED;
+    }
+
+    pr_info("wolfCrypt FIPS ["
+
+#if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 3)
+            "ready"
+#elif defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 2) \
+    && defined(WOLFCRYPT_FIPS_RAND)
+            "140-2 rand"
+#elif defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 2)
+            "140-2"
+#else
+            "140"
+#endif
+            "] POST succeeded.\n");
+#endif /* HAVE_FIPS */
+
 #ifdef WOLFCRYPT_ONLY
     ret = wolfCrypt_Init();
     if (ret != 0) {
         pr_err("wolfCrypt_Init() failed: %s", wc_GetErrorString(ret));
-        return -ENOTRECOVERABLE;
+        return -ECANCELED;
     }
 #else
     ret = wolfSSL_Init();
     if (ret != WOLFSSL_SUCCESS) {
         pr_err("wolfSSL_Init() failed: %s", wc_GetErrorString(ret));
-        return -ENOTRECOVERABLE;
+        return -ECANCELED;
     }
 #endif
 
@@ -78,7 +215,7 @@ static int wolfssl_init(void)
         pr_err("wolfcrypt self-test failed with return code %d.", ret);
         (void)libwolfssl_cleanup();
         msleep(10);
-        return -ENOTRECOVERABLE;
+        return -ECANCELED;
     }
     pr_info("wolfCrypt self-test passed.\n");
 #endif
@@ -112,3 +249,114 @@ MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("https://www.wolfssl.com/");
 MODULE_DESCRIPTION("libwolfssl cryptographic and protocol facilities");
 MODULE_VERSION(LIBWOLFSSL_VERSION_STRING);
+
+#ifdef USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE
+
+static int set_up_wolfssl_linuxkm_pie_redirect_table(void) {
+    memset(
+        &wolfssl_linuxkm_pie_redirect_table,
+        0,
+        sizeof wolfssl_linuxkm_pie_redirect_table);
+
+#ifndef __ARCH_MEMCMP_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.memcmp = memcmp;
+#endif
+#ifndef __ARCH_MEMCPY_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.memcpy = memcpy;
+#endif
+#ifndef __ARCH_MEMSET_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.memset = memset;
+#endif
+#ifndef __ARCH_MEMMOVE_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.memmove = memmove;
+#endif
+#ifndef __ARCH_STRNCMP_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strncmp = strncmp;
+#endif
+#ifndef __ARCH_STRLEN_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strlen = strlen;
+#endif
+#ifndef __ARCH_STRSTR_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strstr = strstr;
+#endif
+#ifndef __ARCH_STRNCPY_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strncpy = strncpy;
+#endif
+#ifndef __ARCH_STRNCAT_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strncat = strncat;
+#endif
+#ifndef __ARCH_STRNCASECMP_NO_REDIRECT
+    wolfssl_linuxkm_pie_redirect_table.strncasecmp = strncasecmp;
+#endif
+    wolfssl_linuxkm_pie_redirect_table.kstrtoll = kstrtoll;
+
+    wolfssl_linuxkm_pie_redirect_table.printk = printk;
+    wolfssl_linuxkm_pie_redirect_table.snprintf = snprintf;
+
+    wolfssl_linuxkm_pie_redirect_table._ctype = _ctype;
+
+    wolfssl_linuxkm_pie_redirect_table.kmalloc = kmalloc;
+    wolfssl_linuxkm_pie_redirect_table.kfree = kfree;
+    wolfssl_linuxkm_pie_redirect_table.ksize = ksize;
+    wolfssl_linuxkm_pie_redirect_table.krealloc = krealloc;
+#ifdef HAVE_KVMALLOC
+    wolfssl_linuxkm_pie_redirect_table.kvmalloc_node = kvmalloc_node;
+    wolfssl_linuxkm_pie_redirect_table.kvfree = kvfree;
+#endif
+    wolfssl_linuxkm_pie_redirect_table.is_vmalloc_addr = is_vmalloc_addr;
+    wolfssl_linuxkm_pie_redirect_table.kmem_cache_alloc_trace =
+        kmem_cache_alloc_trace;
+    wolfssl_linuxkm_pie_redirect_table.kmalloc_order_trace =
+        kmalloc_order_trace;
+
+    wolfssl_linuxkm_pie_redirect_table.get_random_bytes = get_random_bytes;
+    wolfssl_linuxkm_pie_redirect_table.ktime_get_real_seconds =
+        ktime_get_real_seconds;
+    wolfssl_linuxkm_pie_redirect_table.ktime_get_with_offset =
+        ktime_get_with_offset;
+
+#if defined(WOLFSSL_AESNI) || defined(USE_INTEL_SPEEDUP)
+    #ifdef kernel_fpu_begin
+    wolfssl_linuxkm_pie_redirect_table.kernel_fpu_begin_mask =
+        kernel_fpu_begin_mask;
+    #else
+    wolfssl_linuxkm_pie_redirect_table.kernel_fpu_begin =
+        kernel_fpu_begin;
+    #endif
+    wolfssl_linuxkm_pie_redirect_table.kernel_fpu_end = kernel_fpu_end;
+#endif
+
+    wolfssl_linuxkm_pie_redirect_table.__mutex_init = __mutex_init;
+    wolfssl_linuxkm_pie_redirect_table.mutex_lock = mutex_lock;
+    wolfssl_linuxkm_pie_redirect_table.mutex_unlock = mutex_unlock;
+
+#ifdef HAVE_FIPS
+    wolfssl_linuxkm_pie_redirect_table.wolfCrypt_FIPS_first =
+        wolfCrypt_FIPS_first;
+    wolfssl_linuxkm_pie_redirect_table.wolfCrypt_FIPS_last =
+        wolfCrypt_FIPS_last;
+#endif
+
+#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
+    wolfssl_linuxkm_pie_redirect_table.GetCA = GetCA;
+#ifndef NO_SKID
+    wolfssl_linuxkm_pie_redirect_table.GetCAByName = GetCAByName;
+#endif
+#endif
+
+    /* runtime assert that the table has no null slots after initialization. */
+    {
+        unsigned long *i;
+        for (i = (unsigned long *)&wolfssl_linuxkm_pie_redirect_table;
+             i < (unsigned long *)&wolfssl_linuxkm_pie_redirect_table._last_slot;
+             ++i)
+            if (*i == 0) {
+                pr_err("wolfCrypt container redirect table initialization was incomplete.\n");
+                return -EFAULT;
+            }
+    }
+
+    return 0;
+}
+
+#endif /* USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE */

+ 42 - 0
linuxkm/pie_first.c

@@ -0,0 +1,42 @@
+/* linuxkm/pie_first.c -- memory fenceposts for checking binary image stability
+ *
+ * Copyright (C) 2006-2021 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 2 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
+ */
+
+#ifndef __PIE__
+    #error pie_first.c must be compiled -fPIE.
+#endif
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/ssl.h>
+
+int wolfCrypt_PIE_first_function(void);
+int wolfCrypt_PIE_first_function(void) {
+    return 0;
+}
+
+const unsigned int wolfCrypt_PIE_rodata_start[];
+const unsigned int wolfCrypt_PIE_rodata_start[] =
+/* random values, analogous to wolfCrypt_FIPS_ro_{start,end} */
+{ 0x8208f9ca, 0x9daf4ac9 };

+ 42 - 0
linuxkm/pie_last.c

@@ -0,0 +1,42 @@
+/* linuxkm/pie_last.c -- memory fenceposts for checking binary image stability
+ *
+ * Copyright (C) 2006-2021 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 2 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
+ */
+
+#ifndef __PIE__
+    #error pie_last.c must be compiled -fPIE.
+#endif
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/ssl.h>
+
+int wolfCrypt_PIE_last_function(void);
+int wolfCrypt_PIE_last_function(void) {
+    return 1;
+}
+
+const unsigned int wolfCrypt_PIE_rodata_end[];
+const unsigned int wolfCrypt_PIE_rodata_end[] =
+/* random values, analogous to wolfCrypt_FIPS_ro_{start,end} */
+{ 0xa4aaaf71, 0x55c4b7d0 };

+ 76 - 0
linuxkm/pie_redirect_table.c

@@ -0,0 +1,76 @@
+/* pie_redirect_table.c -- module load/unload hooks for libwolfssl.ko
+ *
+ * Copyright (C) 2006-2021 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 2 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
+ */
+
+#ifndef __PIE__
+    #error pie_redirect_table.c must be compiled -fPIE.
+#endif
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/ssl.h>
+
+/* compiling -fPIE results in references to the GOT or equivalent thereof, which remain after linking
+ * even if all other symbols are resolved by the link.  naturally there is no
+ * GOT in the kernel, and the wolfssl Kbuild script explicitly checks that no
+ * GOT relocations occur in the PIE objects, but we still need to include a
+ * dummy value here, scoped to the module, to eliminate the otherwise unresolved
+ * symbol.
+ */
+#if defined(CONFIG_X86)
+    extern void * const _GLOBAL_OFFSET_TABLE_;
+    void * const _GLOBAL_OFFSET_TABLE_ = 0;
+#elif defined(CONFIG_MIPS)
+  extern void * const _gp_disp;
+  void * const _gp_disp = 0;
+#endif
+
+struct wolfssl_linuxkm_pie_redirect_table wolfssl_linuxkm_pie_redirect_table;
+
+const struct wolfssl_linuxkm_pie_redirect_table
+*wolfssl_linuxkm_get_pie_redirect_table(void) {
+    return &wolfssl_linuxkm_pie_redirect_table;
+}
+
+/* placeholder implementations for missing functions. */
+#if defined(CONFIG_MIPS)
+    #undef memcpy
+    void *memcpy(void *dest, const void *src, size_t n) {
+        char *dest_i = (char *)dest;
+        char *dest_end = dest_i + n;
+        char *src_i = (char *)src;
+        while (dest_i < dest_end)
+            *dest_i++ = *src_i++;
+        return dest;
+    }
+
+    #undef memset
+    void *memset(void *dest, int c, size_t n) {
+        char *dest_i = (char *)dest;
+        char *dest_end = dest_i + n;
+        while (dest_i < dest_end)
+            *dest_i++ = c;
+        return dest;
+    }
+#endif

+ 9 - 2
m4/ax_linuxkm.m4

@@ -136,12 +136,19 @@ AC_DEFUN([AX_SIMD_CC_COMPILER_FLAGS], [
         fi
 
         if test "$ASFLAGS_FPUSIMD_ENABLE" = ""; then
-            AX_APPEND_COMPILE_FLAGS([-Wa,-march="${BASE_TARGET_ARCH}+fpu+simd"],[$ASFLAGS_FPUSIMD_ENABLE])
+            AX_APPEND_COMPILE_FLAGS([-Wa,-march="${BASE_TARGET_ARCH}+fpu+simd"],[ASFLAGS_FPUSIMD_ENABLE])
         fi
 
         ;;
     *)
-        AC_MSG_ERROR([Don't know how to construct assembler flags for target "${host_cpu}".])
+        # fall back to defining only $ASFLAGS_FPUSIMD_DISABLE
+        if test "$BASE_TARGET_ARCH" = ""; then
+            BASE_TARGET_ARCH=all
+        fi
+
+        if test "$ASFLAGS_FPUSIMD_DISABLE" = ""; then
+            AX_APPEND_COMPILE_FLAGS([-Wa,-march="${BASE_TARGET_ARCH}+nofpu+nosimd"],[ASFLAGS_FPUSIMD_DISABLE])
+        fi
         ;;
     esac
 

+ 1 - 1
tests/api.c

@@ -47448,7 +47448,7 @@ static void test_OBJ_NAME_do_all(void)
 #endif
 }
 
-static void test_SSL_CIPHER_get_xxx()
+static void test_SSL_CIPHER_get_xxx(void)
 {
 #if defined(OPENSSL_ALL) && !defined(NO_CERTS) && \
        !defined(NO_FILESYSTEM)

+ 9 - 3
wolfcrypt/src/asn.c

@@ -9704,9 +9704,14 @@ int ParseCert(DecodedCert* cert, int type, int verify, void* cm)
     return ret;
 }
 
-#if !defined(OPENSSL_EXTRA) && !defined(OPENSSL_EXTRA_X509_SMALL)
+#if defined(WOLFCRYPT_ONLY) || defined(NO_CERTS) \
+    || (!defined(OPENSSL_EXTRA) && !defined(OPENSSL_EXTRA_X509_SMALL) \
+        && !defined(GetCA))
 /* from SSL proper, for locking can't do find here anymore.
- * brought in from internal.h if built with compat layer */
+ * brought in from internal.h if built with compat layer.
+ * if defined(GetCA), it's a predefined macro and these prototypes
+ * would conflict.
+ */
 #ifdef __cplusplus
     extern "C" {
 #endif
@@ -9717,7 +9722,8 @@ int ParseCert(DecodedCert* cert, int type, int verify, void* cm)
 #ifdef __cplusplus
     }
 #endif
-#endif
+
+#endif /* WOLFCRYPT_ONLY || NO_CERTS || (!OPENSSL_EXTRA && !OPENSSL_EXTRA_X509_SMALL && !GetCA) */
 
 #if defined(WOLFCRYPT_ONLY) || defined(NO_CERTS)
 

+ 3 - 3
wolfcrypt/src/ecc.c

@@ -28,9 +28,6 @@
 /* in case user set HAVE_ECC there */
 #include <wolfssl/wolfcrypt/settings.h>
 
-/* public ASN interface */
-#include <wolfssl/wolfcrypt/asn_public.h>
-
 /*
 Possible ECC enable options:
  * HAVE_ECC:            Overall control of ECC                  default: on
@@ -121,6 +118,9 @@ ECC Curve Sizes:
 	#endif
 #endif
 
+/* public ASN interface */
+#include <wolfssl/wolfcrypt/asn_public.h>
+
 #include <wolfssl/wolfcrypt/ecc.h>
 #include <wolfssl/wolfcrypt/asn.h>
 #include <wolfssl/wolfcrypt/error-crypt.h>

+ 8 - 6
wolfcrypt/src/wc_port.c

@@ -2529,8 +2529,7 @@ char* mystrnstr(const char* s1, const char* s2, unsigned int n)
 
 #endif /* WOLFSSL_NUCLEUS_1_2 */
 
-#ifdef WOLFSSL_LINUXKM
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+#if defined(WOLFSSL_LINUXKM) && defined(HAVE_KVMALLOC)
     /* adapted from kvrealloc() draft by Changli Gao, 2010-05-13 */
     void *lkm_realloc(void *ptr, size_t newsize) {
         void *nptr;
@@ -2542,7 +2541,7 @@ char* mystrnstr(const char* s1, const char* s2, unsigned int n)
         }
 
         if (unlikely(ptr == NULL))
-            return kvmalloc(newsize, GFP_KERNEL);
+            return kvmalloc_node(newsize, GFP_KERNEL, NUMA_NO_NODE);
 
         if (is_vmalloc_addr(ptr)) {
             /* no way to discern the size of the old allocation,
@@ -2552,21 +2551,25 @@ char* mystrnstr(const char* s1, const char* s2, unsigned int n)
              */
             return NULL;
         } else {
+#ifndef __PIE__
             struct page *page;
 
             page = virt_to_head_page(ptr);
             if (PageSlab(page) || PageCompound(page)) {
                 if (newsize < PAGE_SIZE)
+#endif /* ! __PIE__ */
                     return krealloc(ptr, newsize, GFP_KERNEL);
+#ifndef __PIE__
                 oldsize = ksize(ptr);
             } else {
                 oldsize = page->private;
                 if (newsize <= oldsize)
                     return ptr;
             }
+#endif /* ! __PIE__ */
 	}
 
-	nptr = kvmalloc(newsize, GFP_KERNEL);
+	nptr = kvmalloc_node(newsize, GFP_KERNEL, NUMA_NO_NODE);
 	if (nptr != NULL) {
             memcpy(nptr, ptr, oldsize);
             kvfree(ptr);
@@ -2574,8 +2577,7 @@ char* mystrnstr(const char* s1, const char* s2, unsigned int n)
 
 	return nptr;
     }
-#endif /* >= 4.12 */
-#endif /* WOLFSSL_LINUXKM */
+#endif /* WOLFSSL_LINUXKM && HAVE_KVMALLOC */
 
 #if defined(WOLFSSL_TI_CRYPT) || defined(WOLFSSL_TI_HASH)
     #include <wolfcrypt/src/port/ti/ti-ccm.c>  /* initialize and Mutex for TI Crypt Engine */

+ 4 - 6
wolfcrypt/test/test.c

@@ -194,7 +194,7 @@ _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
         #include <stdio.h>
     #endif
 
-    #if defined(WOLFSSL_LINUXKM) && !defined(DEBUG_WOLFSSL_VERBOSE)
+    #if defined(WOLFSSL_LINUXKM) && !defined(WOLFSSL_LINUXKM_VERBOSE_DEBUG)
         #undef printf
         #define printf(...) ({})
     #endif
@@ -583,8 +583,7 @@ typedef struct func_args {
 } func_args;
 #endif /* !HAVE_WOLFCRYPT_TEST_OPTIONS */
 
-#ifdef HAVE_FIPS
-
+#if defined(HAVE_FIPS) && !defined(WOLFSSL_LINUXKM)
 static void myFipsCb(int ok, int err, const char* hash)
 {
     printf("in my Fips callback, ok = %d, err = %d\n", ok, err);
@@ -596,8 +595,7 @@ static void myFipsCb(int ok, int err, const char* hash)
         printf("into verifyCore[] in fips_test.c and rebuild\n");
     }
 }
-
-#endif /* HAVE_FIPS */
+#endif /* HAVE_FIPS && !WOLFSSL_LINUXKM */
 
 #ifdef WOLFSSL_STATIC_MEMORY
     #ifdef BENCH_EMBEDDED
@@ -732,7 +730,7 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
     wc_SetLoggingHeap(HEAP_HINT);
 #endif
 
-#ifdef HAVE_FIPS
+#if defined(HAVE_FIPS) && !defined(WOLFSSL_LINUXKM)
     wolfCrypt_SetCb_fips(myFipsCb);
 #endif
 

+ 305 - 31
wolfssl/wolfcrypt/wc_port.h

@@ -68,6 +68,21 @@
 
     #ifdef BUILDING_WOLFSSL
 
+    #if defined(CONFIG_MIPS) && defined(HAVE_LINUXKM_PIE_SUPPORT)
+        /* __ZBOOT__ disables some unhelpful macros around the mem*() funcs in
+         * legacy arch/mips/include/asm/string.h
+         */
+        #define __ZBOOT__
+        #define memcmp __builtin_memcmp
+        #define __ARCH_MEMCMP_NO_REDIRECT
+        #define __ARCH_MEMCPY_NO_REDIRECT
+        #define __builtin_memcpy memcpy
+        extern void *memcpy(void *dest, const void *src, unsigned int n);
+        #define __ARCH_MEMCPY_NO_REDIRECT
+        #define __builtin_memset memset
+        extern void *memset(void *dest, int c, unsigned int n);
+    #endif
+
     _Pragma("GCC diagnostic push");
 
     /* we include all the needed kernel headers with these masked out. else
@@ -83,6 +98,7 @@
     _Pragma("GCC diagnostic ignored \"-Wbad-function-cast\"");
     _Pragma("GCC diagnostic ignored \"-Wdiscarded-qualifiers\"");
     _Pragma("GCC diagnostic ignored \"-Wtype-limits\"");
+    _Pragma("GCC diagnostic ignored \"-Wswitch-enum\"");
 
     /* suppress inclusion of stdint-gcc.h to avoid conflicts with Linux native include/linux/types.h: */
     #define _GCC_STDINT_H
@@ -93,6 +109,13 @@
     #include <linux/ctype.h>
     #include <linux/init.h>
     #include <linux/module.h>
+    #ifdef __PIE__
+        /* without this, mm.h brings in static, but not inline, pmd_to_page(),
+         * with direct references to global vmem variables.
+         */
+        #undef USE_SPLIT_PMD_PTLOCKS
+        #define USE_SPLIT_PMD_PTLOCKS 0
+    #endif
     #include <linux/mm.h>
     #ifndef SINGLE_THREADED
         #include <linux/kthread.h>
@@ -135,26 +158,274 @@
      */
     #define HAVE_ANONYMOUS_INLINE_AGGREGATES 1
 
+    #define NO_THREAD_LS
+    #define NO_ATTRIBUTE_CONSTRUCTOR
+
+    /* kvmalloc()/kvfree() and friends added in linux commit a7c3e901 */
+    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+        #define HAVE_KVMALLOC
+    #endif
+
+    #ifdef HAVE_FIPS
+        extern int wolfCrypt_FIPS_first(void);
+        extern int wolfCrypt_FIPS_last(void);
+    #endif
+
+    #if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
+        /* work around backward dependency of asn.c on ssl.c. */
+        struct Signer;
+        struct Signer *GetCA(void *signers, unsigned char *hash);
+        #ifndef NO_SKID
+            struct Signer *GetCAByName(void* signers, unsigned char *hash);
+        #endif
+    #endif
+
+    #if defined(__PIE__) && !defined(USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE)
+        #error "compiling -fPIE without PIE support."
+    #endif
+
+    #if defined(HAVE_FIPS) && !defined(HAVE_LINUXKM_PIE_SUPPORT)
+        #error "FIPS build requires PIE support."
+    #endif
+
+    #ifdef USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE
+
+#ifdef CONFIG_MIPS
+    #undef __ARCH_MEMCMP_NO_REDIRECT
+    #undef memcmp
+    extern int memcmp(const void *s1, const void *s2, size_t n);
+#endif
+
+    struct wolfssl_linuxkm_pie_redirect_table {
+    #ifndef __ARCH_MEMCMP_NO_REDIRECT
+        typeof(memcmp) *memcmp;
+    #endif
+    #ifndef __ARCH_MEMCPY_NO_REDIRECT
+        typeof(memcpy) *memcpy;
+    #endif
+    #ifndef __ARCH_MEMSET_NO_REDIRECT
+        typeof(memset) *memset;
+    #endif
+    #ifndef __ARCH_MEMMOVE_NO_REDIRECT
+        typeof(memmove) *memmove;
+    #endif
+    #ifndef __ARCH_STRNCMP_NO_REDIRECT
+        typeof(strncmp) *strncmp;
+    #endif
+    #ifndef __ARCH_STRLEN_NO_REDIRECT
+        typeof(strlen) *strlen;
+    #endif
+    #ifndef __ARCH_STRSTR_NO_REDIRECT
+        typeof(strstr) *strstr;
+    #endif
+    #ifndef __ARCH_STRNCPY_NO_REDIRECT
+        typeof(strncpy) *strncpy;
+    #endif
+    #ifndef __ARCH_STRNCAT_NO_REDIRECT
+        typeof(strncat) *strncat;
+    #endif
+    #ifndef __ARCH_STRNCASECMP_NO_REDIRECT
+        typeof(strncasecmp) *strncasecmp;
+    #endif
+        typeof(kstrtoll) *kstrtoll;
+
+        typeof(printk) *printk;
+        typeof(snprintf) *snprintf;
+
+        const unsigned char *_ctype;
+
+        typeof(kmalloc) *kmalloc;
+        typeof(kfree) *kfree;
+        typeof(ksize) *ksize;
+        typeof(krealloc) *krealloc;
+        #ifdef HAVE_KVMALLOC
+        typeof(kvmalloc_node) *kvmalloc_node;
+        typeof(kvfree) *kvfree;
+        #endif
+        typeof(is_vmalloc_addr) *is_vmalloc_addr;
+        typeof(kmem_cache_alloc_trace) *kmem_cache_alloc_trace;
+        typeof(kmalloc_order_trace) *kmalloc_order_trace;
+
+        typeof(get_random_bytes) *get_random_bytes;
+        typeof(ktime_get_real_seconds) *ktime_get_real_seconds;
+        typeof(ktime_get_with_offset) *ktime_get_with_offset;
+
+        #if defined(WOLFSSL_AESNI) || defined(USE_INTEL_SPEEDUP)
+        /* kernel_fpu_begin() replaced by kernel_fpu_begin_mask() in commit e4512289,
+         * released in kernel 5.11, backported to 5.4.93
+         */
+        #ifdef kernel_fpu_begin
+            typeof(kernel_fpu_begin_mask) *kernel_fpu_begin_mask;
+        #else
+            typeof(kernel_fpu_begin) *kernel_fpu_begin;
+        #endif
+        typeof(kernel_fpu_end) *kernel_fpu_end;
+        #endif
+
+        typeof(__mutex_init) *__mutex_init;
+        typeof(mutex_lock) *mutex_lock;
+        typeof(mutex_unlock) *mutex_unlock;
+
+        #ifdef HAVE_FIPS
+        typeof(wolfCrypt_FIPS_first) *wolfCrypt_FIPS_first;
+        typeof(wolfCrypt_FIPS_last) *wolfCrypt_FIPS_last;
+        #endif
+
+        #if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
+        typeof(GetCA) *GetCA;
+        #ifndef NO_SKID
+        typeof(GetCAByName) *GetCAByName;
+        #endif
+        #endif
+
+        const void *_last_slot;
+    };
+
+    extern const struct wolfssl_linuxkm_pie_redirect_table *wolfssl_linuxkm_get_pie_redirect_table(void);
+
+    #ifdef __PIE__
+
+    #ifndef __ARCH_MEMCMP_NO_REDIRECT
+        #define memcmp (wolfssl_linuxkm_get_pie_redirect_table()->memcmp)
+    #endif
+    #ifndef __ARCH_MEMCPY_NO_REDIRECT
+        #define memcpy (wolfssl_linuxkm_get_pie_redirect_table()->memcpy)
+    #endif
+    #ifndef __ARCH_MEMSET_NO_REDIRECT
+        #define memset (wolfssl_linuxkm_get_pie_redirect_table()->memset)
+    #endif
+    #ifndef __ARCH_MEMMOVE_NO_REDIRECT
+        #define memmove (wolfssl_linuxkm_get_pie_redirect_table()->memmove)
+    #endif
+    #ifndef __ARCH_STRNCMP_NO_REDIRECT
+        #define strncmp (wolfssl_linuxkm_get_pie_redirect_table()->strncmp)
+    #endif
+    #ifndef __ARCH_STRLEN_NO_REDIRECT
+        #define strlen (wolfssl_linuxkm_get_pie_redirect_table()->strlen)
+    #endif
+    #ifndef __ARCH_STRSTR_NO_REDIRECT
+        #define strstr (wolfssl_linuxkm_get_pie_redirect_table()->strstr)
+    #endif
+    #ifndef __ARCH_STRNCPY_NO_REDIRECT
+        #define strncpy (wolfssl_linuxkm_get_pie_redirect_table()->strncpy)
+    #endif
+    #ifndef __ARCH_STRNCAT_NO_REDIRECT
+        #define strncat (wolfssl_linuxkm_get_pie_redirect_table()->strncat)
+    #endif
+    #ifndef __ARCH_STRNCASECMP_NO_REDIRECT
+        #define strncasecmp (wolfssl_linuxkm_get_pie_redirect_table()->strncasecmp)
+    #endif
+    #define kstrtoll (wolfssl_linuxkm_get_pie_redirect_table()->kstrtoll)
+
+    #define printk (wolfssl_linuxkm_get_pie_redirect_table()->printk)
+    #define snprintf (wolfssl_linuxkm_get_pie_redirect_table()->snprintf)
+
+    #define _ctype (wolfssl_linuxkm_get_pie_redirect_table()->_ctype)
+
+    #define kmalloc (wolfssl_linuxkm_get_pie_redirect_table()->kmalloc)
+    #define kfree (wolfssl_linuxkm_get_pie_redirect_table()->kfree)
+    #define ksize (wolfssl_linuxkm_get_pie_redirect_table()->ksize)
+    #define krealloc (wolfssl_linuxkm_get_pie_redirect_table()->krealloc)
+    #ifdef HAVE_KVMALLOC
+        #define kvmalloc_node (wolfssl_linuxkm_get_pie_redirect_table()->kvmalloc_node)
+        #define kvfree (wolfssl_linuxkm_get_pie_redirect_table()->kvfree)
+    #endif
+    #define is_vmalloc_addr (wolfssl_linuxkm_get_pie_redirect_table()->is_vmalloc_addr)
+    #define kmem_cache_alloc_trace (wolfssl_linuxkm_get_pie_redirect_table()->kmem_cache_alloc_trace)
+    #define kmalloc_order_trace (wolfssl_linuxkm_get_pie_redirect_table()->kmalloc_order_trace)
+
+    #define get_random_bytes (wolfssl_linuxkm_get_pie_redirect_table()->get_random_bytes)
+    #define ktime_get_real_seconds (wolfssl_linuxkm_get_pie_redirect_table()->ktime_get_real_seconds)
+    #define ktime_get_with_offset (wolfssl_linuxkm_get_pie_redirect_table()->ktime_get_with_offset)
+
+    #if defined(WOLFSSL_AESNI) || defined(USE_INTEL_SPEEDUP)
+        #ifdef kernel_fpu_begin
+            #define kernel_fpu_begin_mask (wolfssl_linuxkm_get_pie_redirect_table()->kernel_fpu_begin_mask)
+        #else
+            #define kernel_fpu_begin (wolfssl_linuxkm_get_pie_redirect_table()->kernel_fpu_begin)
+        #endif
+        #define kernel_fpu_end (wolfssl_linuxkm_get_pie_redirect_table()->kernel_fpu_end)
+    #endif
+
+    #define __mutex_init (wolfssl_linuxkm_get_pie_redirect_table()->__mutex_init)
+    #define mutex_lock (wolfssl_linuxkm_get_pie_redirect_table()->mutex_lock)
+    #define mutex_unlock (wolfssl_linuxkm_get_pie_redirect_table()->mutex_unlock)
+
+    /* per linux/ctype.h, tolower() and toupper() are macros bound to static inlines
+     * that use macros that bring in the _ctype global.  for __PIE__, this needs to
+     * be masked out.
+     */
+    #undef tolower
+    #undef toupper
+    #define tolower(c) (islower(c) ? (c) : ((c) + ('a'-'A')))
+    #define toupper(c) (isupper(c) ? (c) : ((c) - ('a'-'A')))
+
+    #if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
+        #define GetCA (wolfssl_linuxkm_get_pie_redirect_table()->GetCA)
+        #ifndef NO_SKID
+            #define GetCAByName (wolfssl_linuxkm_get_pie_redirect_table()->GetCAByName)
+        #endif
+    #endif
+
+    #endif /* __PIE__ */
+
+    #endif /* USE_WOLFSSL_LINUXKM_PIE_REDIRECT_TABLE */
+
     /* Linux headers define these using C expressions, but we need
      * them to be evaluable by the preprocessor, for use in sp_int.h.
      */
-    _Static_assert(sizeof(ULONG_MAX) == 8, "WOLFSSL_LINUXKM supported only on targets with 64 bit long words.");
-    #undef UCHAR_MAX
-    #define UCHAR_MAX 255
-    #undef USHRT_MAX
-    #define USHRT_MAX 65535
-    #undef UINT_MAX
-    #define UINT_MAX 4294967295U
-    #undef ULONG_MAX
-    #define ULONG_MAX 18446744073709551615UL
-    #undef ULLONG_MAX
-    #define ULLONG_MAX ULONG_MAX
-    #undef INT_MAX
-    #define INT_MAX 2147483647
-    #undef LONG_MAX
-    #define LONG_MAX 9223372036854775807L
-    #undef LLONG_MAX
-    #define LLONG_MAX LONG_MAX
+    #if BITS_PER_LONG == 64
+        _Static_assert(sizeof(ULONG_MAX) == 8, "BITS_PER_LONG is 64, but ULONG_MAX is not.");
+
+        #undef UCHAR_MAX
+        #define UCHAR_MAX 255
+        #undef USHRT_MAX
+        #define USHRT_MAX 65535
+        #undef UINT_MAX
+        #define UINT_MAX 4294967295U
+        #undef ULONG_MAX
+        #define ULONG_MAX 18446744073709551615UL
+        #undef ULLONG_MAX
+        #define ULLONG_MAX ULONG_MAX
+        #undef INT_MAX
+        #define INT_MAX 2147483647
+        #undef LONG_MAX
+        #define LONG_MAX 9223372036854775807L
+        #undef LLONG_MAX
+        #define LLONG_MAX LONG_MAX
+
+    #elif BITS_PER_LONG == 32
+
+        _Static_assert(sizeof(ULONG_MAX) == 4, "BITS_PER_LONG is 32, but ULONG_MAX is not.");
+
+        #undef UCHAR_MAX
+        #define UCHAR_MAX 255
+        #undef USHRT_MAX
+        #define USHRT_MAX 65535
+        #undef UINT_MAX
+        #define UINT_MAX 4294967295U
+        #undef ULONG_MAX
+        #define ULONG_MAX 4294967295UL
+        #undef INT_MAX
+        #define INT_MAX 2147483647
+        #undef LONG_MAX
+        #define LONG_MAX 2147483647L
+
+        #undef ULLONG_MAX
+        #undef LLONG_MAX
+        #if BITS_PER_LONG_LONG == 64
+            #define ULLONG_MAX 18446744073709551615UL
+            #define LLONG_MAX 9223372036854775807L
+        #else
+            #undef NO_64BIT
+            #define NO_64BIT
+            #define ULLONG_MAX ULONG_MAX
+            #define LLONG_MAX LONG_MAX
+        #endif
+
+#else
+        #error unexpected BITS_PER_LONG value.
+#endif
 
     /* remove this multifariously conflicting macro, picked up from
      * Linux arch/<arch>/include/asm/current.h.
@@ -166,17 +437,16 @@
      */
     #define _MM_MALLOC_H_INCLUDED
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
-    /* kvmalloc()/kvfree() and friends added in linux commit a7c3e901 */
-    #define malloc(x) kvmalloc(x, GFP_KERNEL)
-    #define free(x) kvfree(x)
-    void *lkm_realloc(void *ptr, size_t newsize);
-    #define realloc(x, y) lkm_realloc(x, y)
-#else
-    #define malloc(x) kmalloc(x, GFP_KERNEL)
-    #define free(x) kfree(x)
-    #define realloc(x,y) krealloc(x, y, GFP_KERNEL)
-#endif
+    #ifdef HAVE_KVMALLOC
+        #define malloc(x) kvmalloc_node(x, GFP_KERNEL, NUMA_NO_NODE)
+        #define free(x) kvfree(x)
+        void *lkm_realloc(void *ptr, size_t newsize);
+        #define realloc(x, y) lkm_realloc(x, y)
+    #else
+        #define malloc(x) kmalloc(x, GFP_KERNEL)
+        #define free(x) kfree(x)
+        #define realloc(x,y) krealloc(x, y, GFP_KERNEL)
+    #endif
 
     /* min() and max() in linux/kernel.h over-aggressively type-check, producing
      * myriad spurious -Werrors throughout the codebase.
@@ -192,15 +462,19 @@
     #define lkm_printf(format, args...) printk(KERN_INFO "wolfssl: %s(): " format, __func__, ## args)
     #define printf(...) lkm_printf(__VA_ARGS__)
 
+    #ifdef HAVE_FIPS
+        extern void fipsEntry(void);
+    #endif
+
     #endif /* BUILDING_WOLFSSL */
 
     /* needed to suppress inclusion of stdio.h in wolfssl/wolfcrypt/types.h */
     #define XSNPRINTF snprintf
 
-    /* the rigmarole around kstrtol() here is to accommodate its warn-unused-result attribute. */
+    /* the rigmarole around kstrtoll() here is to accommodate its warn-unused-result attribute. */
     #define XATOI(s) ({                                 \
-          long _xatoi_res = 0;                          \
-          int _xatoi_ret = kstrtol(s, 10, &_xatoi_res); \
+          long long _xatoi_res = 0;                     \
+          int _xatoi_ret = kstrtoll(s, 10, &_xatoi_res); \
           if (_xatoi_ret != 0) {                        \
             _xatoi_res = 0;                             \
           }                                             \