diff --git a/src/sniffer.c b/src/sniffer.c index fd9aa08599..e8664721b1 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -6720,12 +6720,21 @@ static int ssl_DecodePacketInternal(const byte* packet, int length, int isChain, #ifdef WOLFSSL_SNIFFER_CHAIN_INPUT struct iovec* chain; word32 i; + size_t totalLength; word32 chainSz = (word32)length; chain = (struct iovec*)packet; - length = 0; - for (i = 0; i < chainSz; i++) length += chain[i].iov_len; + totalLength = 0; + for (i = 0; i < chainSz; i++) { + size_t prev = totalLength; + totalLength += chain[i].iov_len; + if (totalLength < prev || totalLength > (size_t)INT_MAX) { + SetError(BAD_INPUT_STR, error, session, FATAL_ERROR_STATE); + return WOLFSSL_SNIFFER_ERROR; + } + } + length = (int)totalLength; tmpPacket = (byte*)XMALLOC(length, NULL, DYNAMIC_TYPE_SNIFFER_CHAIN_BUFFER); if (tmpPacket == NULL) return MEMORY_E; @@ -6733,7 +6742,7 @@ static int ssl_DecodePacketInternal(const byte* packet, int length, int isChain, length = 0; for (i = 0; i < chainSz; i++) { XMEMCPY(tmpPacket+length,chain[i].iov_base,chain[i].iov_len); - length += chain[i].iov_len; + length += (int)chain[i].iov_len; } packet = (const byte*)tmpPacket; #else diff --git a/tests/api.c b/tests/api.c index fd16b4d5b2..3fc95c52c7 100644 --- a/tests/api.c +++ b/tests/api.c @@ -159,6 +159,12 @@ #include "wolfssl/internal.h" #endif +#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) + #include + #include + #include +#endif + /* include misc.c here regardless of NO_INLINE, because misc.c implementations * have default (hidden) visibility, and in the absence of visibility, it's * benign to mask out the library implementation. @@ -33786,6 +33792,46 @@ int test_wc_LmsKey_reload_cache(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) +static int test_sniffer_chain_input_overflow(void) +{ + EXPECT_DECLS; + struct iovec chain[3]; + byte* data = NULL; + char error[WOLFSSL_MAX_ERROR_SZ]; + int ret; + byte dummy[1] = {0}; + + /* Test 1: iov_len values that sum to more than INT_MAX. + * Before the fix, these size_t values would be truncated when accumulated + * into an int, causing an undersized allocation followed by an oversized + * copy (heap buffer overflow). After the fix, the function should detect + * the overflow and return an error without allocating or copying. */ + chain[0].iov_base = dummy; + chain[0].iov_len = (size_t)0x80000000UL; /* 2GB */ + chain[1].iov_base = dummy; + chain[1].iov_len = (size_t)0x80000000UL; /* 2GB */ + chain[2].iov_base = dummy; + chain[2].iov_len = (size_t)0x80000000UL; /* 2GB */ + + XMEMSET(error, 0, sizeof(error)); + ret = ssl_DecodePacketWithChain(chain, 3, &data, error); + ExpectIntEQ(ret, WOLFSSL_SNIFFER_ERROR); + + /* Test 2: total exactly at INT_MAX boundary should also be rejected since + * it would require a ~2GB allocation that is unreasonable for a packet. */ + chain[0].iov_len = (size_t)0x7FFFFFFFUL; /* INT_MAX */ + chain[1].iov_len = (size_t)1; + chain[2].iov_len = (size_t)0; + + XMEMSET(error, 0, sizeof(error)); + ret = ssl_DecodePacketWithChain(chain, 2, &data, error); + ExpectIntEQ(ret, WOLFSSL_SNIFFER_ERROR); + + return EXPECT_RESULT(); +} +#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_CHAIN_INPUT */ + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -34591,6 +34637,11 @@ TEST_CASE testCases[] = { TEST_DECL(test_ocsp_responder), TEST_TLS_DECLS, TEST_DECL(test_wc_DhSetNamedKey), + +#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) + TEST_DECL(test_sniffer_chain_input_overflow), +#endif + /* This test needs to stay at the end to clean up any caches allocated. */ TEST_DECL(test_wolfSSL_Cleanup) };