libssh/CVE-2026-59848.patch
Pavol Žáčik a97adc51f9
Backport CVE patches from 0.11.5
Resolves: RHEL-215674
Resolves: RHEL-215675
Resolves: RHEL-215676
Resolves: RHEL-215677
Resolves: RHEL-215678
Resolves: RHEL-215679
Resolves: RHEL-215680

Assisted-by: Ymir
2026-07-30 12:22:19 +02:00

855 lines
23 KiB
Diff

From 104b189167d79d76955844acd0fe4b393eccfe42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Mon, 1 Jun 2026 16:33:03 +0200
Subject: [PATCH 1/2] CVE-2026-59848 sftp: handle responses with unknown
request IDs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adds a new field to sftp_session_struct,
containing a list of outstanding request IDs.
An ID is added to the list when a request
is constructed and removed when the corresponding
request is received. If a client receives a response
with an unknown request ID, it reports an error.
Storing responses with unknown request IDs in
the response queue could be abused by a malicious
SFTP server which could deplete client memory
this way.
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/sftp.h | 1 +
src/sftp.c | 227 ++++++++++++++++++++-----
tests/client/CMakeLists.txt | 1 +
tests/client/torture_sftp_request_id.c | 183 ++++++++++++++++++++
4 files changed, 370 insertions(+), 42 deletions(-)
create mode 100644 tests/client/torture_sftp_request_id.c
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index 8c14b21d..f2344ad3 100644
--- a/include/libssh/sftp.h
+++ b/include/libssh/sftp.h
@@ -90,6 +90,7 @@ struct sftp_session_struct {
void **handles;
sftp_ext ext;
sftp_packet read_packet;
+ struct ssh_list *outstanding_ids;
};
struct sftp_packet_struct {
diff --git a/src/sftp.c b/src/sftp.c
index e3a842d5..68fa5972 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -27,6 +27,7 @@
#include "config.h"
#include <stdbool.h>
+#include <inttypes.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
@@ -150,6 +151,12 @@ sftp_session sftp_new(ssh_session session)
goto error;
}
+ sftp->outstanding_ids = ssh_list_new();
+ if (sftp->outstanding_ids == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+
if (ssh_channel_open_session(sftp->channel)) {
goto error;
}
@@ -166,6 +173,7 @@ error:
if (sftp->channel != NULL) {
ssh_channel_free(sftp->channel);
}
+ ssh_list_free(sftp->outstanding_ids);
if (sftp->read_packet != NULL) {
if (sftp->read_packet->payload != NULL) {
SSH_BUFFER_FREE(sftp->read_packet->payload);
@@ -197,6 +205,12 @@ sftp_new_channel(ssh_session session, ssh_channel channel)
goto error;
}
+ sftp->outstanding_ids = ssh_list_new();
+ if (sftp->outstanding_ids == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+
sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct));
if (sftp->read_packet == NULL) {
ssh_set_error_oom(session);
@@ -218,6 +232,7 @@ error:
if (sftp->ext != NULL) {
sftp_ext_free(sftp->ext);
}
+ ssh_list_free(sftp->outstanding_ids);
if (sftp->read_packet != NULL) {
if (sftp->read_packet->payload != NULL) {
SSH_BUFFER_FREE(sftp->read_packet->payload);
@@ -357,6 +372,7 @@ void sftp_server_free(sftp_session sftp)
void sftp_free(sftp_session sftp)
{
sftp_request_queue ptr;
+ struct ssh_iterator *id_it = NULL;
if (sftp == NULL) {
return;
@@ -383,6 +399,12 @@ void sftp_free(sftp_session sftp)
sftp_ext_free(sftp->ext);
+ id_it = ssh_list_get_iterator(sftp->outstanding_ids);
+ for (; id_it != NULL; id_it = id_it->next) {
+ free((uint32_t *)id_it->data);
+ }
+ ssh_list_free(sftp->outstanding_ids);
+
SAFE_FREE(sftp);
}
@@ -577,6 +599,8 @@ static sftp_message sftp_get_message(sftp_packet packet)
{
sftp_session sftp = packet->sftp;
sftp_message msg = NULL;
+ struct ssh_iterator *id_it = NULL;
+ bool id_found = false;
int rc;
switch(packet->type) {
@@ -624,6 +648,28 @@ static sftp_message sftp_get_message(sftp_packet packet)
msg->id,
msg->packet_type);
+ /* Validate that this ID is in our outstanding requests list */
+ id_it = ssh_list_get_iterator(sftp->outstanding_ids);
+ for (; id_it != NULL; id_it = id_it->next) {
+ uint32_t *stored_id = (uint32_t *)id_it->data;
+ if (*stored_id == msg->id) {
+ id_found = true;
+ ssh_list_remove(sftp->outstanding_ids, id_it);
+ free(stored_id);
+ break;
+ }
+ }
+
+ if (!id_found) {
+ ssh_set_error(packet->sftp->session,
+ SSH_FATAL,
+ "Unknown request ID %" PRIu32,
+ msg->id);
+ sftp_message_free(msg);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
return msg;
}
@@ -911,10 +957,45 @@ static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
/*
* Assigns a new SFTP ID for new requests and assures there is no collision
* between them.
- * Returns a new ID ready to use in a request
+ * @brief Assigns a new SFTP ID for new requests and assures there is no
+ * collision between them.
+ *
+ * @param sftp The sftp session handle.
+ * @param id_out Pointer to store the new ID.
+ *
+ * @returns SSH_OK on success with the new ID stored in *id_out
+ * @returns SSH_ERROR on failure with the sftp and ssh errors set
*/
-static inline uint32_t sftp_get_new_id(sftp_session session) {
- return ++session->id_counter;
+static int sftp_get_new_id(sftp_session sftp, uint32_t *id_out)
+{
+ uint32_t *id = NULL;
+ int rc;
+
+ if (id_out == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = malloc(sizeof(uint32_t));
+ if (id == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ *id = ++sftp->id_counter;
+ rc = ssh_list_append(sftp->outstanding_ids, id);
+ if (rc != SSH_OK) {
+ free(id);
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ *id_out = *id;
+
+ return SSH_OK;
}
static sftp_status_message parse_status_msg(sftp_message msg){
@@ -1028,6 +1109,11 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
payload = ssh_buffer_new();
if (payload == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1035,8 +1121,6 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(payload,
"ds",
id,
@@ -1560,6 +1644,11 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
int rc;
if (dir->buffer == NULL) {
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
payload = ssh_buffer_new();
if (payload == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1567,8 +1656,6 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(payload,
"dS",
id,
@@ -1693,6 +1780,11 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1700,8 +1792,6 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dS",
id,
@@ -1804,6 +1894,11 @@ sftp_file sftp_open(sftp_session sftp,
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1831,7 +1926,6 @@ sftp_file sftp_open(sftp_session sftp,
sftp_flags |= SSH_FXF_APPEND;
}
SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags);
- id = sftp_get_new_id(sftp);
rc = ssh_buffer_pack(buffer,
"dsd",
@@ -1936,14 +2030,17 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
return 0;
}
+ rc = sftp_get_new_id(handle->sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
return -1;
}
- id = sftp_get_new_id(handle->sftp);
-
rc = ssh_buffer_pack(buffer,
"dSqd",
id,
@@ -2036,6 +2133,11 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2043,8 +2145,6 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dSqd",
id,
@@ -2162,6 +2262,11 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
size_t packetlen;
int rc;
+ rc = sftp_get_new_id(file->sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2169,8 +2274,6 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
return -1;
}
- id = sftp_get_new_id(file->sftp);
-
rc = ssh_buffer_pack(buffer,
"dSqdP",
id,
@@ -2280,6 +2383,11 @@ int sftp_unlink(sftp_session sftp, const char *file) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2287,8 +2395,6 @@ int sftp_unlink(sftp_session sftp, const char *file) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2355,6 +2461,11 @@ int sftp_rmdir(sftp_session sftp, const char *directory) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2362,8 +2473,6 @@ int sftp_rmdir(sftp_session sftp, const char *directory) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2428,6 +2537,11 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2439,8 +2553,6 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
attr.permissions = mode;
attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2527,6 +2639,11 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2534,8 +2651,6 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dss",
id,
@@ -2611,6 +2726,11 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr)
sftp_status_message status = NULL;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2618,8 +2738,6 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2741,6 +2859,11 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
return -1;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2748,8 +2871,6 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
/* TODO check for version number if they ever fix it. */
if (ssh_get_openssh_version(sftp->session)) {
rc = ssh_buffer_pack(buffer,
@@ -2839,6 +2960,12 @@ char *sftp_readlink(sftp_session sftp, const char *path)
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
+
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2846,8 +2973,6 @@ char *sftp_readlink(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2965,6 +3090,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2972,8 +3102,6 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dss",
id,
@@ -3040,6 +3168,11 @@ int sftp_fsync(sftp_file file)
}
sftp = file->sftp;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3047,8 +3180,6 @@ int sftp_fsync(sftp_file file)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dsS",
id,
@@ -3140,6 +3271,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file)
}
sftp = file->sftp;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3147,8 +3283,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dsS",
id,
@@ -3227,6 +3361,11 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3234,8 +3373,6 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -3318,6 +3455,11 @@ static sftp_attributes sftp_xstat(sftp_session sftp,
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3325,8 +3467,6 @@ static sftp_attributes sftp_xstat(sftp_session sftp,
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -3396,6 +3536,11 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
+ rc = sftp_get_new_id(file->sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(file->sftp->session);
@@ -3403,8 +3548,6 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
- id = sftp_get_new_id(file->sftp);
-
rc = ssh_buffer_pack(buffer,
"dS",
id,
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 14f24c26..56aaa805 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -43,6 +43,7 @@ if (WITH_SFTP)
torture_sftp_dir
torture_sftp_read
torture_sftp_fsync
+ torture_sftp_request_id
${SFTP_BENCHMARK_TESTS})
endif (WITH_SFTP)
diff --git a/tests/client/torture_sftp_request_id.c b/tests/client/torture_sftp_request_id.c
new file mode 100644
index 00000000..f75ab696
--- /dev/null
+++ b/tests/client/torture_sftp_request_id.c
@@ -0,0 +1,183 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "sftp.c"
+#include "torture.h"
+
+#include <pwd.h>
+#include <sys/types.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = torture_ssh_session(s,
+ TORTURE_SSH_SERVER,
+ NULL,
+ TORTURE_SSH_USER_ALICE,
+ NULL);
+ assert_non_null(s->ssh.session);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_request_id_null(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ int rc;
+
+ rc = sftp_get_new_id(sftp, NULL);
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_sftp_request_id_add(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ uint32_t id1, id2;
+ int rc;
+ size_t count;
+
+ /* The list of IDs should be empty at first */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ /* Request a new ID */
+ rc = sftp_get_new_id(sftp, &id1);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Check that the list has one ID now */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 1);
+
+ /* Request another ID */
+ rc = sftp_get_new_id(sftp, &id2);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Check that the IDs differ */
+ assert_int_not_equal(id1, id2);
+
+ /* Check that the list has two IDs now */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 2);
+}
+
+static void torture_sftp_request_id_remove(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ sftp_attributes attr = NULL;
+ size_t count;
+
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ /* We send a request and receive a response */
+ attr = sftp_stat(sftp, SSH_EXECUTABLE);
+ assert_non_null(attr);
+
+ /* The number of outstanding requests should be back to 0 */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ sftp_attributes_free(attr);
+}
+
+static void torture_sftp_request_id_unknown(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ ssh_buffer buffer = NULL;
+ uint32_t id = 0;
+ int rc;
+ size_t count;
+
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ buffer = ssh_buffer_new();
+ assert_non_null(buffer);
+
+ rc = ssh_buffer_pack(buffer, "ds", id, "/tmp");
+ assert_int_equal(rc, SSH_OK);
+
+ /* Send a request without saving the request ID */
+ rc = sftp_packet_write(sftp, SSH_FXP_OPENDIR, buffer);
+ assert_int_not_equal(rc, -1);
+ SSH_BUFFER_FREE(buffer);
+
+ /* An attempt to read and dispatch the response should fail
+ * because the ID is not in the outstanding list */
+ rc = sftp_read_and_dispatch(sftp);
+ assert_int_equal(rc, -1);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_null,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_add,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_remove,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_unknown,
+ session_setup,
+ session_teardown),
+ };
+
+ ssh_init();
+
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
+ ssh_finalize();
+
+ return rc;
+}
From af4c693af9557ea4a2cb8f3f2e94f15038c9779a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Wed, 3 Jun 2026 12:56:09 +0200
Subject: [PATCH 2/2] CVE-2026-59848 sftp: Initialize sftp_request_queue ptr in
sftp_free
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
src/sftp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sftp.c b/src/sftp.c
index 68fa5972..870758a8 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -371,7 +371,7 @@ void sftp_server_free(sftp_session sftp)
void sftp_free(sftp_session sftp)
{
- sftp_request_queue ptr;
+ sftp_request_queue ptr = NULL;
struct ssh_iterator *id_it = NULL;
if (sftp == NULL) {