diff --git a/linuxkm/Kbuild b/linuxkm/Kbuild index 5a2c0495342..1ec7bbfb504 100644 --- a/linuxkm/Kbuild +++ b/linuxkm/Kbuild @@ -147,7 +147,18 @@ endif ccflags-y = $(WOLFSSL_CFLAGS) $(WOLFSSL_CFLAGS_NO_VECTOR_INSNS) +# Retpoline mitigation for Spectre v2 was added in 4.15, but under knob +# CONFIG_RETPOLINE, and return thunk mitigation for retbleed was added +# in 5.19, but under knob CONFIG_RETHUNK. The current knob names were +# merged in 6.9; alias to current names so one set of conditionals +# serves both. +CONFIG_MITIGATION_RETPOLINE ?= $(CONFIG_RETPOLINE) +CONFIG_MITIGATION_RETHUNK ?= $(CONFIG_RETHUNK) + ifeq "$(ENABLED_LINUXKM_PIE)" "yes" + ifdef CONFIG_CFI_CLANG + $(error CONFIG_CFI_CLANG is not compatible with ENABLED_LINUXKM_PIE.) + endif # note, we need -fno-stack-protector to avoid references to # "__stack_chk_fail" from the wolfCrypt container. PIE_FLAGS := -DWC_CONTAINERIZE_THIS -fno-stack-protector -fno-toplevel-reorder @@ -206,44 +217,86 @@ ifeq "$(ENABLED_LINUXKM_PIE)" "yes" ifeq "$(KERNEL_ARCH_X86)" "yes" PIE_FLAGS += -mcmodel=small - # eliminate external references to __x86_return_thunk and - # __x86_indirect_thunk_foo implementations. _all_ references must be - # eliminated, not just those in PIE objects, otherwise some kernels will + # eliminate external references to __x86_return_thunk (via + # -mfunction-return=thunk-inline) and __x86_indirect_thunk_foo (via + # -mindirect-branch=thunk-inline). _all_ references must be eliminated, + # not just those in PIE objects, otherwise some kernels will # false-positively complain about unpatched thunks. - ifeq "$(CONFIG_MITIGATION_RETPOLINE)" "y" - PIE_SUPPORT_FLAGS += -mfunction-return=thunk-inline - else - PIE_SUPPORT_FLAGS += -mfunction-return=keep - endif - ifeq "$(CONFIG_MITIGATION_RETHUNK)" "y" - PIE_SUPPORT_FLAGS += -mindirect-branch=thunk-inline - else - PIE_SUPPORT_FLAGS += -mindirect-branch=keep - endif + + NO_EXTERNAL_THUNKS := y OBJECT_FILES_NON_STANDARD := y endif ifeq "$(KERNEL_ARCH)" "arm" - PIE_FLAGS += -fno-unwind-tables + PIE_FLAGS += -fno-unwind-tables endif ifeq "$(KERNEL_ARCH)" "mips" - PIE_FLAGS += -mabicalls + PIE_FLAGS += -mabicalls endif - ccflags-y += $(PIE_SUPPORT_FLAGS) $(WOLFCRYPT_PIE_FILES): ccflags-y += $(PIE_FLAGS) $(WOLFCRYPT_PIE_FILES): ccflags-remove-y += -pg \ - $(call cc-option,-ftrivial-auto-var-init=zero) + $(call cc-option,-ftrivial-auto-var-init=zero) +endif + +# An explicit FORCE_GLOBAL_OBJTOOL_OFF is refused outright with clang -- the +# thunk repair below is gcc-only. +ifdef CONFIG_CC_IS_CLANG + ifdef FORCE_GLOBAL_OBJTOOL_OFF + $(error FORCE_GLOBAL_OBJTOOL_OFF is not supported with clang.) + endif endif -# On some kernels/configs, objtool runs and warns on the linked libwolfssl.o, -# ignoring the below "OBJECT_FILES_NON_STANDARD := y" annotations. This -# mechanism, activated with "make KBUILD_EXTRA_FLAGS=FORCE_GLOBAL_OBJTOOL_OFF=1", -# inhibits objtool completely. This may be necessary on modules compiled with -# assembly accelerations and/or ENABLED_LINUXKM_PIE. +# Similar to scripts/Makefile.lib delay-objtool: disable objtool on the kernels +# that run it on the linked object, ignoring OBJECT_FILES_NON_STANDARD. (Parse +# order prevents testing delay-objtool itself here: Makefile.build includes this +# file before Makefile.lib.) Auto-disablement is a gcc-only remedy, unavailable +# on clang, and objtool is mandatory to CONFIG_LTO_CLANG in any case. +ifndef CONFIG_CC_IS_CLANG + ifneq ($(or $(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD)),) + ifeq "$(ENABLED_LINUXKM_PIE)" "yes" + FORCE_GLOBAL_OBJTOOL_OFF := y + else ifeq "$(ENABLED_ASM)" "yes" + FORCE_GLOBAL_OBJTOOL_OFF := y + endif + endif +endif + +# objtool disablement can be manually activated with "make +# KBUILD_EXTRA_FLAGS=FORCE_GLOBAL_OBJTOOL_OFF=1" ifdef FORCE_GLOBAL_OBJTOOL_OFF undefine CONFIG_OBJTOOL + # On <5.19, the knob is named CONFIG_STACK_VALIDATION. + undefine CONFIG_STACK_VALIDATION + NO_EXTERNAL_THUNKS := y endif +ifdef NO_EXTERNAL_THUNKS + ifeq "$(KERNEL_ARCH_X86)" "yes" + ifdef CONFIG_CC_IS_CLANG + # clang has no inline-thunk codegen (-mfunction-return accepts + # only keep/thunk-extern, and -mindirect-branch is a gcc-only + # spelling), so external thunk references are ineliminable when + # the mitigations are configured. + ifneq "$(CONFIG_MITIGATION_RETHUNK)$(CONFIG_MITIGATION_RETPOLINE)" "" + $(error NO_EXTERNAL_THUNKS requires gcc when retpoline/rethunk is configured.) + endif + else + ifeq "$(CONFIG_MITIGATION_RETHUNK)" "y" + PIE_SUPPORT_FLAGS += -mfunction-return=thunk-inline + else + PIE_SUPPORT_FLAGS += -mfunction-return=keep + endif + ifeq "$(CONFIG_MITIGATION_RETPOLINE)" "y" + PIE_SUPPORT_FLAGS += -mindirect-branch=thunk-inline -mindirect-branch-register + else + PIE_SUPPORT_FLAGS += -mindirect-branch=keep + endif + endif + endif +endif + +ccflags-y += $(PIE_SUPPORT_FLAGS) + ifdef KERNEL_EXTRA_CFLAGS_REMOVE ccflags-remove-y += $(KERNEL_EXTRA_CFLAGS_REMOVE) endif @@ -352,9 +405,9 @@ RENAME_PIE_TEXT_AND_DATA_SECTIONS := \ cd "$(obj)" || exit $$?; \ for file in $(WOLFCRYPT_PIE_FILES); do \ $(OBJCOPY) $$($(READELF) --sections --wide "$$file" | \ - $(AWK) ' \ + $(AWK) ' \ { \ - if (match($$0, "^ *\\[ *[0-9]+\\] +\\.(text|rodata|data|bss)(\\.[^ ]+)? ", a)) \ + if (match($$0, "^ *\\[ *[0-9]+\\] +\\.(text|rodata|data|bss)(\\.[^ ]+)? ", a)) \ { \ printf("--rename-section .%s%s=.%s_wolfcrypt ", \ a[1], a[2], a[1]); \ @@ -364,7 +417,7 @@ RENAME_PIE_TEXT_AND_DATA_SECTIONS := \ printf("--rename-section .%s.%s=.%s_wolfcrypt ", a[1], a[2], a[2]); \ } \ }') "$$file" || exit $$?; \ - done; \ + done; \ { $(READELF) --sections --syms --wide $(WOLFCRYPT_PIE_FILES) | \ $(AWK) -v obj="$(obj)" ' \ /^File:/ { \ diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 87fd83daf1b..935e74ab887 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -12847,12 +12847,11 @@ static void bench_frodokem_keygen(int type, const char* name, int keySize, do { /* while free pending slots in queue, submit ops */ for (times = 0; times < agreeTimes || pending > 0; times++) { - wc_FrodoKemKey_Free(key); ret = wc_FrodoKemKey_Init(key, type, HEAP_HINT, INVALID_DEVID); if (ret != 0) goto exit; - ret = wc_FrodoKemKey_MakeKey(key, &gRng); + wc_FrodoKemKey_Free(key); if (ret != 0) goto exit; RECORD_MULTI_VALUE_STATS(); @@ -12882,6 +12881,7 @@ static void bench_frodokem_keygen(int type, const char* name, int keySize, static void bench_frodokem_encap(int type, const char* name, int keySize, FrodoKemKey* key1, FrodoKemKey* key2) { +#ifndef WOLFSSL_FRODOKEM_NO_MAKE_KEY int ret = 0, times, count, pending = 0; double start; const char**desc = bench_desc_words[lng_index]; @@ -12898,6 +12898,14 @@ static void bench_frodokem_encap(int type, const char* name, int keySize, WC_ALLOC_VAR(ss, byte, FRODOKEM_MAX_LENSEC, HEAP_HINT); WC_ALLOC_VAR(pub, byte, FRODOKEM_MAX_PUBLIC_KEY_SIZE, HEAP_HINT); + ret = wc_FrodoKemKey_Init(key1, type, HEAP_HINT, INVALID_DEVID); + if (ret != 0) + goto exit; + + ret = wc_FrodoKemKey_MakeKey(key1, &gRng); + if (ret != 0) + goto exit; + ret = wc_FrodoKemKey_PublicKeySize(key1, &pubLen); if (ret != 0) { goto exit; @@ -12977,6 +12985,9 @@ static void bench_frodokem_encap(int type, const char* name, int keySize, exit: + wc_FrodoKemKey_Free(key2); + wc_FrodoKemKey_Free(key1); + WC_FREE_VAR(ct, HEAP_HINT); WC_FREE_VAR(ss, HEAP_HINT); WC_FREE_VAR(pub, HEAP_HINT); @@ -12985,6 +12996,13 @@ static void bench_frodokem_encap(int type, const char* name, int keySize, printf("error: bench_frodokem_encap() failed with code %d.\n", ret); return; +#else + (void)type; + (void)name; + (void)keySize; + (void)key1; + (void)key2; +#endif /* !WOLFSSL_FRODOKEM_NO_MAKE_KEY */ } #endif @@ -13055,10 +13073,14 @@ void bench_frodokem(int type) } #endif - /* Zero the key objects so the wc_FrodoKemKey_Free calls below are safe even - * if a benchmark helper returns before initializing its key. */ + /* Zero the key objects so wc_FrodoKemKey_Free calls are safe even + * if a benchmark helper cleans up before initializing its key. */ XMEMSET(key1, 0, sizeof(*key1)); XMEMSET(key2, 0, sizeof(*key2)); +#ifdef WOLF_CRYPTO_CB + key1->devId = INVALID_DEVID; + key2->devId = INVALID_DEVID; +#endif for (v = 0; v < sizeof(mods) / sizeof(mods[0]); v++) { int mod = mods[v]; @@ -13078,9 +13100,6 @@ void bench_frodokem(int type) #endif } - wc_FrodoKemKey_Free(key2); - wc_FrodoKemKey_Free(key1); - WC_FREE_VAR_EX(key1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(key2, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 45d757b3e35..6d1aca24ef5 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -78533,6 +78533,7 @@ static wc_test_ret_t slhdsa_init_fail_free_test(void) #define SLHDSA_TEST_CERT_SHA2_128S #endif #if !defined(NO_ASN) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_MALLOC) && \ (defined(SLHDSA_TEST_CERT_SHAKE128S) || \ defined(SLHDSA_TEST_CERT_SHA2_128S)) #define SLHDSA_TEST_CERT diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 91200566acb..d7a35203478 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -331,8 +331,10 @@ WOLFSSL_LOCAL void wc_MemZero_Add(const char* name, const void* addr, WOLFSSL_LOCAL void wc_MemZero_Check(void* addr, size_t len); #endif -#ifndef WOLFSSL_NO_FORCE_ZERO -WOLFSSL_API void wc_ForceZero(void *mem, size_t len); +#ifdef WOLFSSL_NO_FORCE_ZERO + #define wc_ForceZero ForceZero +#else + WOLFSSL_API void wc_ForceZero(void *mem, size_t len); #endif #ifndef WOLFSSL_NO_CONST_CMP diff --git a/wolfssl/wolfcrypt/wc_compat.h b/wolfssl/wolfcrypt/wc_compat.h index 2bfa5de687d..b6de0f97065 100644 --- a/wolfssl/wolfcrypt/wc_compat.h +++ b/wolfssl/wolfcrypt/wc_compat.h @@ -69,6 +69,16 @@ #define wc_AesGcmEncrypt wc_AesGcmEncrypt_compat_shim + /* Reachable (via types.h) before memory.h's body when an application + * includes memory.h first: its include guard defers the body, so declare + * the exported zeroizer here for the inline shims below. Redeclaration + * with identical tokens is harmless. */ + #ifdef WOLFSSL_NO_FORCE_ZERO + #define wc_ForceZero ForceZero + #else + WOLFSSL_API void wc_ForceZero(void *mem, size_t len); + #endif + #ifdef WOLFSSL_AESGCM_STREAM static WC_INLINE int wc_AesGcmEncrypt_compat_shim(Aes* aes, byte* out, const byte* in, word32 sz, @@ -84,12 +94,24 @@ return BAD_FUNC_ARG; #endif ret = wc_AesGcmInit(aes, NULL /* key */, 0 /* len */, iv, ivSz); - if (ret) - return ret; - ret = wc_AesGcmEncryptUpdate(aes, out, in, sz, authIn, authInSz); - if (ret) - return ret; - return wc_AesGcmEncryptFinal(aes, authTag, authTagSz); + if (ret == 0) + ret = wc_AesGcmEncryptUpdate(aes, out, in, sz, authIn, authInSz); + if (ret == 0) + ret = wc_AesGcmEncryptFinal(aes, authTag, authTagSz); + if (ret != 0) { + /* Match the native one-shot's failure contract: no ciphertext and no + * tag escape a failed operation, whichever stage failed. On the + * Init/Update failure paths authTag was never written, so there this + * is contract uniformity rather than leak repair. */ + if (out != NULL) + wc_ForceZero(out, sz); + if (authTag != NULL) + wc_ForceZero(authTag, + authTagSz > WC_AES_BLOCK_SIZE + ? WC_AES_BLOCK_SIZE + : authTagSz); + } + return ret; } #else /* ! WOLFSSL_AESGCM_STREAM */ @@ -109,13 +131,22 @@ return BAD_LENGTH_E; ret = wc_AesGcmSetExtIV(aes, iv, ivSz); - if (ret != 0) - return ret; - ret = wc_AesGcmEncrypt_ex(aes, out, in, sz, scratch, ivSz, authTag, - authTagSz, authIn, authInSz); - if (ret != 0) - return ret; - return wc_AesGcmSetExtIV(aes, iv, ivSz); + if (ret == 0) + ret = wc_AesGcmEncrypt_ex(aes, out, in, sz, scratch, ivSz, authTag, + authTagSz, authIn, authInSz); + if (ret == 0) + ret = wc_AesGcmSetExtIV(aes, iv, ivSz); + if (ret != 0) { + /* Match above ISO-19790 wipe--this arm airdropped into old-FIPS. */ + if (out != NULL) + wc_ForceZero(out, sz); + if (authTag != NULL) + wc_ForceZero(authTag, + authTagSz > WC_AES_BLOCK_SIZE + ? WC_AES_BLOCK_SIZE + : authTagSz); + } + return ret; } #endif /* ! WOLFSSL_AESGCM_STREAM */ #endif /* HAVE_FIPS && HAVE_AESGCM && */