@@ -2702,3 +2702,82 @@ int test_wolfSSL_EVP_mdc2(void)
27022702 return EXPECT_RESULT ();
27032703}
27042704
2705+ /* Test for integer overflow in EVP AEAD AAD accumulation.
2706+ *
2707+ * wolfSSL_EVP_CipherUpdate_GCM_AAD (and the CCM/ARIA variants) compute
2708+ * allocation sizes as (ctx->authInSz + inl) where both operands are int.
2709+ * Repeated AAD calls can accumulate authInSz to a value where adding inl
2710+ * overflows the signed int sum. The overflowed value is then cast to size_t
2711+ * for XMALLOC/XREALLOC, producing either:
2712+ * - A huge allocation on 64-bit (masking the bug as MEMORY_E), or
2713+ * - A potential heap buffer overflow on 32-bit if the wrapped size is small
2714+ * enough to succeed but the subsequent XMEMCPY uses the original large
2715+ * authInSz offset.
2716+ *
2717+ * This test simulates the overflow condition by directly setting authInSz near
2718+ * INT_MAX after legitimate initialization, then calling EVP_EncryptUpdate with
2719+ * AAD that triggers the overflow. A properly-fixed implementation should detect
2720+ * the overflow and return WOLFSSL_FAILURE before attempting the allocation.
2721+ */
2722+ int test_evp_cipher_aead_aad_overflow (void )
2723+ {
2724+ EXPECT_DECLS ;
2725+ #if defined(OPENSSL_EXTRA ) && !defined(NO_AES ) && defined(HAVE_AESGCM ) && \
2726+ defined(WOLFSSL_AES_256 ) && !defined(HAVE_SELFTEST ) && !defined(HAVE_FIPS ) && \
2727+ !defined(WOLFSSL_AESGCM_STREAM )
2728+
2729+ WOLFSSL_EVP_CIPHER_CTX * ctx = NULL ;
2730+ byte key [32 ] = {0 };
2731+ byte iv [12 ] = {0 };
2732+ byte aad [32 ] = {0 };
2733+ int outl = 0 ;
2734+ int savedAuthInSz ;
2735+
2736+ /* Initialize AES-256-GCM encryption context */
2737+ ctx = EVP_CIPHER_CTX_new ();
2738+ ExpectNotNull (ctx );
2739+ ExpectIntEQ (WOLFSSL_SUCCESS , EVP_EncryptInit_ex (ctx , EVP_aes_256_gcm (),
2740+ NULL , key , iv ));
2741+
2742+ /* Feed a small legitimate AAD to allocate authIn */
2743+ ExpectIntEQ (WOLFSSL_SUCCESS , EVP_EncryptUpdate (ctx , NULL , & outl , aad , 16 ));
2744+
2745+ if (EXPECT_SUCCESS ()) {
2746+ ExpectIntEQ (ctx -> authInSz , 16 );
2747+
2748+ /* Simulate accumulated AAD near INT_MAX.
2749+ * In a real attack scenario, an attacker controlling AAD input to a
2750+ * server could accumulate authInSz toward INT_MAX through many calls.
2751+ * We set it directly to avoid needing ~2GB of actual allocations.
2752+ */
2753+ savedAuthInSz = ctx -> authInSz ;
2754+ ctx -> authInSz = INT_MAX - 16 ;
2755+
2756+ /* Attempt AAD update that causes overflow:
2757+ * (INT_MAX - 16) + 32 = INT_MAX + 16
2758+ * This overflows signed int (undefined behavior in C). The result:
2759+ * - As signed int: wraps to INT_MIN + 15 (on 2's complement)
2760+ * - Cast to size_t on 64-bit: ~0xFFFFFFFF8000000F (huge)
2761+ * - Cast to size_t on 32-bit: ~0x8000000F (~2GB)
2762+ *
2763+ * With no overflow check, the code proceeds to XREALLOC with the
2764+ * wrapped size. On 64-bit this fails (MEMORY_E), accidentally
2765+ * preventing corruption. On 32-bit, if the allocation succeeds,
2766+ * XMEMCPY writes at offset (INT_MAX - 16) into the buffer, causing
2767+ * heap corruption.
2768+ */
2769+ ExpectIntNE (WOLFSSL_SUCCESS ,
2770+ EVP_EncryptUpdate (ctx , NULL , & outl , aad , 32 ));
2771+
2772+ /* Restore authInSz so cleanup doesn't operate on corrupted state */
2773+ if (ctx != NULL )
2774+ ctx -> authInSz = savedAuthInSz ;
2775+ }
2776+
2777+ EVP_CIPHER_CTX_free (ctx );
2778+
2779+ #endif /* OPENSSL_EXTRA && HAVE_AESGCM && WOLFSSL_AES_256 */
2780+ return EXPECT_RESULT ();
2781+ }
2782+
2783+
0 commit comments