From d82d36b8704f21e7a353daa0e4a67a9198ba2c2d Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Fri, 25 Sep 2026 14:25:45 +0300 Subject: [PATCH 1/7] tests: drop freed SSL pointers before the next mem-fail case ExpectNotNull skips the following wolfSSL_new when wolfSSL_CTX_new fails, so wolfSSL_free ran on the previous SSL. Clear both pointers after each free. Skip the DTLS guard calls when the constructor did not produce an object. Signed-off-by: Sameeh Jubran --- tests/api/test_ssl_cert.c | 16 ++++++++++++ tests/api/test_tls_parse.c | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 1e30855b539..20c31e97281 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -4605,6 +4605,15 @@ int test_wolfSSL_dtls_api_on_dtls_object(void) ExpectNotNull(tctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); ExpectNotNull(dssl = wolfSSL_new(dctx)); ExpectNotNull(tssl = wolfSSL_new(tctx)); + /* A failed constructor leaves the later calls holding NULL. Those calls + * are not inside Expect(), so they would run anyway and dereference it. */ + if (dctx == NULL || tctx == NULL || dssl == NULL || tssl == NULL) { + wolfSSL_free(dssl); + wolfSSL_free(tssl); + wolfSSL_CTX_free(dctx); + wolfSSL_CTX_free(tctx); + return EXPECT_RESULT(); + } /* `ssl == NULL || !ssl->options.dtls` -- three vectors, one per outcome */ (void)wolfSSL_dtls_got_timeout(NULL); @@ -5142,6 +5151,13 @@ int test_wolfSSL_dtls_api_more_guards(void) XMEMSET(peer, 0, sizeof(peer)); ExpectNotNull(dctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method())); ExpectNotNull(dssl = wolfSSL_new(dctx)); + /* A failed constructor leaves the later calls holding NULL. Those calls + * are not inside Expect(), so they would run anyway and dereference it. */ + if (dctx == NULL || dssl == NULL) { + wolfSSL_free(dssl); + wolfSSL_CTX_free(dctx); + return EXPECT_RESULT(); + } /* `peer == NULL || peerSz == NULL` -- one call per operand */ (void)wolfSSL_dtls_get0_peer(NULL, &p0, &p0Sz); diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 0c908fac6f7..60d716831a7 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -528,7 +528,9 @@ int test_TLSX_TCA_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* Client side, response direction, trusted_ca_keys never requested: * unsupported extension. */ @@ -545,7 +547,9 @@ int test_TLSX_TCA_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif #endif return EXPECT_RESULT(); @@ -598,7 +602,9 @@ int test_TLSX_certtype_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -687,7 +693,9 @@ int test_TLSX_EncryptThenMac_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, server_hello, NULL), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -741,7 +749,9 @@ int test_TLSX_MFL_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, server_hello, NULL), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -779,7 +789,9 @@ int test_TLSX_THM_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -953,7 +965,9 @@ int test_TLSX_SecureRenegotiation_parse(void) (void)wolfSSL_SetAllocators(prevM, prevF, prevR); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* Client side response direction: *input == 2*TLS_FINISHED_SZ, but the * declared extension length disagrees with it -- length is the @@ -987,7 +1001,9 @@ int test_TLSX_SecureRenegotiation_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, server_hello, NULL), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -1042,7 +1058,9 @@ int test_TLSX_SupportedVersions_parse(void) &found), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* An over-long list also fails on its own: odd total length (so the * parity check already passes) but still over MAX_SV_EXT_LEN. */ @@ -1066,7 +1084,9 @@ int test_TLSX_SupportedVersions_parse(void) &found), WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* server_hello / hello_retry_request direction, client side. */ ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); @@ -1105,7 +1125,9 @@ int test_TLSX_SupportedVersions_parse(void) WC_NO_ERR_TRACE(SANITY_MSG_E)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* Downgrade bookkeeping: ssl->options.downgrade set and the connection * already sitting at TLS 1.2 minor -- vs. either being false. */ @@ -1140,7 +1162,9 @@ int test_TLSX_SupportedVersions_parse(void) &found), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -1185,7 +1209,9 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #if !defined(NO_RSA) && defined(HAVE_TLS_EXTENSIONS) && defined(WOLFSSL_TLS13) /* SignatureAlgorithmsCert: same length checks, separate extension. @@ -1220,7 +1246,9 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* TLSX_SignatureAlgorithms_MapPss(): rsa_pss_sa_algo entries whose * second byte is within [pss_sha256, pss_sha512] vs. just above it, @@ -1248,7 +1276,9 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif #endif #endif @@ -1304,7 +1334,9 @@ int test_TLSX_CSR_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #if defined(WOLFSSL_TLS13) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) /* Client side, TLS 1.3 certificate direction (RFC 8446 4.4.2): the OCSP @@ -1511,7 +1543,9 @@ int test_TLSX_PointFormat_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && \ @@ -1715,7 +1749,9 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #ifndef NO_WOLFSSL_CLIENT /* Client side response direction: SNI configured (extension and its @@ -1741,7 +1777,9 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; /* Client side, SNI not requested at all: response is an unsupported * extension. */ @@ -1756,7 +1794,9 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif /* !NO_WOLFSSL_CLIENT */ #endif return EXPECT_RESULT(); @@ -1962,7 +2002,9 @@ int test_TLSX_ValidateSupportedCurves(void) 1); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -2225,7 +2267,9 @@ int test_TLSX_SupportedGroups_parse(void) TLSX_FreeAll(extensions, NULL); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } @@ -2535,7 +2579,9 @@ int test_TLSX_KeyShare_negotiate(void) WC_NO_ERR_TRACE(PEER_KEY_ERROR)); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif /* !NO_DH && HAVE_FFDHE_2048 */ #endif return EXPECT_RESULT(); @@ -2770,7 +2816,9 @@ int test_TLSX_KeyShare_gen(void) } } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */ #endif return EXPECT_RESULT(); @@ -2918,7 +2966,9 @@ int test_TLSX_KeyShare_freesizewrite(void) ExpectIntGT(respOff, 0); } wolfSSL_free(ssl); + ssl = NULL; wolfSSL_CTX_free(ctx); + ctx = NULL; #endif return EXPECT_RESULT(); } From b934acdf184a3680e65b01d6b8cf75772f797903 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Fri, 25 Sep 2026 15:32:24 +0300 Subject: [PATCH 2/7] tests: free the key share when its list node cannot be allocated Once MEM_FAIL_CNT is hit, every later allocation fails. TLSX_Push then cannot take the peer entry, so test_TLSX_KeyShare_process leaked it. Free that entry directly. Clear the ServerHello test pointers after the first free so the next wolfSSL_free does not run twice. Signed-off-by: Sameeh Jubran --- tests/api/test_tls13.c | 6 ++++++ tests/api/test_tls_parse.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 9e015bc56cc..a297472b676 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -11293,6 +11293,12 @@ int test_tls13_serverhello_legacy_version(void) wolfSSL_CTX_free(ctx_c); wolfSSL_free(ssl_s); wolfSSL_CTX_free(ctx_s); + /* Expect skips the next wolfSSL_new when this block's allocation failed, + * so the pointers still name the objects just freed. */ + ssl_c = NULL; + ctx_c = NULL; + ssl_s = NULL; + ctx_s = NULL; /* supported_versions on a ServerHello negotiating below TLS 1.3 is * refused whatever version it names (RFC 8446 Section 4.2.1). Hand the diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 60d716831a7..51b73cafa0d 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -198,11 +198,36 @@ TEST_TLS_PARSE_UNUSED static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) { TLSX* extensions = NULL; + void* heap; + if (kse == NULL) return; - if (TLSX_Push(&extensions, TLSX_KEY_SHARE, kse, ssl->heap) != 0) + if (TLSX_Push(&extensions, TLSX_KEY_SHARE, kse, ssl->heap) == 0) { + TLSX_FreeAll(extensions, ssl->heap); return; - TLSX_FreeAll(extensions, ssl->heap); + } + + /* TLSX_Push allocates the list node. After the mem-fail count is hit, + * that allocation fails and every later one fails too, so FreeAll never + * runs. The entry is still ours. */ + heap = ssl->heap; + if (kse->group == WOLFSSL_ECC_X25519) { +#ifdef HAVE_CURVE25519 + wc_curve25519_free((curve25519_key*)kse->key); +#endif + } +#ifdef HAVE_ECC + else { + wc_ecc_free((ecc_key*)kse->key); + } +#endif + XFREE(kse->key, heap, DYNAMIC_TYPE_PRIVATE_KEY); +#if !defined(NO_DH) || defined(WOLFSSL_HAVE_MLKEM) + XFREE(kse->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); +#endif + XFREE(kse->pubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(kse->ke, heap, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(kse, heap, DYNAMIC_TYPE_TLSX); } #endif /* WOLFSSL_TEST_STATIC_BUILD && WOLFSSL_TLS13 && HAVE_SUPPORTED_CURVES */ From 08a25e7efb695e7b0011a1324381a994e04eb33b Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Wed, 30 Sep 2026 14:45:44 +0300 Subject: [PATCH 3/7] tests: free a key share with the function for its group The push-failure path called wc_ecc_free for every group other than X25519, so a live X448 key was freed as an ECC key. FFDHE, X25519, X448, and ECC now each use their own free. Drop the pointer clears in tests that were not failing under MEM_FAIL_CNT. Signed-off-by: Sameeh Jubran --- tests/api/test_tls_parse.c | 68 +++++++++++++------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 51b73cafa0d..88baef61748 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -211,16 +211,34 @@ static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) * that allocation fails and every later one fails too, so FreeAll never * runs. The entry is still ours. */ heap = ssl->heap; - if (kse->group == WOLFSSL_ECC_X25519) { + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(kse->group)) { +#ifndef NO_DH + if (kse->key != NULL) + wc_FreeDhKey((DhKey*)kse->key); + if (kse->privKey != NULL && kse->privKeyLen > 0) + ForceZero(kse->privKey, kse->privKeyLen); +#endif + } + else if (kse->group == WOLFSSL_ECC_X25519) { #ifdef HAVE_CURVE25519 - wc_curve25519_free((curve25519_key*)kse->key); + if (kse->key != NULL) + wc_curve25519_free((curve25519_key*)kse->key); #endif } -#ifdef HAVE_ECC - else { - wc_ecc_free((ecc_key*)kse->key); + else if (kse->group == WOLFSSL_ECC_X448) { +#ifdef HAVE_CURVE448 + if (kse->key != NULL) + wc_curve448_free((curve448_key*)kse->key); +#endif } + /* ECC is the remaining group. A PQC key is not an ecc_key. */ + else if (!WOLFSSL_NAMED_GROUP_IS_PQC(kse->group) && + !WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(kse->group)) { +#ifdef HAVE_ECC + if (kse->key != NULL) + wc_ecc_free((ecc_key*)kse->key); #endif + } XFREE(kse->key, heap, DYNAMIC_TYPE_PRIVATE_KEY); #if !defined(NO_DH) || defined(WOLFSSL_HAVE_MLKEM) XFREE(kse->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); @@ -553,9 +571,7 @@ int test_TLSX_TCA_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* Client side, response direction, trusted_ca_keys never requested: * unsupported extension. */ @@ -572,9 +588,7 @@ int test_TLSX_TCA_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif #endif return EXPECT_RESULT(); @@ -627,9 +641,7 @@ int test_TLSX_certtype_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } @@ -718,9 +730,7 @@ int test_TLSX_EncryptThenMac_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, server_hello, NULL), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } @@ -814,9 +824,7 @@ int test_TLSX_THM_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } @@ -990,9 +998,7 @@ int test_TLSX_SecureRenegotiation_parse(void) (void)wolfSSL_SetAllocators(prevM, prevF, prevR); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* Client side response direction: *input == 2*TLS_FINISHED_SZ, but the * declared extension length disagrees with it -- length is the @@ -1026,9 +1032,7 @@ int test_TLSX_SecureRenegotiation_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, server_hello, NULL), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } @@ -1083,9 +1087,7 @@ int test_TLSX_SupportedVersions_parse(void) &found), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* An over-long list also fails on its own: odd total length (so the * parity check already passes) but still over MAX_SV_EXT_LEN. */ @@ -1109,9 +1111,7 @@ int test_TLSX_SupportedVersions_parse(void) &found), WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* server_hello / hello_retry_request direction, client side. */ ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); @@ -1150,9 +1150,7 @@ int test_TLSX_SupportedVersions_parse(void) WC_NO_ERR_TRACE(SANITY_MSG_E)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* Downgrade bookkeeping: ssl->options.downgrade set and the connection * already sitting at TLS 1.2 minor -- vs. either being false. */ @@ -1187,9 +1185,7 @@ int test_TLSX_SupportedVersions_parse(void) &found), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } @@ -1234,9 +1230,7 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #if !defined(NO_RSA) && defined(HAVE_TLS_EXTENSIONS) && defined(WOLFSSL_TLS13) /* SignatureAlgorithmsCert: same length checks, separate extension. @@ -1271,9 +1265,7 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* TLSX_SignatureAlgorithms_MapPss(): rsa_pss_sa_algo entries whose * second byte is within [pss_sha256, pss_sha512] vs. just above it, @@ -1301,9 +1293,7 @@ int test_TLSX_SignatureAlgorithms_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif #endif #endif @@ -1359,9 +1349,7 @@ int test_TLSX_CSR_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #if defined(WOLFSSL_TLS13) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) /* Client side, TLS 1.3 certificate direction (RFC 8446 4.4.2): the OCSP @@ -1568,9 +1556,7 @@ int test_TLSX_PointFormat_parse(void) ExpectIntEQ(TLSX_Parse(ssl, ext, extLen, client_hello, suites), 0); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && \ @@ -1774,9 +1760,7 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #ifndef NO_WOLFSSL_CLIENT /* Client side response direction: SNI configured (extension and its @@ -1802,9 +1786,7 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(BUFFER_ERROR)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; /* Client side, SNI not requested at all: response is an unsupported * extension. */ @@ -1819,9 +1801,7 @@ int test_TLSX_SNI_parse(void) WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif /* !NO_WOLFSSL_CLIENT */ #endif return EXPECT_RESULT(); @@ -2027,9 +2007,7 @@ int test_TLSX_ValidateSupportedCurves(void) 1); } wolfSSL_free(ssl); - ssl = NULL; wolfSSL_CTX_free(ctx); - ctx = NULL; #endif return EXPECT_RESULT(); } From afc4bf00d2acafb5544d3b683de05e403bbf5d47 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 1 Oct 2026 08:56:02 +0300 Subject: [PATCH 4/7] tests: free async key-share state when the list node allocation fails Call wc_ForceZero so this file builds where ForceZero is not in scope. Release the software-async buffers the same way TLSX_KeyShare_FreeAll does, and fail the list-node allocation for each generated key type. Signed-off-by: Sameeh Jubran --- tests/api/test_tls_parse.c | 116 +++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 4 deletions(-) diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 88baef61748..e93a7a0ad68 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -213,16 +213,32 @@ static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) heap = ssl->heap; if (WOLFSSL_NAMED_GROUP_IS_FFDHE(kse->group)) { #ifndef NO_DH - if (kse->key != NULL) + if (kse->key != NULL) { + #if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \ + defined(WC_ASYNC_ENABLE_DH) + if (((DhKey*)kse->key)->nb != NULL) { + XFREE(((DhKey*)kse->key)->nb, heap, + DYNAMIC_TYPE_TMP_BUFFER); + ((DhKey*)kse->key)->nb = NULL; + } + #endif wc_FreeDhKey((DhKey*)kse->key); + } if (kse->privKey != NULL && kse->privKeyLen > 0) - ForceZero(kse->privKey, kse->privKeyLen); + wc_ForceZero(kse->privKey, kse->privKeyLen); #endif } else if (kse->group == WOLFSSL_ECC_X25519) { #ifdef HAVE_CURVE25519 - if (kse->key != NULL) + if (kse->key != NULL) { + #if defined(WC_X25519_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) + if (((curve25519_key*)kse->key)->nb_ctx != NULL) { + XFREE(((curve25519_key*)kse->key)->nb_ctx, heap, + DYNAMIC_TYPE_TMP_BUFFER); + } + #endif wc_curve25519_free((curve25519_key*)kse->key); + } #endif } else if (kse->group == WOLFSSL_ECC_X448) { @@ -235,8 +251,16 @@ static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) else if (!WOLFSSL_NAMED_GROUP_IS_PQC(kse->group) && !WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(kse->group)) { #ifdef HAVE_ECC - if (kse->key != NULL) + if (kse->key != NULL) { + #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \ + defined(WC_ASYNC_ENABLE_ECC) + if (((ecc_key*)kse->key)->nb_ctx != NULL) { + XFREE(((ecc_key*)kse->key)->nb_ctx, heap, + DYNAMIC_TYPE_TMP_BUFFER); + } + #endif wc_ecc_free((ecc_key*)kse->key); + } #endif } XFREE(kse->key, heap, DYNAMIC_TYPE_PRIVATE_KEY); @@ -247,6 +271,38 @@ static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) XFREE(kse->ke, heap, DYNAMIC_TYPE_PUBLIC_KEY); XFREE(kse, heap, DYNAMIC_TYPE_TLSX); } + +#if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) +/* TLSX_Push's first allocation is the list node. Fail that one so the + * direct free above runs. Later allocations in this call are allowed. */ +TEST_TLS_PARSE_UNUSED +static int test_tls_parse_free_kse_push_fail(WOLFSSL* ssl, KeyShareEntry* kse) +{ + wolfSSL_Malloc_cb prevM = NULL; + wolfSSL_Free_cb prevF = NULL; + wolfSSL_Realloc_cb prevR = NULL; + int ret; + + ret = wolfSSL_GetAllocators(&prevM, &prevF, &prevR); + if (ret != 0) { + test_tls_parse_free_kse(ssl, kse); + return ret; + } + ret = wolfSSL_SetAllocators(tls_parse_fail_malloc, tls_parse_fail_free, + tls_parse_fail_realloc); + if (ret != 0) { + test_tls_parse_free_kse(ssl, kse); + return ret; + } + tls_parse_alloc_seen = 0; + tls_parse_fail_after = 0; + test_tls_parse_free_kse(ssl, kse); + tls_parse_fail_after = -1; + (void)wolfSSL_SetAllocators(prevM, prevF, prevR); + return 0; +} +#endif #endif /* WOLFSSL_TEST_STATIC_BUILD && WOLFSSL_TLS13 && HAVE_SUPPORTED_CURVES */ /* ---- ALPN --------------------------------------------------------------- */ @@ -2705,6 +2761,19 @@ int test_TLSX_KeyShare_gen(void) test_tls_parse_free_kse(ssl, kse); } } +#endif +#if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) + /* Key is generated. The next allocation, the list node, fails. */ + ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), + ssl->heap, DYNAMIC_TYPE_TLSX)); + if (kse != NULL) { + XMEMSET(kse, 0, sizeof(*kse)); + kse->group = WOLFSSL_FFDHE_2048; + ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); + ExpectNotNull(kse->pubKey); + ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + } #endif } wolfSSL_free(ssl); @@ -2749,6 +2818,19 @@ int test_TLSX_KeyShare_gen(void) ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); test_tls_parse_free_kse(ssl, kse); } +#if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) + /* Key is generated. The next allocation, the list node, fails. */ + ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), + ssl->heap, DYNAMIC_TYPE_TLSX)); + if (kse != NULL) { + XMEMSET(kse, 0, sizeof(*kse)); + kse->group = WOLFSSL_ECC_X25519; + ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); + ExpectNotNull(kse->pubKey); + ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + } +#endif } wolfSSL_free(ssl); ssl = NULL; @@ -2787,6 +2869,19 @@ int test_TLSX_KeyShare_gen(void) ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); test_tls_parse_free_kse(ssl, kse); } +#if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) + /* Key is generated. The next allocation, the list node, fails. */ + ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), + ssl->heap, DYNAMIC_TYPE_TLSX)); + if (kse != NULL) { + XMEMSET(kse, 0, sizeof(*kse)); + kse->group = WOLFSSL_ECC_X448; + ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); + ExpectNotNull(kse->pubKey); + ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + } +#endif } wolfSSL_free(ssl); ssl = NULL; @@ -2817,6 +2912,19 @@ int test_TLSX_KeyShare_gen(void) ssl->rng = savedRng; test_tls_parse_free_kse(ssl, kse); } +#if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) + /* Key is generated. The next allocation, the list node, fails. */ + ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), + ssl->heap, DYNAMIC_TYPE_TLSX)); + if (kse != NULL) { + XMEMSET(kse, 0, sizeof(*kse)); + kse->group = WOLFSSL_ECC_SECP256R1; + ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); + ExpectNotNull(kse->pubKey); + ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + } +#endif } wolfSSL_free(ssl); ssl = NULL; From e2b6dd49a354b40db3291b5fdb1a5f43432a1967 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 1 Oct 2026 11:41:54 +0300 Subject: [PATCH 5/7] tests: guard the key-share push-fail helper with USE_WOLFSSL_MEMORY wolfSSL_GetAllocators and wolfSSL_SetAllocators are built only when USE_WOLFSSL_MEMORY is set. The new helper called them without that guard, so the API guard check failed. Signed-off-by: Sameeh Jubran --- tests/api/test_tls_parse.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index e93a7a0ad68..fbdefd7f6f0 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -273,7 +273,7 @@ static void test_tls_parse_free_kse(WOLFSSL* ssl, KeyShareEntry* kse) } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ - !defined(WOLFSSL_DEBUG_MEMORY) + !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) /* TLSX_Push's first allocation is the list node. Fail that one so the * direct free above runs. Later allocations in this call are allowed. */ TEST_TLS_PARSE_UNUSED @@ -2763,7 +2763,7 @@ int test_TLSX_KeyShare_gen(void) } #endif #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ - !defined(WOLFSSL_DEBUG_MEMORY) + !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) /* Key is generated. The next allocation, the list node, fails. */ ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); @@ -2819,7 +2819,7 @@ int test_TLSX_KeyShare_gen(void) test_tls_parse_free_kse(ssl, kse); } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ - !defined(WOLFSSL_DEBUG_MEMORY) + !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) /* Key is generated. The next allocation, the list node, fails. */ ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); @@ -2870,7 +2870,7 @@ int test_TLSX_KeyShare_gen(void) test_tls_parse_free_kse(ssl, kse); } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ - !defined(WOLFSSL_DEBUG_MEMORY) + !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) /* Key is generated. The next allocation, the list node, fails. */ ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); @@ -2913,7 +2913,7 @@ int test_TLSX_KeyShare_gen(void) test_tls_parse_free_kse(ssl, kse); } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ - !defined(WOLFSSL_DEBUG_MEMORY) + !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) /* Key is generated. The next allocation, the list node, fails. */ ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); From 59e3b141c91b25ab1839c03ce825a43b51086441 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 1 Oct 2026 14:22:06 +0300 Subject: [PATCH 6/7] tests: drop the freed key-share pointer before the next case After an Expect fails, the next ExpectNotNull does not assign kse, so the push-fail block still named the entry just freed. Clear kse first. Signed-off-by: Sameeh Jubran --- tests/api/test_tls_parse.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index fbdefd7f6f0..49fe4f37636 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -2764,7 +2764,9 @@ int test_TLSX_KeyShare_gen(void) #endif #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) - /* Key is generated. The next allocation, the list node, fails. */ + /* Key is generated. The next allocation, the list node, fails. + * A failed Expect skips the assignment, so drop the freed pointer. */ + kse = NULL; ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { @@ -2820,7 +2822,9 @@ int test_TLSX_KeyShare_gen(void) } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) - /* Key is generated. The next allocation, the list node, fails. */ + /* Key is generated. The next allocation, the list node, fails. + * A failed Expect skips the assignment, so drop the freed pointer. */ + kse = NULL; ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { @@ -2871,7 +2875,9 @@ int test_TLSX_KeyShare_gen(void) } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) - /* Key is generated. The next allocation, the list node, fails. */ + /* Key is generated. The next allocation, the list node, fails. + * A failed Expect skips the assignment, so drop the freed pointer. */ + kse = NULL; ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { @@ -2914,7 +2920,9 @@ int test_TLSX_KeyShare_gen(void) } #if !defined(NO_TLS) && !defined(WOLFSSL_STATIC_MEMORY) && \ !defined(WOLFSSL_DEBUG_MEMORY) && defined(USE_WOLFSSL_MEMORY) - /* Key is generated. The next allocation, the list node, fails. */ + /* Key is generated. The next allocation, the list node, fails. + * A failed Expect skips the assignment, so drop the freed pointer. */ + kse = NULL; ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { From 41588f0f3e7a1a29703c9279a1e3ca34194958b6 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Wed, 7 Oct 2026 14:25:07 +0300 Subject: [PATCH 7/7] tests: free the key share when an earlier Expect fails ExpectIntEQ does not call its argument after a failure, so the push-fail helper never freed the entry. Call it first, then check the result. Run the P-256 cases only when GenEccKey accepts that group. Signed-off-by: Sameeh Jubran --- tests/api/test_tls_parse.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 49fe4f37636..0a6541c5057 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -2770,11 +2770,16 @@ int test_TLSX_KeyShare_gen(void) ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { + int pushFailRet; + XMEMSET(kse, 0, sizeof(*kse)); kse->group = WOLFSSL_FFDHE_2048; ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); ExpectNotNull(kse->pubKey); - ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + /* Expect skips its arguments after a failure. Free the entry + * even when GenKey or the pubKey check failed. */ + pushFailRet = test_tls_parse_free_kse_push_fail(ssl, kse); + ExpectIntEQ(pushFailRet, 0); } #endif } @@ -2828,11 +2833,16 @@ int test_TLSX_KeyShare_gen(void) ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { + int pushFailRet; + XMEMSET(kse, 0, sizeof(*kse)); kse->group = WOLFSSL_ECC_X25519; ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); ExpectNotNull(kse->pubKey); - ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + /* Expect skips its arguments after a failure. Free the entry + * even when GenKey or the pubKey check failed. */ + pushFailRet = test_tls_parse_free_kse_push_fail(ssl, kse); + ExpectIntEQ(pushFailRet, 0); } #endif } @@ -2881,11 +2891,16 @@ int test_TLSX_KeyShare_gen(void) ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { + int pushFailRet; + XMEMSET(kse, 0, sizeof(*kse)); kse->group = WOLFSSL_ECC_X448; ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); ExpectNotNull(kse->pubKey); - ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + /* Expect skips its arguments after a failure. Free the entry + * even when GenKey or the pubKey check failed. */ + pushFailRet = test_tls_parse_free_kse_push_fail(ssl, kse); + ExpectIntEQ(pushFailRet, 0); } #endif } @@ -2895,11 +2910,13 @@ int test_TLSX_KeyShare_gen(void) ctx = NULL; #endif /* HAVE_CURVE448 */ -#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) +#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + !defined(NO_ECC_SECP) && ECC_MIN_KEY_SZ <= 256 /* TLSX_KeyShare_GenEccKey(): only the "ret == 0" half of "ret == 0 && * pubKey == NULL" is open (the pubKey half already has coverage * elsewhere); force it false the same way as the Curve25519/X448 - * cases above. */ + * cases above. P-256 is the group this switch arm accepts. */ ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); ExpectNotNull(ssl = wolfSSL_new(ctx)); if (ssl != NULL) { @@ -2926,11 +2943,16 @@ int test_TLSX_KeyShare_gen(void) ExpectNotNull(kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), ssl->heap, DYNAMIC_TYPE_TLSX)); if (kse != NULL) { + int pushFailRet; + XMEMSET(kse, 0, sizeof(*kse)); kse->group = WOLFSSL_ECC_SECP256R1; ExpectIntEQ(TLSX_KeyShare_GenKey(ssl, kse), 0); ExpectNotNull(kse->pubKey); - ExpectIntEQ(test_tls_parse_free_kse_push_fail(ssl, kse), 0); + /* Expect skips its arguments after a failure. Free the entry + * even when GenKey or the pubKey check failed. */ + pushFailRet = test_tls_parse_free_kse_push_fail(ssl, kse); + ExpectIntEQ(pushFailRet, 0); } #endif } @@ -2938,7 +2960,9 @@ int test_TLSX_KeyShare_gen(void) ssl = NULL; wolfSSL_CTX_free(ctx); ctx = NULL; -#endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */ +#endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT && + (!NO_ECC256 || HAVE_ALL_CURVES) && !NO_ECC_SECP && + ECC_MIN_KEY_SZ <= 256 */ #endif return EXPECT_RESULT(); }