]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ssls: fix potential memory leak on import
authorStefan Eissing <stefan@eissing.org>
Tue, 14 Jul 2026 07:35:58 +0000 (09:35 +0200)
committerViktor Szakats <commit@vsz.me>
Wed, 15 Jul 2026 20:18:04 +0000 (22:18 +0200)
Memory can be leaked when importing corrupted SSL session data that
carry the same data fields repeatedly.

Fixes #22323

Closes #22324

docs/tests/FILEFORMAT.md
lib/vtls/vtls_scache.c
lib/vtls/vtls_scache.h
lib/vtls/vtls_spack.c
tests/data/Makefile.am
tests/data/test1678 [new file with mode: 0644]
tests/libtest/Makefile.inc
tests/libtest/lib1678.c [new file with mode: 0644]
tests/runtests.pl

index 5ded0d343f944cb2e5fce9b74ae4fec9374e5f8f..447593d523a7f33c13209bdd385974b1568e2cf5 100644 (file)
@@ -534,6 +534,7 @@ Features testable here are:
 - `SPNEGO`
 - `SSL`
 - `SSLpinning`
+- `SSLS-EXPORT`
 - `SSPI`
 - `threaded-resolver`
 - `TLS-SRP`
index 3ba97222858c8d31f215897cab0ef2df9b5da0a8..c7e1cdb46c4dd2bc0495c2039123c52411eb1e42 100644 (file)
@@ -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);
 }
 
index 5fd9d3c054811ad0755e5b5265cdcb8d0605d18e..e6f359395b18a51d27736e5c3fb2bbb24c26d9a3 100644 (file)
@@ -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 */
index e572e923f5424a03e19c22271f3e52bbb61913ae..d8e51c4fd118a4c6dfe4b70e667aebc36326a1c2 100644 (file)
@@ -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:
index 45df54464959a405603a0a3182dd49f2a51f7512..83704d1020725a77ba1299cd71edd99eb248d3cb 100644 (file)
@@ -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 (file)
index 0000000..c0cada0
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+ssls-import
+</keywords>
+</info>
+
+# Client-side
+<client>
+<features>
+SSLS-EXPORT
+</features>
+<tool>
+lib%TESTNUMBER
+</tool>
+<name>
+SSL session import tests
+</name>
+</client>
+</testcase>
index 140ec0bd5819439f8d6f102cbdee82825e09daa0..6fd8469dfa14a98986fbaf5d56f362fa67d55402 100644 (file)
@@ -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 (file)
index 0000000..144cc99
--- /dev/null
@@ -0,0 +1,163 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 */
index b35d6b536897a035230ca9524b7689167fc15153..449500119428ac0802ef2e542bae2e1b6d1bb9e4 100755 (executable)
@@ -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