From fe564c832c920fe5debc014dcfd4eb57c8633ede Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Nov 2026 12:24:57 +0100 Subject: [PATCH 1/4] Fix AES context alignment in heap allocations --- wolfcrypt/src/aes.c | 69 +++++++++++++++++++++++++++++++++++-------- wolfcrypt/test/test.c | 9 ++++-- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index d3daf08842b..56737bf13fd 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -16993,6 +16993,37 @@ int wc_AesCcmEncrypt_ex(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* HAVE_AESCCM */ +/* Aes contains 16-byte-aligned members, but malloc on 32-bit Arm may only + * return an 8-byte-aligned pointer. Keep the original allocation for XFREE. */ +static WC_MAYBE_UNUSED Aes* AesAlloc(void* heap) +{ +#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) + const size_t align = WC_ALIGNOF(Aes); + byte* raw = (byte*)XMALLOC(sizeof(Aes) + align - 1 + sizeof(void*), heap, + DYNAMIC_TYPE_AES); + wc_ptr_t p; + + if (raw == NULL) + return NULL; + p = ((wc_ptr_t)(raw + sizeof(void*)) + align - 1) & + ~((wc_ptr_t)align - 1); + ((void**)p)[-1] = raw; + return (Aes*)p; +#else + return (Aes*)XMALLOC(sizeof(Aes), heap, DYNAMIC_TYPE_AES); +#endif +} + +static WC_MAYBE_UNUSED void AesAllocFree(Aes* aes, void* heap) +{ +#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) + if (aes != NULL) + XFREE(((void**)aes)[-1], heap, DYNAMIC_TYPE_AES); +#else + XFREE(aes, heap, DYNAMIC_TYPE_AES); +#endif +} + #ifndef WC_NO_CONSTRUCTORS #define AES_NEW_INIT_PLAIN 0 @@ -17006,7 +17037,7 @@ static Aes* _AesNew_common(void* heap, int devId, int *result_code, int idLen, const char* label) { int ret; - Aes* aes = (Aes*)XMALLOC(sizeof(Aes), heap, DYNAMIC_TYPE_AES); + Aes* aes = AesAlloc(heap); if (aes == NULL) { ret = MEMORY_E; } @@ -17040,7 +17071,7 @@ static Aes* _AesNew_common(void* heap, int devId, int *result_code, break; } if (ret != 0) { - XFREE(aes, heap, DYNAMIC_TYPE_AES); + AesAllocFree(aes, heap); aes = NULL; } } @@ -17085,7 +17116,7 @@ int wc_AesDelete(Aes *aes, Aes** aes_p) return BAD_FUNC_ARG; heap = aes->heap; wc_AesFree(aes); - XFREE(aes, heap, DYNAMIC_TYPE_AES); + AesAllocFree(aes, heap); if (aes_p != NULL) *aes_p = NULL; return 0; @@ -18673,8 +18704,7 @@ int wc_AesKeyWrap(const byte* key, word32 keySz, const byte* in, word32 inSz, return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK - if ((aes = (Aes *)XMALLOC(sizeof *aes, NULL, - DYNAMIC_TYPE_AES)) == NULL) + if ((aes = AesAlloc(NULL)) == NULL) return MEMORY_E; #endif @@ -18693,7 +18723,9 @@ int wc_AesKeyWrap(const byte* key, word32 keySz, const byte* in, word32 inSz, wc_AesFree(aes); out: - WC_FREE_VAR_EX(aes, NULL, DYNAMIC_TYPE_AES); +#ifdef WOLFSSL_SMALL_STACK + AesAllocFree(aes, NULL); +#endif return ret; } @@ -18868,8 +18900,7 @@ int wc_AesKeyUnWrap(const byte* key, word32 keySz, const byte* in, word32 inSz, return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK - if ((aes = (Aes *)XMALLOC(sizeof *aes, NULL, - DYNAMIC_TYPE_AES)) == NULL) + if ((aes = AesAlloc(NULL)) == NULL) return MEMORY_E; #endif @@ -18889,7 +18920,9 @@ int wc_AesKeyUnWrap(const byte* key, word32 keySz, const byte* in, word32 inSz, wc_AesFree(aes); out: - WC_FREE_VAR_EX(aes, NULL, DYNAMIC_TYPE_AES); +#ifdef WOLFSSL_SMALL_STACK + AesAllocFree(aes, NULL); +#endif return ret; } @@ -19000,7 +19033,10 @@ int wc_AesKeyWrap_Pad(const byte* key, word32 keySz, const byte* in, return BAD_FUNC_ARG; } - WC_ALLOC_VAR_EX(aes, Aes, 1, NULL, DYNAMIC_TYPE_AES, return MEMORY_E); +#ifdef WOLFSSL_SMALL_STACK + if ((aes = AesAlloc(NULL)) == NULL) + return MEMORY_E; +#endif ret = wc_AesInit(aes, NULL, INVALID_DEVID); if (ret != 0) { @@ -19018,7 +19054,9 @@ int wc_AesKeyWrap_Pad(const byte* key, word32 keySz, const byte* in, wc_AesFree(aes); out: - WC_FREE_VAR_EX(aes, NULL, DYNAMIC_TYPE_AES); +#ifdef WOLFSSL_SMALL_STACK + AesAllocFree(aes, NULL); +#endif return ret; } @@ -19173,7 +19211,10 @@ int wc_AesKeyUnWrap_Pad(const byte* key, word32 keySz, const byte* in, return BAD_FUNC_ARG; } - WC_ALLOC_VAR_EX(aes, Aes, 1, NULL, DYNAMIC_TYPE_AES, return MEMORY_E); +#ifdef WOLFSSL_SMALL_STACK + if ((aes = AesAlloc(NULL)) == NULL) + return MEMORY_E; +#endif ret = wc_AesInit(aes, NULL, INVALID_DEVID); if (ret != 0) { @@ -19191,7 +19232,9 @@ int wc_AesKeyUnWrap_Pad(const byte* key, word32 keySz, const byte* in, wc_AesFree(aes); out: - WC_FREE_VAR_EX(aes, NULL, DYNAMIC_TYPE_AES); +#ifdef WOLFSSL_SMALL_STACK + AesAllocFree(aes, NULL); +#endif return ret; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 74d42b0a6d6..adf3209dac2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -17698,6 +17698,10 @@ static wc_test_ret_t aes_key_size_test(void) aes = wc_AesNew(HEAP_HINT, devId, &ret); if (aes == NULL) return WC_TEST_RET_ENC_EC(ret); +#ifdef WC_ALIGNOF + if (!WC_IS_ALIGNED(aes, WC_ALIGNOF(Aes))) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif #else ret = wc_AesInit(aes, HEAP_HINT, devId); /* 0 check OK for FIPSv1 */ @@ -26904,9 +26908,8 @@ typedef struct keywrapVector { * byte alignment through its ALIGN16 members, which XMALLOC does not guarantee * on 32-bit targets, where malloc only promises 8 -- and wc_AesSetIV() lowers * to an alignment-qualified NEON store when building for armv8-a+crypto, which - * faults on an under-aligned object. Neither a plain local nor a plain XMALLOC - * (wc_AesNew() included) satisfies both, so allocate with headroom, align by - * hand, and keep the base pointer for XFREE. */ + * faults on an under-aligned object. Allocate with headroom, align by hand, + * and keep the base pointer for XFREE. */ #define AESKEYWRAP_AES_ALIGN 16 static wc_test_ret_t aeskeywrap_caller_aes_test(const keywrapVector* v, byte* output, word32 outputSz, From d9cbe2a6186c2f93c5b45243645b866faee8b115 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Nov 2026 12:52:21 +0100 Subject: [PATCH 2/4] Fix AES whitebox cleanup for aligned allocations --- tests/unit-mcdc/test_aes_whitebox.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit-mcdc/test_aes_whitebox.c b/tests/unit-mcdc/test_aes_whitebox.c index b84cbee4aec..59c9a69e4a1 100644 --- a/tests/unit-mcdc/test_aes_whitebox.c +++ b/tests/unit-mcdc/test_aes_whitebox.c @@ -218,10 +218,8 @@ static void wb_ghash_update(void) { WB_NOTE("AESGCM stream off; GHASH_UPDATE ski * ------------------------------------------------------------------------- */ static void wb_release(Aes* aes) { - if (aes != NULL) { - wc_AesFree(aes); - XFREE(aes, NULL, DYNAMIC_TYPE_AES); - } + if (aes != NULL) + (void)wc_AesDelete(aes, NULL); } static void wb_aesnew_common(void) From ce91bb64fa9ee1ed0e3e87e1a06ee8d7d12ba281 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Nov 2026 12:55:08 +0100 Subject: [PATCH 3/4] Limit AES alignment assertion to non-FIPS builds --- wolfcrypt/test/test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index adf3209dac2..2a2a7b35996 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -17698,7 +17698,8 @@ static wc_test_ret_t aes_key_size_test(void) aes = wc_AesNew(HEAP_HINT, devId, &ret); if (aes == NULL) return WC_TEST_RET_ENC_EC(ret); -#ifdef WC_ALIGNOF +#if defined(WC_ALIGNOF) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) if (!WC_IS_ALIGNED(aes, WC_ALIGNOF(Aes))) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif From 7da1ac76cf9f7a2ecedcb5f52f59bb4e0ca9b401 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Nov 2026 14:23:31 +0100 Subject: [PATCH 4/4] Keep AES allocation helpers available to TI key wrap --- wolfcrypt/src/aes.c | 65 ++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 56737bf13fd..85c772cdd2e 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -392,6 +392,40 @@ int wc_local_AesGcmCheckTagSz(word32 authTagSz) { } #endif /* HAVE_AESGCM */ +/* Aes contains 16-byte-aligned members, but malloc on 32-bit Arm may only + * return an 8-byte-aligned pointer. Keep the original allocation for XFREE. + * The TI backend uses its existing plain allocation path. */ +static WC_MAYBE_UNUSED Aes* AesAlloc(void* heap) +{ +#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) && \ + !defined(WOLFSSL_TI_CRYPT) + const size_t align = WC_ALIGNOF(Aes); + byte* raw = (byte*)XMALLOC(sizeof(Aes) + align - 1 + sizeof(void*), heap, + DYNAMIC_TYPE_AES); + wc_ptr_t p; + + if (raw == NULL) + return NULL; + p = ((wc_ptr_t)(raw + sizeof(void*)) + align - 1) & + ~((wc_ptr_t)align - 1); + ((void**)p)[-1] = raw; + return (Aes*)p; +#else + return (Aes*)XMALLOC(sizeof(Aes), heap, DYNAMIC_TYPE_AES); +#endif +} + +static WC_MAYBE_UNUSED void AesAllocFree(Aes* aes, void* heap) +{ +#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) && \ + !defined(WOLFSSL_TI_CRYPT) + if (aes != NULL) + XFREE(((void**)aes)[-1], heap, DYNAMIC_TYPE_AES); +#else + XFREE(aes, heap, DYNAMIC_TYPE_AES); +#endif +} + #if defined(WOLFSSL_TI_CRYPT) #include @@ -16993,37 +17027,6 @@ int wc_AesCcmEncrypt_ex(Aes* aes, byte* out, const byte* in, word32 sz, #endif /* HAVE_AESCCM */ -/* Aes contains 16-byte-aligned members, but malloc on 32-bit Arm may only - * return an 8-byte-aligned pointer. Keep the original allocation for XFREE. */ -static WC_MAYBE_UNUSED Aes* AesAlloc(void* heap) -{ -#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) - const size_t align = WC_ALIGNOF(Aes); - byte* raw = (byte*)XMALLOC(sizeof(Aes) + align - 1 + sizeof(void*), heap, - DYNAMIC_TYPE_AES); - wc_ptr_t p; - - if (raw == NULL) - return NULL; - p = ((wc_ptr_t)(raw + sizeof(void*)) + align - 1) & - ~((wc_ptr_t)align - 1); - ((void**)p)[-1] = raw; - return (Aes*)p; -#else - return (Aes*)XMALLOC(sizeof(Aes), heap, DYNAMIC_TYPE_AES); -#endif -} - -static WC_MAYBE_UNUSED void AesAllocFree(Aes* aes, void* heap) -{ -#if defined(WOLFSSL_USE_ALIGN) && defined(WC_ALIGNOF) - if (aes != NULL) - XFREE(((void**)aes)[-1], heap, DYNAMIC_TYPE_AES); -#else - XFREE(aes, heap, DYNAMIC_TYPE_AES); -#endif -} - #ifndef WC_NO_CONSTRUCTORS #define AES_NEW_INIT_PLAIN 0