From: Stefan Eissing Date: Tue, 14 Jul 2026 07:35:58 +0000 (+0200) Subject: ssls: fix potential memory leak on import X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0dc22b690dd8dba4048d494f09a50122dd7c0dd4;p=thirdparty%2Fcurl.git ssls: fix potential memory leak on import Memory can be leaked when importing corrupted SSL session data that carry the same data fields repeatedly. Fixes #22323 Closes #22324 --- diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index 5ded0d343f..447593d523 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -534,6 +534,7 @@ Features testable here are: - `SPNEGO` - `SSL` - `SSLpinning` +- `SSLS-EXPORT` - `SSPI` - `threaded-resolver` - `TLS-SRP` diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 3ba9722285..c7e1cdb46c 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -380,9 +380,9 @@ static void cf_ssl_scache_session_ldestroy(void *udata, void *obj) { struct Curl_ssl_session *s = obj; (void)udata; - curlx_free(CURL_UNCONST(s->sdata)); - curlx_free(CURL_UNCONST(s->quic_tp)); - curlx_free((void *)s->alpn); + curlx_free(s->sdata); + curlx_free(s->quic_tp); + curlx_free(s->alpn); curlx_free(s); } diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index 5fd9d3c054..e6f359395b 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -122,13 +122,13 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, /* All about an SSL session ticket */ struct Curl_ssl_session { - const void *sdata; /* session ticket data, plain bytes */ + uint8_t *sdata; /* session ticket data, plain bytes */ size_t sdata_len; /* number of bytes in sdata */ curl_off_t valid_until; /* seconds since EPOCH until ticket expires */ int ietf_tls_id; /* TLS protocol identifier negotiated */ char *alpn; /* APLN TLS negotiated protocol string */ size_t earlydata_max; /* max 0-RTT data supported by peer */ - const unsigned char *quic_tp; /* Optional QUIC transport param bytes */ + uint8_t *quic_tp; /* Optional QUIC transport param bytes */ size_t quic_tp_len; /* number of bytes in quic_tp */ struct Curl_llist_node list; /* internal storage handling */ BIT(sectrust_verified); /* session comes from sectrust verified TLS */ diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index e572e923f5..d8e51c4fd1 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -244,6 +244,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, uint16_t val16; uint32_t val32; uint64_t val64; + size_t dlen; CURLcode result; DEBUGASSERT(buf); @@ -271,6 +272,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, switch(val8) { case CURL_SPACK_ALPN: + curlx_free(s->alpn); result = spack_decstr16(&s->alpn, &buf, end); if(result) goto out; @@ -288,17 +290,21 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, s->ietf_tls_id = val16; break; case CURL_SPACK_QUICTP: { - result = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + result = spack_decdata16(&pval8, &dlen, &buf, end); if(result) goto out; + curlx_free(s->quic_tp); s->quic_tp = pval8; + s->quic_tp_len = dlen; break; } case CURL_SPACK_TICKET: { - result = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + result = spack_decdata16(&pval8, &dlen, &buf, end); if(result) goto out; + curlx_free(s->sdata); s->sdata = pval8; + s->sdata_len = dlen; break; } case CURL_SPACK_VALID_UNTIL: diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 45df544649..83704d1020 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -220,7 +220,7 @@ test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 \ test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ -test1677 test1680 test1681 test1682 test1683 test1684 \ +test1677 test1678 test1680 test1681 test1682 test1683 test1684 \ test1685 test1686 \ \ test1700 test1702 test1703 test1704 test1705 test1706 test1707 \ diff --git a/tests/data/test1678 b/tests/data/test1678 new file mode 100644 index 0000000000..c0cada0ba7 --- /dev/null +++ b/tests/data/test1678 @@ -0,0 +1,21 @@ + + + + +ssls-import + + + +# Client-side + + +SSLS-EXPORT + + +lib%TESTNUMBER + + +SSL session import tests + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 140ec0bd58..6fd8469dfa 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -103,7 +103,7 @@ TESTS_C = \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ lib1647.c lib1648.c lib1649.c \ - lib1662.c \ + lib1662.c lib1678.c \ lib1686.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ diff --git a/tests/libtest/lib1678.c b/tests/libtest/lib1678.c new file mode 100644 index 0000000000..144cc9977a --- /dev/null +++ b/tests/libtest/lib1678.c @@ -0,0 +1,163 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +#if defined(USE_SSL) && defined(USE_SSLS_EXPORT) + +#define T1678_IMPORT_COUNT 64 +#define T1678_FIRST_TICKET_SIZE 4096 +#define T1678_FIRST_QUICTP_SIZE 127 +#define T1678_FIRST_ALPN_SIZE 10 + +static uint8_t *t1678_make_packet(size_t *packet_len) +{ + const size_t t1678_total = + 1 + /* format version */ + 1 + 2 + T1678_FIRST_TICKET_SIZE + /* first ticket */ + 1 + 2 + T1678_FIRST_QUICTP_SIZE + /* first QUIC traffic params */ + 1 + 2 + T1678_FIRST_ALPN_SIZE + /* first ALPN params */ + 1 + 2 + 1 + /* second ticket */ + 1 + 2 + 1 + /* second ALPN */ + 1 + 2 + 1; /* second QUIC traffic params */ + + uint8_t *packet = curlx_malloc(t1678_total); + uint8_t *p; + + if(!packet) + return NULL; + + p = packet; + + /* CURL_SPACK_VERSION */ + *p++ = 0x01; + + /* First CURL_SPACK_TICKET */ + *p++ = 0x04; + *p++ = (uint8_t)(T1678_FIRST_TICKET_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_TICKET_SIZE & 0x0ff); + memset(p, 'A', T1678_FIRST_TICKET_SIZE); + p += T1678_FIRST_TICKET_SIZE; + + /* First CURL_SPACK_QUICTP */ + *p++ = 0x07; + *p++ = (uint8_t)(T1678_FIRST_QUICTP_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_QUICTP_SIZE & 0x0ff); + memset(p, 'Q', T1678_FIRST_QUICTP_SIZE); + p += T1678_FIRST_QUICTP_SIZE; + + /* First CURL_SPACK_ALPN */ + *p++ = 0x05; + *p++ = (uint8_t)(T1678_FIRST_ALPN_SIZE >> 8); + *p++ = (uint8_t)(T1678_FIRST_ALPN_SIZE & 0x0ff); + memset(p, 'a', T1678_FIRST_ALPN_SIZE); + p += T1678_FIRST_ALPN_SIZE; + + /* Second CURL_SPACK_TICKET: one byte. */ + *p++ = 0x04; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'B'; + + /* Second CURL_SPACK_ALPN: one byte. */ + *p++ = 0x05; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'b'; + + /* Second CURL_SPACK_QUICTP: one byte. */ + *p++ = 0x07; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 'R'; + + *packet_len = t1678_total; + return packet; +} + +static CURLcode test_lib1678(const char *URL) +{ + uint8_t *packet; + size_t packet_len; + CURLSH *share = NULL; + CURL *easy = NULL; + CURLSHcode shrc; + CURLcode result = CURLE_FAILED_INIT; + int i; + + (void)URL; + packet = t1678_make_packet(&packet_len); + if(!packet) + goto test_cleanup; + + result = curl_global_init(CURL_GLOBAL_DEFAULT); + if(result != CURLE_OK) + goto test_cleanup; + + share = curl_share_init(); + easy = curl_easy_init(); + + if(!share || !easy) + goto test_cleanup; + + shrc = curl_share_setopt(share, + CURLSHOPT_SHARE, + CURL_LOCK_DATA_SSL_SESSION); + if(shrc != CURLSHE_OK) + goto test_cleanup; + + result = curl_easy_setopt(easy, CURLOPT_SHARE, share); + if(result) + goto test_cleanup; + + for(i = 0; i < T1678_IMPORT_COUNT; ++i) { + result = curl_easy_ssls_import(easy, + "example.test:443", + NULL, + 0, + packet, + packet_len); + if(result) { + curl_mfprintf(stderr, + "import %d failed: %d (%s)\n", + i, (int)result, + curl_easy_strerror(result)); + break; + } + } + +test_cleanup: + curlx_free(packet); + curl_easy_cleanup(easy); + curl_share_cleanup(share); + curl_global_cleanup(); + + return result; +} +#else +static CURLcode test_lib1678(const char *URL) +{ + (void)URL; + return CURLE_OK; +} +#endif /* USE_SSL && USE_SSLS_EXPORT */ diff --git a/tests/runtests.pl b/tests/runtests.pl index b35d6b5368..4495001194 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -733,6 +733,7 @@ sub checksystemfeatures { # 'https-proxy' is used as "server" so consider it a protocol push @protocols, 'https-proxy'; } + $feature{"SSLS-EXPORT"} = $feat =~ /SSLS-EXPORT/; # Unicode support $feature{"Unicode"} = $feat =~ /Unicode/i; # Thread-safe init