CVE-2021-22901: Use After Free (CWE-416)
value: curl 7.75.0 through 7.76.1 suffers from a use-after-free vulnerability resulting in already freed memory being used when a TLS 1.3 session ticket arrives over a connection. A malicious server can use this in rare unfortunate circumstances to potentially reach remote code execution in the client. When libcurl at run-time sets up support for TLS 1.3 session tickets on a connection using OpenSSL, it stores pointers to the transfer in-memory object for later retrieval when a session ticket arrives. If the connection is used by multiple transfers (like with a reused HTTP/1.1 connection or multiplexed HTTP/2 connection) that first transfer object might be freed before the new session is established on that connection and then the function will access a memory buffer that might be freed. When using that memory, libcurl might even call a function pointer in the object, making it possible for a remote code execution if the server could somehow manage to get crafted memory content into the correct place in memory.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 8.1
Published: 2021-06-11T16:15Z
Repository: https://github.com/curl/curl
Language: C
Files changed: lib/multi.c, lib/vtls/gskit.c, lib/vtls/gtls.c, lib/vtls/mbedtls.c, lib/vtls/mesalink.c, lib/vtls/nss.c, lib/vtls/openssl.c, lib/vtls/rustls.c, lib/vtls/schannel.c, lib/vtls/sectransp.c, lib/vtls/vtls.c, lib/vtls/vtls.h, lib/vtls/wolfssl.c
Commit date: 2021-05-24 13:15:10 +0200
Commit message: openssl: associate/detach the transfer from connection

CVE-2021-22901

Bug: https://curl.se/docs/CVE-2021-22901.html

Diff: diff --git a/lib/multi.c b/lib/multi.c
index 54365f399e..1b3e261c68 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -876,12 +876,14 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi)
  * occasionally be called with the data->conn pointer already cleared.
  */
 void Curl_detach_connnection(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
-  if(conn)
+  if(conn) {
     Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
+    Curl_ssl_detach_conn(data, conn);
+  }
   data->conn = NULL;
 }
 
 /*
  * Curl_attach_connnection() attaches this transfer to this connection.
@@ -896,10 +898,11 @@ void Curl_attach_connnection(struct Curl_easy *data,
   data->conn = conn;
   Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
                          &data->conn_queue);
   if(conn->handler->attach)
     conn->handler->attach(data, conn);
+  Curl_ssl_associate_conn(data, conn);
 }
 
 static int waitconnect_getsock(struct connectdata *conn,
                                curl_socket_t *sock)
 {
diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c
index c648f62457..ca953769d1 100644
--- a/lib/vtls/gskit.c
+++ b/lib/vtls/gskit.c
@@ -1302,9 +1302,11 @@ const struct Curl_ssl Curl_ssl_gskit = {
   Curl_none_session_free,         /* session_free */
   Curl_none_set_engine,           /* set_engine */
   Curl_none_set_engine_default,   /* set_engine_default */
   Curl_none_engines_list,         /* engines_list */
   Curl_none_false_start,          /* false_start */
-  NULL                            /* sha256sum */
+  NULL,                           /* sha256sum */
+  NULL,                           /* associate_connection */
+  NULL                            /* disassociate_connection */
 };
 
 #endif /* USE_GSKIT */
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index a10c0dbcca..ecde5c44de 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -1654,9 +1654,11 @@ const struct Curl_ssl Curl_ssl_gnutls = {
   gtls_session_free,             /* session_free */
   Curl_none_set_engine,          /* set_engine */
   Curl_none_set_engine_default,  /* set_engine_default */
   Curl_none_engines_list,        /* engines_list */
   Curl_none_false_start,         /* false_start */
-  gtls_sha256sum                 /* sha256sum */
+  gtls_sha256sum,                /* sha256sum */
+  NULL,                          /* associate_connection */
+  NULL                           /* disassociate_connection */
 };
 
 #endif /* USE_GNUTLS */
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index ca77de5866..3a0be0f04b 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -1091,9 +1091,11 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
   mbedtls_session_free,             /* session_free */
   Curl_none_set_engine,             /* set_engine */
   Curl_none_set_engine_default,     /* set_engine_default */
   Curl_none_engines_list,           /* engines_list */
   Curl_none_false_start,            /* false_start */
-  mbedtls_sha256sum                 /* sha256sum */
+  mbedtls_sha256sum,                /* sha256sum */
+  NULL,                             /* associate_connection */
+  NULL                              /* disassociate_connection */
 };
 
 #endif /* USE_MBEDTLS */
diff --git a/lib/vtls/mesalink.c b/lib/vtls/mesalink.c
index f16c77c27f..bf8600d323 100644
--- a/lib/vtls/mesalink.c
+++ b/lib/vtls/mesalink.c
@@ -664,9 +664,11 @@ const struct Curl_ssl Curl_ssl_mesalink = {
   Curl_none_session_free,        /* session_free */
   Curl_none_set_engine,          /* set_engine */
   Curl_none_set_engine_default,  /* set_engine_default */
   Curl_none_engines_list,        /* engines_list */
   Curl_none_false_start,         /* false_start */
-  NULL                           /* sha256sum */
+  NULL,                          /* sha256sum */
+  NULL,                          /* associate_connection */
+  NULL                           /* disassociate_connection */
 };
 
 #endif
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 2aa4bdaa13..1582b1e580 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -2463,9 +2463,11 @@ const struct Curl_ssl Curl_ssl_nss = {
   Curl_none_session_free,       /* session_free */
   Curl_none_set_engine,         /* set_engine */
   Curl_none_set_engine_default, /* set_engine_default */
   Curl_none_engines_list,       /* engines_list */
   nss_false_start,              /* false_start */
-  nss_sha256sum                 /* sha256sum */
+  nss_sha256sum,                /* sha256sum */
+  NULL,                         /* associate_connection */
+  NULL                          /* disassociate_connection */
 };
 
 #endif /* USE_NSS */
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 1521600dd5..ebd7abc3b4 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -238,10 +238,14 @@ struct ssl_backend_data {
   /* Set to true once a valid keylog entry has been created to avoid dupes. */
   bool     keylog_done;
 #endif
 };
 
+static void ossl_associate_connection(struct Curl_easy *data,
+                                      struct connectdata *conn,
+                                      int sockindex);
+
 /*
  * Number of bytes to read from the random number seed file. This must be
  * a finite value (because some entropy "files" like /dev/urandom have
  * an infinite length), but must be large enough to provide enough
  * entropy to properly seed OpenSSL's PRNG.
@@ -2579,10 +2583,11 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
   SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
   X509_LOOKUP *lookup = NULL;
   curl_socket_t sockfd = conn->sock[sockindex];
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   ctx_option_t ctx_options = 0;
+  void *ssl_sessionid = NULL;
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
   bool sni;
   const char * const hostname = SSL_HOST_NAME();
 
@@ -3223,50 +3228,27 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
       infof(data, "WARNING: failed to configure server name indication (SNI) "
             "TLS extension\n");
   }
 #endif
 
-  /* Check if there's a cached ID we can/should use here! */
-  if(SSL_SET_OPTION(primary.sessionid)) {
-    void *ssl_sessionid = NULL;
-    int data_idx = ossl_get_ssl_data_index();
-    int connectdata_idx = ossl_get_ssl_conn_index();
-    int sockindex_idx = ossl_get_ssl_sockindex_index();
-    int proxy_idx = ossl_get_proxy_index();
-
-    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
-       proxy_idx >= 0) {
-      /* Store the data needed for the "new session" callback.
-       * The sockindex is stored as a pointer to an array element. */
-      SSL_set_ex_data(backend->handle, data_idx, data);
-      SSL_set_ex_data(backend->handle, connectdata_idx, conn);
-      SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
-#ifndef CURL_DISABLE_PROXY
-      SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
-                      NULL);
-#else
-      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
-#endif
-
-    }
+  ossl_associate_connection(data, conn, sockindex);
 
-    Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
-                              &ssl_sessionid, NULL, sockindex)) {
-      /* we got a session id, use it! */
-      if(!SSL_set_session(backend->handle, ssl_sessionid)) {
-        Curl_ssl_sessionid_unlock(data);
-        failf(data, "SSL: SSL_set_session failed: %s",
-              ossl_strerror(ERR_get_error(), error_buffer,
-                            sizeof(error_buffer)));
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      /* Informational message */
-      infof(data, "SSL re-using session ID\n");
+  Curl_ssl_sessionid_lock(data);
+  if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
+                            &ssl_sessionid, NULL, sockindex)) {
+    /* we got a session id, use it! */
+    if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+      Curl_ssl_sessionid_unlock(data);
+      failf(data, "SSL: SSL_set_session failed: %s",
+            ossl_strerror(ERR_get_error(), error_buffer,
+                          sizeof(error_buffer)));
+      return CURLE_SSL_CONNECT_ERROR;
     }
-    Curl_ssl_sessionid_unlock(data);
+    /* Informational message */
+    infof(data, "SSL re-using session ID\n");
   }
+  Curl_ssl_sessionid_unlock(data);
 
 #ifndef CURL_DISABLE_PROXY
   if(conn->proxy_ssl[sockindex].use) {
     BIO *const bio = BIO_new(BIO_f_ssl());
     SSL *handle = conn->proxy_ssl[sockindex].backend->handle;
@@ -4496,10 +4478,94 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
   struct ssl_backend_data *backend = connssl->backend;
   return info == CURLINFO_TLS_SESSION ?
          (void *)backend->ctx : (void *)backend->handle;
 }
 
+static void ossl_associate_connection(struct Curl_easy *data,
+                                      struct connectdata *conn,
+                                      int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
+
+  /* If we don't have SSL context, do nothing. */
+  if(!backend->handle)
+    return;
+
+  if(SSL_SET_OPTION(primary.sessionid)) {
+    int data_idx = ossl_get_ssl_data_index();
+    int connectdata_idx = ossl_get_ssl_conn_index();
+    int sockindex_idx = ossl_get_ssl_sockindex_index();
+    int proxy_idx = ossl_get_proxy_index();
+
+    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
+       proxy_idx >= 0) {
+      /* Store the data needed for the "new session" callback.
+       * The sockindex is stored as a pointer to an array element. */
+      SSL_set_ex_data(backend->handle, data_idx, data);
+      SSL_set_ex_data(backend->handle, connectdata_idx, conn);
+      SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
+#ifndef CURL_DISABLE_PROXY
+      SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
+                      NULL);
+#else
+      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
+#endif
+    }
+  }
+}
+
+/*
+ * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
+ * the handshake. If the transfer that sets up the callback gets killed before
+ * this callback arrives, we must make sure to properly clear the data to
+ * avoid UAF problems. A future optimization could be to instead store another
+ * transfer that might still be using the same connection.
+ */
+
+static void ossl_disassociate_connection(struct Curl_easy *data,
+                                         int sockindex)
+{
+  struct connectdata *conn = data->conn;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
+
+  /* If we don't have SSL context, do nothing. */
+  if(!backend->handle)
+    return;
+
+  if(SSL_SET_OPTION(primary.sessionid)) {
+    bool isproxy = FALSE;
+    bool incache;
+    void *old_ssl_sessionid = NULL;
+    int data_idx = ossl_get_ssl_data_index();
+    int connectdata_idx = ossl_get_ssl_conn_index();
+    int sockindex_idx = ossl_get_ssl_sockindex_index();
+    int proxy_idx = ossl_get_proxy_index();
+
+    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
+       proxy_idx >= 0) {
+      /* Invalidate the session cache entry, if any */
+      isproxy = SSL_get_ex_data(backend->handle, proxy_idx) ? TRUE : FALSE;
+
+      /* Disable references to data in "new session" callback to avoid
+       * accessing a stale pointer. */
+      SSL_set_ex_data(backend->handle, data_idx, NULL);
+      SSL_set_ex_data(backend->handle, connectdata_idx, NULL);
+      SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
+      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
+    }
+
+    Curl_ssl_sessionid_lock(data);
+    incache = !(Curl_ssl_getsessionid(data, conn, isproxy,
+                                      &old_ssl_sessionid, NULL, sockindex));
+    if(incache)
+      Curl_ssl_delsessionid(data, old_ssl_sessionid);
+    Curl_ssl_sessionid_unlock(data);
+  }
+}
+
 const struct Curl_ssl Curl_ssl_openssl = {
   { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
 
   SSLSUPP_CA_PATH |
   SSLSUPP_CAINFO_BLOB |
@@ -4531,12 +4597,14 @@ const struct Curl_ssl Curl_ssl_openssl = {
   ossl_set_engine,          /* set_engine */
   ossl_set_engine_default,  /* set_engine_default */
   ossl_engines_list,        /* engines_list */
   Curl_none_false_start,    /* false_start */
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-  ossl_sha256sum            /* sha256sum */
+  ossl_sha256sum,           /* sha256sum */
 #else
-  NULL                      /* sha256sum */
+  NULL,                     /* sha256sum */
 #endif
+  ossl_associate_connection, /* associate_connection */
+  ossl_disassociate_connection /* disassociate_connection */
 };
 
 #endif /* USE_OPENSSL */
diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
index 9dfbd2c3c4..161f3bf51d 100644
--- a/lib/vtls/rustls.c
+++ b/lib/vtls/rustls.c
@@ -602,9 +602,11 @@ const struct Curl_ssl Curl_ssl_rustls = {
   Curl_none_session_free,          /* session_free */
   Curl_none_set_engine,            /* set_engine */
   Curl_none_set_engine_default,    /* set_engine_default */
   Curl_none_engines_list,          /* engines_list */
   Curl_none_false_start,           /* false_start */
-  NULL                             /* sha256sum */
+  NULL,                            /* sha256sum */
+  NULL,                            /* associate_connection */
+  NULL                             /* disassociate_connection */
 };
 
 #endif /* USE_RUSTLS */
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index dba7072273..2bcf11db25 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -327,11 +327,11 @@ get_alg_id_by_name(char *name)
   return 0;
 }
 
 static CURLcode
 set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
-                int *algIds)
+                ALG_ID *algIds)
 {
   char *startCur = ciphers;
   int algCount = 0;
   while(startCur && (0 != *startCur) && (algCount < NUMOF_CIPHERS)) {
     long alg = strtol(startCur, 0, 0);
@@ -2431,9 +2431,11 @@ const struct Curl_ssl Curl_ssl_schannel = {
   schannel_session_free,             /* session_free */
   Curl_none_set_engine,              /* set_engine */
   Curl_none_set_engine_default,      /* set_engine_default */
   Curl_none_engines_list,            /* engines_list */
   Curl_none_false_start,             /* false_start */
-  schannel_sha256sum                 /* sha256sum */
+  schannel_sha256sum,                /* sha256sum */
+  NULL,                              /* associate_connection */
+  NULL                               /* disassociate_connection */
 };
 
 #endif /* USE_SCHANNEL */
diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c
index 4276b89cfb..8b1e84ed77 100644
--- a/lib/vtls/sectransp.c
+++ b/lib/vtls/sectransp.c
@@ -3451,10 +3451,12 @@ const struct Curl_ssl Curl_ssl_sectransp = {
   Curl_none_set_engine,               /* set_engine */
   Curl_none_set_engine_default,       /* set_engine_default */
   Curl_none_engines_list,             /* engines_list */
   sectransp_false_start,              /* false_start */
   sectransp_sha256sum                 /* sha256sum */
+  NULL,                               /* associate_connection */
+  NULL                                /* disassociate_connection */
 };
 
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index d63fd5c763..65f4f773dd 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -584,10 +584,29 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
   }
 
   return CURLE_OK;
 }
 
+void Curl_ssl_associate_conn(struct Curl_easy *data,
+                             struct connectdata *conn)
+{
+  if(Curl_ssl->associate_connection) {
+    Curl_ssl->associate_connection(data, conn, FIRSTSOCKET);
+    if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
+      Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET);
+  }
+}
+
+void Curl_ssl_detach_conn(struct Curl_easy *data,
+                          struct connectdata *conn)
+{
+  if(Curl_ssl->disassociate_connection) {
+    Curl_ssl->disassociate_connection(data, FIRSTSOCKET);
+    if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
+      Curl_ssl->disassociate_connection(data, SECONDARYSOCKET);
+  }
+}
 
 void Curl_ssl_close_all(struct Curl_easy *data)
 {
   /* kill the session ID cache if not shared */
   if(data->state.session && !SSLSESSION_SHARED(data)) {
@@ -1212,11 +1231,13 @@ static const struct Curl_ssl Curl_ssl_multi = {
   Curl_none_session_free,            /* session_free */
   Curl_none_set_engine,              /* set_engine */
   Curl_none_set_engine_default,      /* set_engine_default */
   Curl_none_engines_list,            /* engines_list */
   Curl_none_false_start,             /* false_start */
-  NULL                               /* sha256sum */
+  NULL,                              /* sha256sum */
+  NULL,                              /* associate_connection */
+  NULL                               /* disassociate_connection */
 };
 
 const struct Curl_ssl *Curl_ssl =
 #if defined(CURL_WITH_MULTI_SSL)
   &Curl_ssl_multi;
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index a22d526ca8..7f93e7aedb 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -82,10 +82,15 @@ struct Curl_ssl {
   struct curl_slist *(*engines_list)(struct Curl_easy *data);
 
   bool (*false_start)(void);
   CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
                     unsigned char *sha256sum, size_t sha256sumlen);
+
+  void (*associate_connection)(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               int sockindex);
+  void (*disassociate_connection)(struct Curl_easy *data, int sockindex);
 };
 
 #ifdef USE_SSL
 extern const struct Curl_ssl *Curl_ssl;
 #endif
@@ -281,10 +286,15 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
 
 bool Curl_ssl_cert_status_request(void);
 
 bool Curl_ssl_false_start(void);
 
+void Curl_ssl_associate_conn(struct Curl_easy *data,
+                             struct connectdata *conn);
+void Curl_ssl_detach_conn(struct Curl_easy *data,
+                          struct connectdata *conn);
+
 #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
 
 #else /* if not USE_SSL */
 
 /* When SSL support is not present, just define away these function calls */
@@ -307,8 +317,10 @@ bool Curl_ssl_false_start(void);
 #define Curl_ssl_kill_session(x) Curl_nop_stmt
 #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
 #define Curl_ssl_cert_status_request() FALSE
 #define Curl_ssl_false_start() FALSE
 #define Curl_ssl_tls13_ciphersuites() FALSE
+#define Curl_ssl_associate_conn(a,b) Curl_nop_stmt
+#define Curl_ssl_detach_conn(a,b) Curl_nop_stmt
 #endif
 
 #endif /* HEADER_CURL_VTLS_H */
diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
index 02fcd23669..60e27e3662 100644
--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -1123,9 +1123,11 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
   wolfssl_session_free,            /* session_free */
   Curl_none_set_engine,            /* set_engine */
   Curl_none_set_engine_default,    /* set_engine_default */
   Curl_none_engines_list,          /* engines_list */
   Curl_none_false_start,           /* false_start */
-  wolfssl_sha256sum                /* sha256sum */
+  wolfssl_sha256sum,               /* sha256sum */
+  NULL,                            /* associate_connection */
+  NULL                             /* disassociate_connection */
 };
 
 #endif

Vulnerable code:   if(conn)
  NULL                            /* sha256sum */
  gtls_sha256sum                 /* sha256sum */
  mbedtls_sha256sum                 /* sha256sum */
  NULL                           /* sha256sum */
  nss_sha256sum                 /* sha256sum */
  /* Check if there's a cached ID we can/should use here! */
  if(SSL_SET_OPTION(primary.sessionid)) {
    void *ssl_sessionid = NULL;
    int data_idx = ossl_get_ssl_data_index();
    int connectdata_idx = ossl_get_ssl_conn_index();
    int sockindex_idx = ossl_get_ssl_sockindex_index();
    int proxy_idx = ossl_get_proxy_index();

    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
       proxy_idx >= 0) {
      /* Store the data needed for the "new session" callback.
       * The sockindex is stored as a pointer to an array element. */
      SSL_set_ex_data(backend->handle, data_idx, data);
      SSL_set_ex_data(backend->handle, connectdata_idx, conn);
      SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
      SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
                      NULL);
#else
      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif

    }
    Curl_ssl_sessionid_lock(data);
    if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
                              &ssl_sessionid, NULL, sockindex)) {
      /* we got a session id, use it! */
      if(!SSL_set_session(backend->handle, ssl_sessionid)) {
        Curl_ssl_sessionid_unlock(data);
        failf(data, "SSL: SSL_set_session failed: %s",
              ossl_strerror(ERR_get_error(), error_buffer,
                            sizeof(error_buffer)));
        return CURLE_SSL_CONNECT_ERROR;
      }
      /* Informational message */
      infof(data, "SSL re-using session ID\n");
    Curl_ssl_sessionid_unlock(data);
  ossl_sha256sum            /* sha256sum */
  NULL                      /* sha256sum */
  NULL                             /* sha256sum */
                int *algIds)
  schannel_sha256sum                 /* sha256sum */
  NULL                               /* sha256sum */
  wolfssl_sha256sum                /* sha256sum */
Fixed code:   if(conn) {
    Curl_ssl_detach_conn(data, conn);
  }
  Curl_ssl_associate_conn(data, conn);
  NULL,                           /* sha256sum */
  NULL,                           /* associate_connection */
  NULL                            /* disassociate_connection */
  gtls_sha256sum,                /* sha256sum */
  NULL,                          /* associate_connection */
  NULL                           /* disassociate_connection */
  mbedtls_sha256sum,                /* sha256sum */
  NULL,                             /* associate_connection */
  NULL                              /* disassociate_connection */
  NULL,                          /* sha256sum */
  NULL,                          /* associate_connection */
  NULL                           /* disassociate_connection */
  nss_sha256sum,                /* sha256sum */
  NULL,                         /* associate_connection */
  NULL                          /* disassociate_connection */
static void ossl_associate_connection(struct Curl_easy *data,
                                      struct connectdata *conn,
                                      int sockindex);

  void *ssl_sessionid = NULL;
  ossl_associate_connection(data, conn, sockindex);
  Curl_ssl_sessionid_lock(data);
  if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
                            &ssl_sessionid, NULL, sockindex)) {
    /* we got a session id, use it! */
    if(!SSL_set_session(backend->handle, ssl_sessionid)) {
      Curl_ssl_sessionid_unlock(data);
      failf(data, "SSL: SSL_set_session failed: %s",
            ossl_strerror(ERR_get_error(), error_buffer,
                          sizeof(error_buffer)));
      return CURLE_SSL_CONNECT_ERROR;
    /* Informational message */
    infof(data, "SSL re-using session ID\n");
  Curl_ssl_sessionid_unlock(data);
static void ossl_associate_connection(struct Curl_easy *data,
                                      struct connectdata *conn,
                                      int sockindex)
{
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct ssl_backend_data *backend = connssl->backend;

  /* If we don't have SSL context, do nothing. */
  if(!backend->handle)
    return;

  if(SSL_SET_OPTION(primary.sessionid)) {
    int data_idx = ossl_get_ssl_data_index();
    int connectdata_idx = ossl_get_ssl_conn_index();
    int sockindex_idx = ossl_get_ssl_sockindex_index();
    int proxy_idx = ossl_get_proxy_index();

    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
       proxy_idx >= 0) {
      /* Store the data needed for the "new session" callback.
       * The sockindex is stored as a pointer to an array element. */
      SSL_set_ex_data(backend->handle, data_idx, data);
      SSL_set_ex_data(backend->handle, connectdata_idx, conn);
      SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
      SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
                      NULL);
#else
      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif
    }
  }
}

/*
 * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
 * the handshake. If the transfer that sets up the callback gets killed before
 * this callback arrives, we must make sure to properly clear the data to
 * avoid UAF problems. A future optimization could be to instead store another
 * transfer that might still be using the same connection.
 */

static void ossl_disassociate_connection(struct Curl_easy *data,
                                         int sockindex)
{
  struct connectdata *conn = data->conn;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct ssl_backend_data *backend = connssl->backend;

  /* If we don't have SSL context, do nothing. */
  if(!backend->handle)
    return;

  if(SSL_SET_OPTION(primary.sessionid)) {
    bool isproxy = FALSE;
    bool incache;
    void *old_ssl_sessionid = NULL;
    int data_idx = ossl_get_ssl_data_index();
    int connectdata_idx = ossl_get_ssl_conn_index();
    int sockindex_idx = ossl_get_ssl_sockindex_index();
    int proxy_idx = ossl_get_proxy_index();

    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
       proxy_idx >= 0) {
      /* Invalidate the session cache entry, if any */
      isproxy = SSL_get_ex_data(backend->handle, proxy_idx) ? TRUE : FALSE;

      /* Disable references to data in "new session" callback to avoid
       * accessing a stale pointer. */
      SSL_set_ex_data(backend->handle, data_idx, NULL);
      SSL_set_ex_data(backend->handle, connectdata_idx, NULL);
      SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
    }

    Curl_ssl_sessionid_lock(data);
    incache = !(Curl_ssl_getsessionid(data, conn, isproxy,
                                      &old_ssl_sessionid, NULL, sockindex));
    if(incache)
      Curl_ssl_delsessionid(data, old_ssl_sessionid);
    Curl_ssl_sessionid_unlock(data);
  }
}

  ossl_sha256sum,           /* sha256sum */
  NULL,                     /* sha256sum */
  ossl_associate_connection, /* associate_connection */
  ossl_disassociate_connection /* disassociate_connection */
  NULL,                            /* sha256sum */
  NULL,                            /* associate_connection */
  NULL                             /* disassociate_connection */
                ALG_ID *algIds)
  schannel_sha256sum,                /* sha256sum */
  NULL,                              /* associate_connection */
  NULL                               /* disassociate_connection */
  NULL,                               /* associate_connection */
  NULL                                /* disassociate_connection */
void Curl_ssl_associate_conn(struct Curl_easy *data,
                             struct connectdata *conn)
{
  if(Curl_ssl->associate_connection) {
    Curl_ssl->associate_connection(data, conn, FIRSTSOCKET);
    if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
      Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET);
  }
}

void Curl_ssl_detach_conn(struct Curl_easy *data,
                          struct connectdata *conn)
{
  if(Curl_ssl->disassociate_connection) {
    Curl_ssl->disassociate_connection(data, FIRSTSOCKET);
    if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
      Curl_ssl->disassociate_connection(data, SECONDARYSOCKET);
  }
}
  NULL,                              /* sha256sum */
  NULL,                              /* associate_connection */
  NULL                               /* disassociate_connection */

  void (*associate_connection)(struct Curl_easy *data,
                               struct connectdata *conn,
                               int sockindex);
  void (*disassociate_connection)(struct Curl_easy *data, int sockindex);
void Curl_ssl_associate_conn(struct Curl_easy *data,
                             struct connectdata *conn);
void Curl_ssl_detach_conn(struct Curl_easy *data,
                          struct connectdata *conn);

#define Curl_ssl_associate_conn(a,b) Curl_nop_stmt
#define Curl_ssl_detach_conn(a,b) Curl_nop_stmt
  wolfssl_sha256sum,               /* sha256sum */
  NULL,                            /* associate_connection */
  NULL                             /* disassociate_connection */