From 1653ecd07ec498e020a0c8c5a249dfa07ef494ac Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Mon, 30 Mar 2026 18:08:38 +0200 Subject: [PATCH] Fix DTLS 1.3 extSz out-of-bounds and word16 truncation on oversized certificate chains --- src/dtls13.c | 8 ++++ src/tls13.c | 6 ++- tests/api/test_dtls.c | 95 +++++++++++++++++++++++++++++++++++++++++++ tests/api/test_dtls.h | 6 ++- 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 644af22617..6f0fda11cd 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1042,6 +1042,10 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) Dtls13FreeFragmentsBuffer(ssl); return outputSz; } + if ((word32)outputSz > WOLFSSL_MAX_16BIT) { + Dtls13FreeFragmentsBuffer(ssl); + return BUFFER_E; + } ret = CheckAvailableSize(ssl, outputSz); if (ret != 0) { @@ -1636,6 +1640,10 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) if (!w64IsZero(r->epoch)) sendSz += MAX_MSG_EXTRA; + if ((word32)sendSz > WOLFSSL_MAX_16BIT) { + return BUFFER_E; + } + ret = CheckAvailableSize(ssl, sendSz); if (ret != 0) return ret; diff --git a/src/tls13.c b/src/tls13.c index 8bce848a50..a591532be2 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -9291,7 +9291,7 @@ static int SendTls13Certificate(WOLFSSL* ssl) break; #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ !defined(NO_WOLFSSL_SERVER) - if (MAX_CERT_EXTENSIONS > extIdx) + if (extIdx + 1 < MAX_CERT_EXTENSIONS) extIdx++; #endif } @@ -9324,6 +9324,10 @@ static int SendTls13Certificate(WOLFSSL* ssl) /* DTLS1.3 uses a separate variable and logic for fragments */ ssl->options.buildingMsg = 0; ssl->fragOffset = 0; + if ((word32)sendSz > WOLFSSL_MAX_16BIT || i > WOLFSSL_MAX_16BIT) { + WOLFSSL_MSG("Send Cert DTLS size exceeds word16"); + return BUFFER_E; + } ret = Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)i, certificate, 1); } diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 5bd296fcbf..c6e8f7cc28 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -2814,3 +2814,98 @@ int test_dtls13_no_session_id_echo(void) #endif return EXPECT_RESULT(); } + +/* Test that a DTLS 1.3 handshake with an oversized certificate chain does + * not crash or cause out-of-bounds access in SendTls13Certificate. */ +int test_dtls13_oversized_cert_chain(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \ + && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + XFILE f = XBADFILE; + long sz = 0; + byte *cert = NULL; + byte *chain = NULL; + int copies, off, i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + /* Read server cert */ + f = XFOPEN(svrCertFile, "rb"); + ExpectTrue(f != XBADFILE); + if (EXPECT_SUCCESS()) { + (void)XFSEEK(f, 0, XSEEK_END); + sz = XFTELL(f); + (void)XFSEEK(f, 0, XSEEK_SET); + } + ExpectTrue(sz > 0); + cert = (byte*)XMALLOC((size_t)(sz + 1), NULL, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(cert); + if (EXPECT_SUCCESS()) + ExpectIntEQ((int)XFREAD(cert, 1, (size_t)sz, f), (int)sz); + if (f != XBADFILE) + XFCLOSE(f); + + /* Build an oversized chain by duplicating the cert */ + copies = EXPECT_SUCCESS() ? (int)(70000 / sz) + 2 : 0; + chain = (byte*)XMALLOC((size_t)(sz * copies + 1), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(chain); + off = 0; + if (EXPECT_SUCCESS()) { + for (i = 0; i < copies; i++) { + XMEMCPY(chain + off, cert, (size_t)sz); + off += (int)sz; + } + } + + /* Server context: load the oversized chain */ + ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfDTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_buffer(ctx_s, + chain, (long)off), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + wolfSSL_SetIORecv(ctx_s, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_s, test_memio_write_cb); + } + + /* Client context: no verification (chain certs are duplicates) */ + ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfDTLSv1_3_client_method())); + if (EXPECT_SUCCESS()) { + wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_SetIORecv(ctx_c, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_c, test_memio_write_cb); + } + + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + if (EXPECT_SUCCESS()) { + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + } + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + if (EXPECT_SUCCESS()) { + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + } + + /* Handshake must not crash. If SendTls13Certificate mishandles the + * oversized chain this will trigger a wild pointer dereference or stack + * overflow resulting with the test failing. + * The correct behaviour either returns BUFFER_E or succeeds + * if the build config truncated the chain during loading. */ + (void)test_memio_do_handshake(ssl_c, ssl_s, 10, NULL); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(chain, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 01f47ccda9..3a1e083a60 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -53,6 +53,7 @@ int test_dtls_mtu_fragment_headroom(void); int test_dtls_mtu_split_messages(void); int test_dtls13_min_rtx_interval(void); int test_dtls13_no_session_id_echo(void); +int test_dtls13_oversized_cert_chain(void); #define TEST_DTLS_DECLS \ TEST_DECL_GROUP("dtls", test_dtls12_basic_connection_id), \ @@ -84,6 +85,7 @@ int test_dtls13_no_session_id_echo(void); TEST_DECL_GROUP("dtls", test_dtls_mtu_fragment_headroom), \ TEST_DECL_GROUP("dtls", test_dtls_mtu_split_messages), \ TEST_DECL_GROUP("dtls", test_dtls_memio_wolfio_stateless), \ - TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \ - TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo) + TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \ + TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo), \ + TEST_DECL_GROUP("dtls", test_dtls13_oversized_cert_chain) #endif /* TESTS_API_DTLS_H */