dynbuf: assert init on free

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
This commit is contained in:
Stefan Eissing 2025-03-20 10:31:30 +01:00 committed by Daniel Stenberg
parent cd7eb9e0f2
commit 646b2d6ca2
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
17 changed files with 222 additions and 163 deletions

View 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 */

View 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;
}

View 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;

View File

@ -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;
}

View 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

View 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,28 +1695,31 @@ 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);
/* 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);
memset(imapc, 0, sizeof(*imapc));
}
/* 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 our connection based variables */
Curl_safefree(imapc->mailbox);
Curl_safefree(imapc->mailbox_uidvalidity);
return CURLE_OK;
}

View 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;

View 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);

View 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;
}

View 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) \

View 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;
}

View File

@ -53,6 +53,7 @@ struct rtsp_conn {
size_t rtp_len;
rtp_parse_st state;
BIT(in_header);
BIT(initialised);
};
/****************************************************************************

View File

@ -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);
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;
/* 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;

View 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;
}

View 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

View 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->initialised) {
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(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_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;
}
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;
/* 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_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);
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;

View 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