From b4e6761de0e6ea8a6592ea5407d721282292432c Mon Sep 17 00:00:00 2001 From: "RELAUNCH DEPT." <215028820+ceodaradigu@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:40:14 +0200 Subject: [PATCH] publickey: preserve list state across EAGAIN in `libssh2_publickey_list_fetch()` `libssh2_publickey_list_fetch()` preserves its packet-receive state when a non-blocking channel returns `LIBSSH2_ERROR_EAGAIN`, but the accumulated list, key count and capacity were local to a single invocation. Keep those aggregate values in the opaque publickey handle while a list fetch is in progress. Restore them on retry, transfer ownership to the caller on success, and release them on an error or subsystem shutdown. Fixes GHSA-qwvp-xm4w-32m7 Closes #2578 --- src/libssh2_priv.h | 3 +++ src/publickey.c | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 9bb090d3..981580c3 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -675,6 +675,9 @@ struct _LIBSSH2_PUBLICKEY { unsigned char listFetch_buffer[12]; unsigned char *listFetch_data; size_t listFetch_data_len; + libssh2_publickey_list *listFetch_list; + unsigned long listFetch_keys; + unsigned long listFetch_max_keys; }; #if defined(__clang__) && __clang_major__ >= 13 #pragma clang diagnostic pop diff --git a/src/publickey.c b/src/publickey.c index 36a60c14..6d582623 100644 --- a/src/publickey.c +++ b/src/publickey.c @@ -795,9 +795,9 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, { LIBSSH2_CHANNEL *channel; LIBSSH2_SESSION *session; - libssh2_publickey_list *list = NULL; + libssh2_publickey_list *list; /* 12 = packet_len(4) + list_len(4) + "list"(4) */ - unsigned long buffer_len = 12, keys = 0, max_keys = 0, i; + unsigned long buffer_len = 12, keys, max_keys, i; int response; int rc; @@ -809,6 +809,9 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, if(pkey->listFetch_state == ssh2_NB_state_idle) { pkey->listFetch_data = NULL; + pkey->listFetch_list = NULL; + pkey->listFetch_keys = 0; + pkey->listFetch_max_keys = 0; pkey->listFetch_s = pkey->listFetch_buffer; ssh2_htonu32(pkey->listFetch_s, (uint32_t)(buffer_len - 4)); @@ -824,6 +827,10 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, pkey->listFetch_state = ssh2_NB_state_created; } + list = pkey->listFetch_list; + keys = pkey->listFetch_keys; + max_keys = pkey->listFetch_max_keys; + if(pkey->listFetch_state == ssh2_NB_state_created) { ssize_t nwritten; nwritten = ssh2_channel_write(channel, 0, @@ -844,8 +851,12 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, for(;;) { rc = publickey_packet_receive(pkey, &pkey->listFetch_data, &pkey->listFetch_data_len); - if(rc == LIBSSH2_ERROR_EAGAIN) + if(rc == LIBSSH2_ERROR_EAGAIN) { + pkey->listFetch_list = list; + pkey->listFetch_keys = keys; + pkey->listFetch_max_keys = max_keys; return rc; + } else if(rc) { ssh2_err(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem"); @@ -919,6 +930,9 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, SSH2_SAFEFREE(session, pkey->listFetch_data); *pkey_list = list; *num_keys = keys; + pkey->listFetch_list = NULL; + pkey->listFetch_keys = 0; + pkey->listFetch_max_keys = 0; pkey->listFetch_state = ssh2_NB_state_idle; return 0; } @@ -1200,6 +1214,9 @@ err_exit: SSH2_SAFEFREE(session, pkey->listFetch_data); if(list) libssh2_publickey_list_free(pkey, list); + pkey->listFetch_list = NULL; + pkey->listFetch_keys = 0; + pkey->listFetch_max_keys = 0; pkey->listFetch_state = ssh2_NB_state_idle; return -1; } @@ -1235,6 +1252,7 @@ void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey) { LIBSSH2_SESSION *session; + unsigned long i; int rc; if(!pkey) @@ -1253,6 +1271,17 @@ int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey) SSH2_SAFEFREE(session, pkey->remove_packet); if(pkey->listFetch_data) SSH2_SAFEFREE(session, pkey->listFetch_data); + if(pkey->listFetch_list) { + for(i = 0; i < pkey->listFetch_keys; i++) { + if(pkey->listFetch_list[i].attrs) + SSH2_FREE(session, pkey->listFetch_list[i].attrs); + if(pkey->listFetch_list[i].packet) + SSH2_FREE(session, pkey->listFetch_list[i].packet); + } + SSH2_SAFEFREE(session, pkey->listFetch_list); + pkey->listFetch_keys = 0; + pkey->listFetch_max_keys = 0; + } rc = ssh2_channel_free(pkey->channel); if(rc == LIBSSH2_ERROR_EAGAIN)