]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
dynbuf: assert init on free
authorStefan Eissing <stefan@eissing.org>
Thu, 20 Mar 2025 09:31:30 +0000 (10:31 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 24 Mar 2025 08:53:40 +0000 (09:53 +0100)
Add a DEBUGASSERT() in Curl_dyn_free() that checks that Curl_dyn_init()
has been performed before.

Fix code places that did it wrong.

Fixes #16725
Closes #16775

17 files changed:
lib/curl_krb5.h
lib/dynbuf.c
lib/easy.c
lib/ftp.c
lib/http_negotiate.c
lib/imap.c
lib/imap.h
lib/krb5.c
lib/pingpong.c
lib/pingpong.h
lib/rtsp.c
lib/rtsp.h
lib/url.c
lib/vauth/spnego_gssapi.c
lib/vssh/libssh.c
lib/vssh/libssh2.c
lib/vssh/ssh.h

index ccf6b96a875edd87cee71dfba0bb0a70313a109c..b9caf7f24356f45154c84957a75437d75b8251f8 100644 (file)
@@ -40,13 +40,15 @@ struct Curl_sec_client_mech {
 #define AUTH_ERROR      2
 
 #ifdef HAVE_GSSAPI
+void Curl_sec_conn_init(struct connectdata *);
+void Curl_sec_conn_destroy(struct connectdata *);
 int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *,
                       enum protection_level);
-void Curl_sec_end(struct connectdata *);
 CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *);
 int Curl_sec_request_prot(struct connectdata *conn, const char *level);
 #else
-#define Curl_sec_end(x)
+#define Curl_sec_conn_init(x)     Curl_nop_stmt
+#define Curl_sec_conn_destroy(x)  Curl_nop_stmt
 #endif
 
 #endif /* HEADER_CURL_KRB5_H */
index 353346dcd773a6ae370ac35ac358e4158db424cc..bdfe6a681d415c84073e957ad0cc294464d62300 100644 (file)
@@ -60,6 +60,7 @@ void Curl_dyn_init(struct dynbuf *s, size_t toobig)
 void Curl_dyn_free(struct dynbuf *s)
 {
   DEBUGASSERT(s);
+  DEBUGASSERT(s->init == DYNINIT);
   Curl_safefree(s->bufr);
   s->leng = s->allc = 0;
 }
index 964de98d9684d0bb2b61b5ffe4e8cfd784593100..1b03fddce7ce6dcf7dff6b60e2d3c527b0227b33 100644 (file)
@@ -950,10 +950,6 @@ CURL *curl_easy_duphandle(CURL *d)
    */
   outcurl->set.buffer_size = data->set.buffer_size;
 
-  /* copy all userdefined values */
-  if(dupset(outcurl, data))
-    goto fail;
-
   Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
   Curl_netrc_init(&outcurl->state.netrc);
 
@@ -961,6 +957,16 @@ CURL *curl_easy_duphandle(CURL *d)
   outcurl->state.lastconnect_id = -1;
   outcurl->state.recent_conn_id = -1;
   outcurl->id = -1;
+  outcurl->mid = -1;
+
+#ifndef CURL_DISABLE_HTTP
+  Curl_llist_init(&outcurl->state.httphdrs, NULL);
+#endif
+  Curl_initinfo(outcurl);
+
+  /* copy all userdefined values */
+  if(dupset(outcurl, data))
+    goto fail;
 
   outcurl->progress.flags    = data->progress.flags;
   outcurl->progress.callback = data->progress.callback;
@@ -1054,10 +1060,6 @@ CURL *curl_easy_duphandle(CURL *d)
       goto fail;
   }
 #endif /* USE_ARES */
-#ifndef CURL_DISABLE_HTTP
-  Curl_llist_init(&outcurl->state.httphdrs, NULL);
-#endif
-  Curl_initinfo(outcurl);
 
   outcurl->magic = CURLEASY_MAGIC_NUMBER;
 
index cdb9b0e4e7d3d3e1d558e3833a727fdace11296d..a60af878edc3ad7a2c00e639274119269f77957e 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -4108,7 +4108,6 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
   Curl_safefree(ftpc->prevpath);
   Curl_safefree(ftpc->server_os);
   Curl_pp_disconnect(pp);
-  Curl_sec_end(conn);
   return CURLE_OK;
 }
 
index b1bac5f72457faa64fdd209c765e641e830bc7b8..0b6e70c660642f0fde8258495635b2645f4c1b03 100644 (file)
@@ -109,9 +109,10 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   neg_ctx->sslContext = conn->sslContext;
 #endif
   /* Check if the connection is using SSL and get the channel binding data */
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
+  Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
+#ifdef USE_SSL
   if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
-    Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
     result = Curl_ssl_get_channel_binding(
       data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
     if(result) {
@@ -119,13 +120,14 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
       return result;
     }
   }
-#endif
+#endif /* USE_SSL */
+#endif /* HAVE_GSSAPI */
 
   /* Initialize the security context and decode our challenge */
   result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
                                            host, header, neg_ctx);
 
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
   Curl_dyn_free(&neg_ctx->channel_binding_data);
 #endif
 
index 6091b76cc057226f9a8e8d661fcf69179d7e51e4..5e35f91fd5af675719cc745839409b3442cd8cf2 100644 (file)
@@ -1495,14 +1495,17 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done)
   /* We always support persistent connections in IMAP */
   connkeep(conn, "IMAP default");
 
-  PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
+  if(!imapc->initialised) {
+    PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
 
-  /* Set the default preferred authentication type and mechanism */
-  imapc->preftype = IMAP_TYPE_ANY;
-  Curl_sasl_init(&imapc->sasl, data, &saslimap);
+    /* Set the default preferred authentication type and mechanism */
+    imapc->preftype = IMAP_TYPE_ANY;
+    Curl_sasl_init(&imapc->sasl, data, &saslimap);
 
-  Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
-  Curl_pp_init(pp);
+    Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
+    Curl_pp_init(pp);
+    imapc->initialised = TRUE;
+  }
 
   /* Parse the URL options */
   result = imap_parse_url_options(conn);
@@ -1692,27 +1695,30 @@ static CURLcode imap_disconnect(struct Curl_easy *data,
   struct imap_conn *imapc = &conn->proto.imapc;
   (void)data;
 
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to. */
+  if(imapc->initialised) {
+    /* We cannot send quit unconditionally. If this connection is stale or
+       bad in any way, sending quit and waiting around here will make the
+       disconnect wait in vain and cause more problems than we need to. */
 
-  /* The IMAP session may or may not have been allocated/setup at this
-     point! */
-  if(!dead_connection && conn->bits.protoconnstart) {
-    if(!imap_perform_logout(data))
-      (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
-  }
+    /* The IMAP session may or may not have been allocated/setup at this
+       point! */
+    if(!dead_connection && conn->bits.protoconnstart) {
+      if(!imap_perform_logout(data))
+        (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
+    }
 
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&imapc->pp);
-  Curl_dyn_free(&imapc->dyn);
+    /* Disconnect from the server */
+    Curl_pp_disconnect(&imapc->pp);
+    Curl_dyn_free(&imapc->dyn);
 
-  /* Cleanup the SASL module */
-  Curl_sasl_cleanup(conn, imapc->sasl.authused);
+    /* Cleanup the SASL module */
+    Curl_sasl_cleanup(conn, imapc->sasl.authused);
 
-  /* Cleanup our connection based variables */
-  Curl_safefree(imapc->mailbox);
-  Curl_safefree(imapc->mailbox_uidvalidity);
+    /* Cleanup our connection based variables */
+    Curl_safefree(imapc->mailbox);
+    Curl_safefree(imapc->mailbox_uidvalidity);
+    memset(imapc, 0, sizeof(*imapc));
+  }
 
   return CURLE_OK;
 }
index 784ee97e550618f8ee5bc16a689fae12211d2f60..b3821857d13de35646a6a369b0b4b06556ff735f 100644 (file)
@@ -85,6 +85,7 @@ struct imap_conn {
   BIT(tls_supported);         /* StartTLS capability supported by server */
   BIT(login_disabled);        /* LOGIN command disabled by server */
   BIT(ir_supported);          /* Initial response supported by server */
+  BIT(initialised);           /* members have been initialised */
 };
 
 extern const struct Curl_handler Curl_handler_imap;
index c1c230e241d9ccfec08a4c489f0b051354b1ed72..99fd91f9102febe32f29dfb43eeb7f807aae5693 100644 (file)
@@ -855,7 +855,6 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
             mech->name);
       return CURLE_FAILED_INIT;
     }
-    Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
   }
 
   infof(data, "Trying mechanism %s...", mech->name);
@@ -914,9 +913,16 @@ Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
   return choose_mech(data, conn);
 }
 
+void
+Curl_sec_conn_init(struct connectdata *conn)
+{
+  Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
+  conn->in_buffer.index = 0;
+  conn->in_buffer.eof_flag = 0;
+}
 
 void
-Curl_sec_end(struct connectdata *conn)
+Curl_sec_conn_destroy(struct connectdata *conn)
 {
   if(conn->mech && conn->mech->end)
     conn->mech->end(conn->app_data);
index 69bf421b750d68c1b072e0a9237ea78223bee6c8..5f4d42da40627ccb2e3fd02bc9e697c7ce7c2957 100644 (file)
@@ -151,11 +151,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
 /* initialize stuff to prepare for reading a fresh new response */
 void Curl_pp_init(struct pingpong *pp)
 {
+  DEBUGASSERT(!pp->initialised);
   pp->nread_resp = 0;
   pp->response = Curl_now(); /* start response time-out now! */
   pp->pending_resp = TRUE;
   Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
   Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
+  pp->initialised = TRUE;
 }
 
 /***********************************************************************
@@ -450,8 +452,11 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
 
 CURLcode Curl_pp_disconnect(struct pingpong *pp)
 {
-  Curl_dyn_free(&pp->sendbuf);
-  Curl_dyn_free(&pp->recvbuf);
+  if(pp->initialised) {
+    Curl_dyn_free(&pp->sendbuf);
+    Curl_dyn_free(&pp->recvbuf);
+    memset(pp, 0, sizeof(*pp));
+  }
   return CURLE_OK;
 }
 
index dcd565fafd28202170b09bc2fec7c53f6fe204d8..d9fdbdbac2c86fed0df496c82bb7a3e5b84fb2c5 100644 (file)
@@ -70,6 +70,7 @@ struct pingpong {
   CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
   bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
                     const char *ptr, size_t len, int *code);
+  BIT(initialised);
 };
 
 #define PINGPONG_SETUP(pp,s,e)                   \
index af2781eeb08d88ee8da1346e0efb3ebbe666131e..1f7929cc526abe315c4ad9ce234fbd4666581a48 100644 (file)
@@ -130,14 +130,19 @@ const struct Curl_handler Curl_handler_rtsp = {
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
+  struct rtsp_conn *rtspc = &conn->proto.rtspc;
   struct RTSP *rtsp;
   (void)conn;
 
+  if(!rtspc->initialised) {
+    Curl_dyn_init(&rtspc->buf, MAX_RTP_BUFFERSIZE);
+    rtspc->initialised = TRUE;
+  }
+
   data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP));
   if(!rtsp)
     return CURLE_OUT_OF_MEMORY;
 
-  Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE);
   return CURLE_OK;
 }
 
@@ -182,9 +187,13 @@ static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
 static CURLcode rtsp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead)
 {
+  struct rtsp_conn *rtspc = &conn->proto.rtspc;
   (void) dead;
   (void) data;
-  Curl_dyn_free(&conn->proto.rtspc.buf);
+  if(rtspc->initialised) {
+    Curl_dyn_free(&conn->proto.rtspc.buf);
+    rtspc->initialised = FALSE;
+  }
   return CURLE_OK;
 }
 
index 68f6f4fe01ec8ffb2405efd9ec3f8e56c993d574..2506ea9cb849b0e4e172c541f96760c526d74f89 100644 (file)
@@ -53,6 +53,7 @@ struct rtsp_conn {
   size_t rtp_len;
   rtp_parse_st state;
   BIT(in_header);
+  BIT(initialised);
 };
 
 /****************************************************************************
index ffabad59cf7fe63d1b6ed0852138ddc80d4a857e..a745090b5f6fc9fded97f3f348f81bb89a3c8794 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -90,6 +90,7 @@
 #include "hsts.h"
 #include "noproxy.h"
 #include "cfilters.h"
+#include "curl_krb5.h"
 #include "idn.h"
 
 /* And now for the protocols */
@@ -506,39 +507,38 @@ CURLcode Curl_open(struct Curl_easy **curl)
 
   data->magic = CURLEASY_MAGIC_NUMBER;
 
+  Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
   Curl_req_init(&data->req);
+  Curl_initinfo(data);
+#ifndef CURL_DISABLE_HTTP
+  Curl_llist_init(&data->state.httphdrs, NULL);
+#endif
+  Curl_netrc_init(&data->state.netrc);
 
   result = Curl_resolver_init(data, &data->state.async.resolver);
   if(result) {
     DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
-    Curl_req_free(&data->req, data);
-    free(data);
-    return result;
+    goto out;
   }
 
   result = Curl_init_userdefined(data);
-  if(!result) {
-    Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
-    Curl_initinfo(data);
-
-    /* most recent connection is not yet defined */
-    data->state.lastconnect_id = -1;
-    data->state.recent_conn_id = -1;
-    /* and not assigned an id yet */
-    data->id = -1;
-    data->mid = -1;
+  if(result)
+    goto out;
+
+  /* most recent connection is not yet defined */
+  data->state.lastconnect_id = -1;
+  data->state.recent_conn_id = -1;
+  /* and not assigned an id yet */
+  data->id = -1;
+  data->mid = -1;
 #ifndef CURL_DISABLE_DOH
-    data->set.dohfor_mid = -1;
+  data->set.dohfor_mid = -1;
 #endif
 
-    data->progress.flags |= PGRS_HIDE;
-    data->state.current_speed = -1; /* init to negative == impossible */
-#ifndef CURL_DISABLE_HTTP
-    Curl_llist_init(&data->state.httphdrs, NULL);
-#endif
-    Curl_netrc_init(&data->state.netrc);
-  }
+  data->progress.flags |= PGRS_HIDE;
+  data->state.current_speed = -1; /* init to negative == impossible */
 
+out:
   if(result) {
     Curl_resolver_cleanup(data->state.async.resolver);
     Curl_dyn_free(&data->state.headerb);
@@ -578,6 +578,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
   Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
   Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
 #endif
+  Curl_sec_conn_destroy(conn);
   Curl_safefree(conn->user);
   Curl_safefree(conn->passwd);
   Curl_safefree(conn->sasl_authzid);
@@ -3400,6 +3401,10 @@ static CURLcode create_conn(struct Curl_easy *data,
      any failure */
   *in_connect = conn;
 
+  /* Do the unfailable inits first, before checks that may early return */
+  /* GSSAPI related inits */
+  Curl_sec_conn_init(conn);
+
   result = parseurlandfillconn(data, conn);
   if(result)
     goto out;
index 55232a8e4afb96fd2b3d0c41b63916367aee6fc2..eb415183e86d41a4fd33214e283dccc8ec6d337e 100644 (file)
@@ -156,10 +156,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   }
 
   /* Set channel binding data if available */
-  if(nego->channel_binding_data.leng > 0) {
+  if(Curl_dyn_len(&nego->channel_binding_data)) {
     memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
-    chan.application_data.length = nego->channel_binding_data.leng;
-    chan.application_data.value = nego->channel_binding_data.bufr;
+    chan.application_data.length = Curl_dyn_len(&nego->channel_binding_data);
+    chan.application_data.value = Curl_dyn_ptr(&nego->channel_binding_data);
     chan_bindings = &chan;
   }
 
index 299b7c8c3e333e031ca981fcd6eb3d33bb9be495..b20f82adbbba4c54a9c2f6eeb99e84d53e7c4307 100644 (file)
@@ -2086,10 +2086,14 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data,
   struct SSHPROTO *ssh;
   struct ssh_conn *sshc = &conn->proto.sshc;
 
+  if(!sshc->initialised) {
+    Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
+    sshc->initialised = TRUE;
+  }
+
   data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
   if(!ssh)
     return CURLE_OUT_OF_MEMORY;
-  Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
 
   return CURLE_OK;
 }
@@ -2295,47 +2299,50 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
 static void sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data)
 {
   (void)data;
-  if(sshc->ssh_session) {
-    ssh_free(sshc->ssh_session);
-    sshc->ssh_session = NULL;
-  }
+  if(sshc->initialised) {
+    if(sshc->ssh_session) {
+      ssh_free(sshc->ssh_session);
+      sshc->ssh_session = NULL;
+    }
 
-  /* worst-case scenario cleanup */
-  DEBUGASSERT(sshc->ssh_session == NULL);
-  DEBUGASSERT(sshc->scp_session == NULL);
+    /* worst-case scenario cleanup */
+    DEBUGASSERT(sshc->ssh_session == NULL);
+    DEBUGASSERT(sshc->scp_session == NULL);
 
-  if(sshc->readdir_tmp) {
-    ssh_string_free_char(sshc->readdir_tmp);
-    sshc->readdir_tmp = NULL;
-  }
-  if(sshc->quote_attrs) {
-    sftp_attributes_free(sshc->quote_attrs);
-    sshc->quote_attrs = NULL;
-  }
-  if(sshc->readdir_attrs) {
-    sftp_attributes_free(sshc->readdir_attrs);
-    sshc->readdir_attrs = NULL;
-  }
-  if(sshc->readdir_link_attrs) {
-    sftp_attributes_free(sshc->readdir_link_attrs);
-    sshc->readdir_link_attrs = NULL;
-  }
-  if(sshc->privkey) {
-    ssh_key_free(sshc->privkey);
-    sshc->privkey = NULL;
-  }
-  if(sshc->pubkey) {
-    ssh_key_free(sshc->pubkey);
-    sshc->pubkey = NULL;
-  }
+    if(sshc->readdir_tmp) {
+      ssh_string_free_char(sshc->readdir_tmp);
+      sshc->readdir_tmp = NULL;
+    }
+    if(sshc->quote_attrs) {
+      sftp_attributes_free(sshc->quote_attrs);
+      sshc->quote_attrs = NULL;
+    }
+    if(sshc->readdir_attrs) {
+      sftp_attributes_free(sshc->readdir_attrs);
+      sshc->readdir_attrs = NULL;
+    }
+    if(sshc->readdir_link_attrs) {
+      sftp_attributes_free(sshc->readdir_link_attrs);
+      sshc->readdir_link_attrs = NULL;
+    }
+    if(sshc->privkey) {
+      ssh_key_free(sshc->privkey);
+      sshc->privkey = NULL;
+    }
+    if(sshc->pubkey) {
+      ssh_key_free(sshc->pubkey);
+      sshc->pubkey = NULL;
+    }
 
-  Curl_safefree(sshc->rsa_pub);
-  Curl_safefree(sshc->rsa);
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
-  Curl_dyn_free(&sshc->readdir_buf);
-  Curl_safefree(sshc->readdir_linkPath);
-  SSH_STRING_FREE_CHAR(sshc->homedir);
+    Curl_safefree(sshc->rsa_pub);
+    Curl_safefree(sshc->rsa);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    Curl_dyn_free(&sshc->readdir_buf);
+    Curl_safefree(sshc->readdir_linkPath);
+    SSH_STRING_FREE_CHAR(sshc->homedir);
+    sshc->initialised = FALSE;
+  }
 }
 
 /* BLOCKING, but the function is using the state machine so the only reason
index c2e2223519cf46499db78d5b71e5a6396139c0d7..1587d251d15c2452343adb30dcd260db7c14912f 100644 (file)
@@ -1605,7 +1605,6 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
         if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
            ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
             LIBSSH2_SFTP_S_IFLNK)) {
-          Curl_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
           result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
                                  sshp->readdir_filename);
           state(data, SSH_SFTP_READDIR_LINK);
@@ -2430,7 +2429,6 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
         sshc->actualcode = result ? result : CURLE_SSH;
         break;
       }
-      Curl_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
       state(data, SSH_SFTP_READDIR);
       break;
 
@@ -3024,13 +3022,23 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
 static CURLcode ssh_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
+  struct ssh_conn *sshc = &conn->proto.sshc;
   struct SSHPROTO *ssh;
   (void)conn;
 
+  if(!sshc->initialised) {
+    /* other ssh implementations do something here, let's keep
+     * the initialised flag correct even if this implementation does not. */
+    sshc->initialised = TRUE;
+  }
+
   data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
   if(!ssh)
     return CURLE_OUT_OF_MEMORY;
 
+  Curl_dyn_init(&ssh->readdir, CURL_PATH_MAX * 2);
+  Curl_dyn_init(&ssh->readdir_link, CURL_PATH_MAX);
+
   return CURLE_OK;
 }
 
@@ -3331,60 +3339,62 @@ static int sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
 {
   int rc;
 
-  if(sshc->kh) {
-    libssh2_knownhost_free(sshc->kh);
-    sshc->kh = NULL;
-  }
-
-  if(sshc->ssh_agent) {
-    rc = libssh2_agent_disconnect(sshc->ssh_agent);
-    if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) {
-      return rc;
+  if(sshc->initialised) {
+    if(sshc->kh) {
+      libssh2_knownhost_free(sshc->kh);
+      sshc->kh = NULL;
     }
-    if(rc < 0) {
-      char *err_msg = NULL;
-      (void)libssh2_session_last_error(sshc->ssh_session,
-                                       &err_msg, NULL, 0);
-      infof(data, "Failed to disconnect from libssh2 agent: %d %s",
-            rc, err_msg);
-    }
-    libssh2_agent_free(sshc->ssh_agent);
-    sshc->ssh_agent = NULL;
 
-    /* NB: there is no need to free identities, they are part of internal
-       agent stuff */
-    sshc->sshagent_identity = NULL;
-    sshc->sshagent_prev_identity = NULL;
-  }
+    if(sshc->ssh_agent) {
+      rc = libssh2_agent_disconnect(sshc->ssh_agent);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) {
+        return rc;
+      }
+      if(rc < 0) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "Failed to disconnect from libssh2 agent: %d %s",
+              rc, err_msg);
+      }
+      libssh2_agent_free(sshc->ssh_agent);
+      sshc->ssh_agent = NULL;
 
-  if(sshc->ssh_session) {
-    rc = libssh2_session_free(sshc->ssh_session);
-    if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) {
-      return rc;
+      /* NB: there is no need to free identities, they are part of internal
+         agent stuff */
+      sshc->sshagent_identity = NULL;
+      sshc->sshagent_prev_identity = NULL;
     }
-    if(rc < 0) {
-      char *err_msg = NULL;
-      (void)libssh2_session_last_error(sshc->ssh_session,
-                                       &err_msg, NULL, 0);
-      infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
-    }
-    sshc->ssh_session = NULL;
-  }
 
-  /* worst-case scenario cleanup */
-  DEBUGASSERT(sshc->ssh_session == NULL);
-  DEBUGASSERT(sshc->ssh_channel == NULL);
-  DEBUGASSERT(sshc->sftp_session == NULL);
-  DEBUGASSERT(sshc->sftp_handle == NULL);
-  DEBUGASSERT(sshc->kh == NULL);
-  DEBUGASSERT(sshc->ssh_agent == NULL);
-
-  Curl_safefree(sshc->rsa_pub);
-  Curl_safefree(sshc->rsa);
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
-  Curl_safefree(sshc->homedir);
+    if(sshc->ssh_session) {
+      rc = libssh2_session_free(sshc->ssh_session);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) {
+        return rc;
+      }
+      if(rc < 0) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
+      }
+      sshc->ssh_session = NULL;
+    }
 
+    /* worst-case scenario cleanup */
+    DEBUGASSERT(sshc->ssh_session == NULL);
+    DEBUGASSERT(sshc->ssh_channel == NULL);
+    DEBUGASSERT(sshc->sftp_session == NULL);
+    DEBUGASSERT(sshc->sftp_handle == NULL);
+    DEBUGASSERT(sshc->kh == NULL);
+    DEBUGASSERT(sshc->ssh_agent == NULL);
+
+    Curl_safefree(sshc->rsa_pub);
+    Curl_safefree(sshc->rsa);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    Curl_safefree(sshc->homedir);
+    sshc->initialised = FALSE;
+  }
   return 0;
 }
 
@@ -3426,6 +3436,7 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
 
   Curl_safefree(sshp->path);
   Curl_dyn_free(&sshp->readdir);
+  Curl_dyn_free(&sshp->readdir_link);
 
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
index bbbe95d7dec4b0a85ec8b3c8038e22b39f34f8ae..e882ff3d33828bfe69ebc2c40e59371a8f8e994e 100644 (file)
@@ -212,6 +212,7 @@ struct ssh_conn {
   byte handle[WOLFSSH_MAX_HANDLE];
   curl_off_t offset;
 #endif /* USE_LIBSSH */
+  BIT(initialised);
 };
 
 #ifdef USE_LIBSSH