From dc16169cd3e28cd997f1157a73b64735e72f8a9a Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 22 Jul 2026 09:51:20 -0500 Subject: [PATCH] linuxkm/lkcapi_sha_glue.c: fix sha.W lifecycle management to prevent leaks: * separate WC_LINUXKM_SHA_IMPLEMENT() into WC_LINUXKM_SHA1_IMPLEMENT() (no fixes needed) and WC_LINUXKM_SHA2_IMPLEMENT() (with associated new helpers WC_LINUXKM_SHA2_FREE_W(), WC_LINUXKM_SHA2_DECL_W(), WC_LINUXKM_SHA2_PUSH_W(), and WC_LINUXKM_SHA2_POP_W(), that move .W to a stack buffer). * Reimplement SHA-2 one-shot digest callback to use only direct wolfCrypt calls rather than proxy to other callbacks. --- linuxkm/lkcapi_sha_glue.c | 186 +++++++++++++++++++++++++++++++++++-- wolfcrypt/src/sha256.c | 22 ++--- wolfcrypt/src/sha512.c | 12 +-- wolfssl/wolfcrypt/sha256.h | 9 +- wolfssl/wolfcrypt/sha512.h | 6 +- 5 files changed, 208 insertions(+), 27 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index f7b275c4a6..6c88ba0fc8 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -486,7 +486,7 @@ WC_MAYBE_UNUSED static int sha3_test_once(void) { } #endif -#define WC_LINUXKM_SHA_IMPLEMENT(name, digest_size, block_size, \ +#define WC_LINUXKM_SHA1_IMPLEMENT(name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ free_f, test_routine) \ @@ -588,6 +588,169 @@ static int linuxkm_test_ ## name(void) { \ \ struct wc_swallow_the_semicolon +#ifdef WOLFSSL_SMALL_STACK_CACHE + /* The glue layer needs to take ownership of the .W working buffer to assure + * it can't leak on abandoned descs, or double-free on export-import cycled + * descs. It's small enough to fit comfortably on the stack, so there's + * almost no overhead associated with this. + * + * Eager allocation of .W in SHA-2 init is to assure no heap operations in + * SHA-2 after init, mitigating an infinite recursion: The wolfCrypt DRBG + * sits atop SHA-2, and when LINUXKM_DRBG_GET_RANDOM_BYTES && + * WOLFSSL_LINUXKM_HAVE_GET_RANDOM_CALLBACKS && CONFIG_SLAB_FREELIST_RANDOM, + * it sits _under_ the kernel heap. + */ + #define WC_LINUXKM_SHA2_FREE_W(s) do { free((s)->W); (s)->W = NULL; } while (0) + #define WC_LINUXKM_SHA2_DECL_W(s, l) wc_static_assert((l) % sizeof (s)->W[0] == 0); \ + typeof((s)->W[0]) w_buf[(l) / sizeof (s)->W[0]] + #define WC_LINUXKM_SHA2_PUSH_W(s) { (s)->W = w_buf + #define WC_LINUXKM_SHA2_POP_W(s) ForceZero(w_buf, sizeof w_buf); (s)->W = NULL; } WC_DO_NOTHING +#else + #define WC_LINUXKM_SHA2_FREE_W(s) WC_DO_NOTHING + #define WC_LINUXKM_SHA2_DECL_W(s, l) struct wc_swallow_the_semicolon + #define WC_LINUXKM_SHA2_PUSH_W(s) { WC_DO_NOTHING + #define WC_LINUXKM_SHA2_POP_W(s) } WC_DO_NOTHING +#endif + +#define WC_LINUXKM_SHA2_IMPLEMENT(name, digest_size, block_size, W_size, \ + this_cra_name, this_cra_driver_name, \ + init_f, update_f, final_f, \ + free_f, test_routine) \ + \ + \ +static int km_ ## name ## _init(struct shash_desc *desc) { \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + \ + int ret = init_f(&ctx-> name ## _state); \ + if (ret == 0) { \ + WC_LINUXKM_SHA2_FREE_W(&ctx-> name ## _state); \ + return 0; \ + } \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _update(struct shash_desc *desc, const u8 *data, \ + unsigned int len) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = update_f(&ctx-> name ## _state, data, len); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else { \ + free_f(&ctx-> name ## _state); \ + return -EINVAL; \ + } \ +} \ + \ +static int km_ ## name ## _final(struct shash_desc *desc, u8 *out) { \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = final_f(&ctx-> name ## _state, out); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _finup(struct shash_desc *desc, const u8 *data, \ + unsigned int len, u8 *out) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + WC_LINUXKM_SHA2_DECL_W(&ctx-> name ## _state, W_size); \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = update_f(&ctx-> name ## _state, data, len); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + if (ret != 0) { \ + free_f(&ctx-> name ## _state); \ + return -EINVAL; \ + } \ + \ + WC_LINUXKM_SHA2_PUSH_W(&ctx-> name ## _state); \ + ret = final_f(&ctx-> name ## _state, out); \ + WC_LINUXKM_SHA2_POP_W(&ctx-> name ## _state); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ +static int km_ ## name ## _digest(struct shash_desc *desc, const u8 *data, \ + unsigned int len, u8 *out) \ +{ \ + struct km_sha_state *ctx = (struct km_sha_state *)shash_desc_ctx(desc);\ + int ret; \ + \ + ret = init_f(&ctx-> name ## _state); \ + if (ret != 0) \ + return -EINVAL; \ + \ + ret = update_f(&ctx-> name ## _state, data, len); \ + \ + if (ret == 0) \ + ret = final_f(&ctx-> name ## _state, out); \ + \ + free_f(&ctx-> name ## _state); \ + \ + if (ret == 0) \ + return 0; \ + else \ + return -EINVAL; \ +} \ + \ + \ +static struct shash_alg name ## _alg = \ +{ \ + .digestsize = (digest_size), \ + .init = km_ ## name ## _init, \ + .update = km_ ## name ## _update, \ + .final = km_ ## name ## _final, \ + .finup = km_ ## name ## _finup, \ + .digest = km_ ## name ## _digest, \ + .descsize = sizeof(struct km_sha_state), \ + .base = { \ + .cra_name = (this_cra_name), \ + .cra_driver_name = (this_cra_driver_name), \ + .cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, \ + .cra_blocksize = (block_size), \ + .cra_module = THIS_MODULE \ + } \ +}; \ +static int name ## _alg_loaded = 0; \ + \ +static int linuxkm_test_ ## name(void) { \ + wc_test_ret_t ret = test_routine(); \ + if (ret >= 0) \ + return check_shash_driver_masking(NULL /* tfm */, this_cra_name, \ + this_cra_driver_name); \ + else { \ + wc_test_render_error_message("linuxkm_test_" #name " failed: ", \ + ret); \ + return WC_TEST_RET_DEC_EC(ret); \ + } \ +} \ + \ +struct wc_swallow_the_semicolon + #define WC_LINUXKM_SHA3_IMPLEMENT(name, digest_size, block_size, \ this_cra_name, this_cra_driver_name, \ init_f, update_f, final_f, \ @@ -699,35 +862,46 @@ static int linuxkm_test_ ## name(void) { \ struct wc_swallow_the_semicolon #ifdef LINUXKM_LKCAPI_REGISTER_SHA1 - WC_LINUXKM_SHA_IMPLEMENT(sha1, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, + WC_LINUXKM_SHA1_IMPLEMENT(sha1, WC_SHA_DIGEST_SIZE, WC_SHA_BLOCK_SIZE, WOLFKM_SHA1_NAME, WOLFKM_SHA1_DRIVER, wc_InitSha, wc_ShaUpdate, wc_ShaFinal, wc_ShaFree, sha_test); #endif +#ifndef WC_SHA256_W_SIZE + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) +#endif +#ifndef WC_SHA512_W_SIZE + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) +#endif + #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_224 - WC_LINUXKM_SHA_IMPLEMENT(sha2_224, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_224, WC_SHA224_DIGEST_SIZE, WC_SHA224_BLOCK_SIZE, + WC_SHA256_W_SIZE, WOLFKM_SHA2_224_NAME, WOLFKM_SHA2_224_DRIVER, wc_InitSha224, wc_Sha224Update, wc_Sha224Final, wc_Sha224Free, sha224_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_256 - WC_LINUXKM_SHA_IMPLEMENT(sha2_256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_256, WC_SHA256_DIGEST_SIZE, WC_SHA256_BLOCK_SIZE, + WC_SHA256_W_SIZE, WOLFKM_SHA2_256_NAME, WOLFKM_SHA2_256_DRIVER, wc_InitSha256, wc_Sha256Update, wc_Sha256Final, wc_Sha256Free, sha256_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_384 - WC_LINUXKM_SHA_IMPLEMENT(sha2_384, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_384, WC_SHA384_DIGEST_SIZE, WC_SHA384_BLOCK_SIZE, + WC_SHA512_W_SIZE, WOLFKM_SHA2_384_NAME, WOLFKM_SHA2_384_DRIVER, wc_InitSha384, wc_Sha384Update, wc_Sha384Final, wc_Sha384Free, sha384_test); #endif #ifdef LINUXKM_LKCAPI_REGISTER_SHA2_512 - WC_LINUXKM_SHA_IMPLEMENT(sha2_512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, + WC_LINUXKM_SHA2_IMPLEMENT(sha2_512, WC_SHA512_DIGEST_SIZE, WC_SHA512_BLOCK_SIZE, + WC_SHA512_W_SIZE, WOLFKM_SHA2_512_NAME, WOLFKM_SHA2_512_DRIVER, wc_InitSha512, wc_Sha512Update, wc_Sha512Final, wc_Sha512Free, sha512_test); diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 4c3d4fd2eb..16a56bc757 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -747,7 +747,7 @@ static int InitSha256(wc_Sha256* sha256) sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1329,7 +1329,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1482,7 +1482,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, sha256->devCtx = NULL; #endif #ifdef WOLFSSL_SMALL_STACK_CACHE - sha256->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha256->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_DIGEST); if (sha256->W == NULL) return MEMORY_E; @@ -1570,12 +1570,12 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) word32* W; - W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha256->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word32 W[WC_SHA256_BLOCK_SIZE]; + word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; #endif /* Copy context->state[] to working vars */ @@ -1616,7 +1616,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #endif return 0; } -#else +#else /* SHA256_MANY_REGISTERS */ /* SHA256 version that keeps all data in registers */ #define SCHED1(j) (W[j] = *((word32*)&data[j*sizeof(word32)])) #define SCHED(j) ( \ @@ -1644,7 +1644,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef USE_SLOW_SHA256 int j; #endif - word32 W[WC_SHA256_BLOCK_SIZE/sizeof(word32)]; + word32 W[WC_SHA256_W_SIZE / sizeof(word32)]; /* Copy digest to working vars */ S[0] = sha256->digest[0]; @@ -1693,7 +1693,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, return 0; } #endif /* SHA256_MANY_REGISTERS */ -#endif +#endif /* NEED_SOFT_SHA256 */ /* End wc_ software implementation */ #ifdef XTRANSFORM @@ -2484,7 +2484,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, #ifdef WOLFSSL_SMALL_STACK_CACHE if (sha224->W == NULL) { - sha224->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + sha224->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, sha224->heap, DYNAMIC_TYPE_DIGEST); if (sha224->W == NULL) return MEMORY_E; @@ -3054,7 +3054,7 @@ int wc_Sha224_Grow(wc_Sha224* sha224, const byte* in, int inSz) XMEMCPY(dst, src, sizeof(wc_Sha224)); #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha224)); @@ -3208,7 +3208,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) #ifdef WOLFSSL_SMALL_STACK_CACHE - dst->W = (word32*)XMALLOC(sizeof(word32) * WC_SHA256_BLOCK_SIZE, + dst->W = (word32*)XMALLOC(WC_SHA256_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha256)); diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 311ff37e43..6a6bde5a03 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -1714,7 +1714,7 @@ static int InitSha512_Family(wc_Sha512* sha512, void* heap, int devId, * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha512->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha512->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, sha512->heap, DYNAMIC_TYPE_DIGEST); if (sha512->W == NULL) return MEMORY_E; @@ -1890,11 +1890,11 @@ static int _Transform_Sha512(wc_Sha512* sha512) return BAD_FUNC_ARG; #elif defined(WOLFSSL_SMALL_STACK) word64* W; - W = (word64*) XMALLOC(sizeof(word64) * 16, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); + W = (word64*)XMALLOC(WC_SHA512_W_SIZE, sha512->heap, DYNAMIC_TYPE_TMP_BUFFER); if (W == NULL) return MEMORY_E; #else - word64 W[16]; + word64 W[WC_SHA512_W_SIZE / sizeof(word64)]; #endif /* Copy digest to working vars */ @@ -2759,7 +2759,7 @@ static int InitSha384(wc_Sha384* sha384) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - sha384->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + sha384->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, sha384->heap, DYNAMIC_TYPE_DIGEST); if (sha384->W == NULL) return MEMORY_E; @@ -3117,7 +3117,7 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha512)); @@ -3559,7 +3559,7 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) * _Transform_Sha512() with additional buffer space used by * wc_Sha512Transform(). */ - dst->W = (word64 *)XMALLOC((sizeof(word64) * 16) + WC_SHA384_BLOCK_SIZE, + dst->W = (word64 *)XMALLOC(WC_SHA512_W_SIZE, dst->heap, DYNAMIC_TYPE_DIGEST); if (dst->W == NULL) { XMEMSET(dst, 0, sizeof(wc_Sha384)); diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 8c0f49b30c..e78f0a80b9 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -199,7 +199,7 @@ struct wc_Sha256 { int sha_method; #endif -#endif +#endif /* !FREESCALE_LTC_SHA etc. */ void* heap; #ifdef WOLFSSL_PIC32MZ_HASH hashUpdCache cache; /* cache for updates */ @@ -207,9 +207,14 @@ struct wc_Sha256 { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ +#ifdef SHA256_MANY_REGISTERS + #define WC_SHA256_W_SIZE WC_SHA256_BLOCK_SIZE +#else /* !SHA256_MANY_REGISTERS */ + #define WC_SHA256_W_SIZE (sizeof(word32) * WC_SHA256_BLOCK_SIZE) +#endif /* !SHA256_MANY_REGISTERS */ #ifdef WOLFSSL_SMALL_STACK_CACHE word32* W; -#endif /* !FREESCALE_LTC_SHA && !STM32_HASH_SHA2 */ +#endif /* WOLFSSL_SMALL_STACK_CACHE */ #ifdef WOLFSSL_DEVCRYPTO_HASH WC_CRYPTODEV ctx; #endif diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 5f2ea96f21..c8bc8ef047 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -166,9 +166,11 @@ struct wc_Sha512 { WC_ASYNC_DEV asyncDev; #endif /* WOLFSSL_ASYNC_CRYPT */ #ifdef WOLFSSL_SMALL_STACK_CACHE + #define WC_SHA512_W_SIZE ((sizeof(word64) * 16) + WC_SHA512_BLOCK_SIZE) word64* W; -#endif - +#else /* !WOLFSSL_SMALL_STACK_CACHE */ + #define WC_SHA512_W_SIZE (sizeof(word64) * 16) +#endif /* !WOLFSSL_SMALL_STACK_CACHE */ #if defined(WOLFSSL_ESP32_CRYPT) && \ !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) && \ (!defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) || \