From 373b45cd7a3f4688673e794e5f9fff96c0d1f628 Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 13 Apr 2026 14:53:22 -0700 Subject: [PATCH 1/3] Fix dangling secure_renegotiation pointer after TLSX_FreeAll ssl->secure_renegotiation caches a pointer into extension data owned by the ssl->extensions list. Three call sites free that list via TLSX_FreeAll without NULLing the cached pointer, leaving it dangling: - wolfSSL_clear() - FreeHandshakeResources() (TLSX_FreeAll branch) - wolfSSL_ResourceFree() After wolfSSL_clear(), calling wolfSSL_SSL_get_secure_renegotiation_support() reads the freed SecureRenegotiation struct. Confirmed heap-use-after-free under ASan with nginx, haproxy, and openssl-compat build profiles. NULL the pointer at all three sites. Add regression test covering the wolfSSL_clear path. --- src/internal.c | 9 +++++++++ src/ssl.c | 4 ++++ tests/api.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/internal.c b/src/internal.c index 267c75a5d65..25acacf8826 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8952,6 +8952,11 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) #ifdef HAVE_TLS_EXTENSIONS #if !defined(NO_TLS) TLSX_FreeAll(ssl->extensions, ssl->heap); + ssl->extensions = NULL; + #if defined(HAVE_SECURE_RENEGOTIATION) \ + || defined(HAVE_SERVER_RENEGOTIATION_INFO) + ssl->secure_renegotiation = NULL; + #endif #endif /* !NO_TLS */ #ifdef HAVE_ALPN if (ssl->alpn_peer_requested != NULL) { @@ -9315,6 +9320,10 @@ void FreeHandshakeResources(WOLFSSL* ssl) /* Some extensions need to be kept for post-handshake querying. */ TLSX_FreeAll(ssl->extensions, ssl->heap); ssl->extensions = NULL; + #if defined(HAVE_SECURE_RENEGOTIATION) \ + || defined(HAVE_SERVER_RENEGOTIATION_INFO) + ssl->secure_renegotiation = NULL; + #endif #else #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) TLSX_Remove(&ssl->extensions, TLSX_SIGNATURE_ALGORITHMS, ssl->heap); diff --git a/src/ssl.c b/src/ssl.c index 58cd6701c02..284b09c6013 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -10051,6 +10051,10 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #if defined(HAVE_TLS_EXTENSIONS) && !defined(NO_TLS) TLSX_FreeAll(ssl->extensions, ssl->heap); ssl->extensions = NULL; + #if defined(HAVE_SECURE_RENEGOTIATION) \ + || defined(HAVE_SERVER_RENEGOTIATION_INFO) + ssl->secure_renegotiation = NULL; + #endif #endif if (ssl->keys.encryptionOn) { diff --git a/tests/api.c b/tests/api.c index 0e9cbe29824..e53ec703854 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10203,6 +10203,38 @@ static int test_wolfSSL_wolfSSL_UseSecureRenegotiation(void) return EXPECT_RESULT(); } +/* TLSX_FreeAll frees the SecureRenegotiation struct but the cached pointer + * ssl->secure_renegotiation was not cleared, causing a use-after-free when + * queried after wolfSSL_clear(). */ +static int test_wolfSSL_clear_secure_renegotiation(void) +{ + EXPECT_DECLS; +#if (defined(HAVE_SECURE_RENEGOTIATION) || \ + defined(HAVE_SERVER_RENEGOTIATION_INFO)) && \ + (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX *ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + WOLFSSL *ssl = wolfSSL_new(ctx); + + ExpectNotNull(ctx); + ExpectNotNull(ssl); + + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSecureRenegotiation(ssl)); + ExpectNotNull(ssl->secure_renegotiation); + if (ssl->secure_renegotiation != NULL) + ssl->secure_renegotiation->enabled = 1; + + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_clear(ssl)); + ExpectNull(ssl->secure_renegotiation); + ExpectIntEQ(WOLFSSL_FAILURE, + wolfSSL_SSL_get_secure_renegotiation_support(ssl)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + /* Test reconnecting with a different ciphersuite after a renegotiation. */ static int test_wolfSSL_SCR_Reconnect(void) { @@ -35770,6 +35802,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_TLSX_TCA_Find), TEST_DECL(test_TLSX_SNI_GetSize_overflow), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), + TEST_DECL(test_wolfSSL_clear_secure_renegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), TEST_DECL(test_wolfSSL_SCR_check_enabled), TEST_DECL(test_tls_ext_duplicate), From 6f37b1775760b2a9f8172d34395d28fc2f7e1b8b Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Tue, 21 Apr 2026 02:56:36 +0200 Subject: [PATCH 2/3] Address Copilot suggestions --- src/internal.c | 12 ++++++------ tests/api.c | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/internal.c b/src/internal.c index 25acacf8826..b950af6198c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8953,10 +8953,10 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) #if !defined(NO_TLS) TLSX_FreeAll(ssl->extensions, ssl->heap); ssl->extensions = NULL; - #if defined(HAVE_SECURE_RENEGOTIATION) \ - || defined(HAVE_SERVER_RENEGOTIATION_INFO) +#if defined(HAVE_SECURE_RENEGOTIATION) \ + || defined(HAVE_SERVER_RENEGOTIATION_INFO) ssl->secure_renegotiation = NULL; - #endif +#endif #endif /* !NO_TLS */ #ifdef HAVE_ALPN if (ssl->alpn_peer_requested != NULL) { @@ -9320,10 +9320,10 @@ void FreeHandshakeResources(WOLFSSL* ssl) /* Some extensions need to be kept for post-handshake querying. */ TLSX_FreeAll(ssl->extensions, ssl->heap); ssl->extensions = NULL; - #if defined(HAVE_SECURE_RENEGOTIATION) \ - || defined(HAVE_SERVER_RENEGOTIATION_INFO) +#if defined(HAVE_SECURE_RENEGOTIATION) \ + || defined(HAVE_SERVER_RENEGOTIATION_INFO) ssl->secure_renegotiation = NULL; - #endif +#endif #else #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) TLSX_Remove(&ssl->extensions, TLSX_SIGNATURE_ALGORITHMS, ssl->heap); diff --git a/tests/api.c b/tests/api.c index e53ec703854..56189826692 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10215,6 +10215,7 @@ static int test_wolfSSL_clear_secure_renegotiation(void) !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) WOLFSSL_CTX *ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); WOLFSSL *ssl = wolfSSL_new(ctx); + int support; ExpectNotNull(ctx); ExpectNotNull(ssl); @@ -10225,9 +10226,9 @@ static int test_wolfSSL_clear_secure_renegotiation(void) ssl->secure_renegotiation->enabled = 1; ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_clear(ssl)); + support = wolfSSL_SSL_get_secure_renegotiation_support(ssl); ExpectNull(ssl->secure_renegotiation); - ExpectIntEQ(WOLFSSL_FAILURE, - wolfSSL_SSL_get_secure_renegotiation_support(ssl)); + ExpectIntEQ(WOLFSSL_FAILURE, support); wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); From 389d15fa459b805d5181baecbec7832665b6cece Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Tue, 21 Apr 2026 03:30:39 +0200 Subject: [PATCH 3/3] Fix compile error --- tests/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api.c b/tests/api.c index 56189826692..35ddd983718 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10215,7 +10215,7 @@ static int test_wolfSSL_clear_secure_renegotiation(void) !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) WOLFSSL_CTX *ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); WOLFSSL *ssl = wolfSSL_new(ctx); - int support; + long support; ExpectNotNull(ctx); ExpectNotNull(ssl);