From a30a51003205744c10ba4439306f555206ae8497 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] 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>
(cherry picked from commit 26147eb4767937c797f97ff3b1b1663384232417)

Index: libssh-0.10.6/include/libssh/sftp.h
===================================================================
--- libssh-0.10.6.orig/include/libssh/sftp.h
+++ libssh-0.10.6/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 {
Index: libssh-0.10.6/include/libssh/sftp_priv.h
===================================================================
--- libssh-0.10.6.orig/include/libssh/sftp_priv.h
+++ libssh-0.10.6/include/libssh/sftp_priv.h
@@ -21,6 +21,8 @@
 #ifndef SFTP_PRIV_H
 #define SFTP_PRIV_H
 
+#include "libssh/sftp.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
Index: libssh-0.10.6/src/sftp.c
===================================================================
--- libssh-0.10.6.orig/src/sftp.c
+++ libssh-0.10.6/src/sftp.c
@@ -149,6 +149,12 @@ sftp_session sftp_new(ssh_session sessio
         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;
     }
@@ -165,6 +171,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);
@@ -196,6 +203,12 @@ sftp_new_channel(ssh_session session, ss
         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);
@@ -217,6 +230,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,7 +371,8 @@ 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) {
         return;
@@ -384,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);
 }
 
@@ -571,6 +592,8 @@ static sftp_message sftp_get_message(sft
 {
     sftp_session sftp = packet->sftp;
     sftp_message msg = NULL;
+    struct ssh_iterator *id_it = NULL;
+    bool id_found = false;
     int rc;
 
     switch(packet->type) {
@@ -618,9 +641,63 @@ static sftp_message sftp_get_message(sft
             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;
 }
 
+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 int sftp_read_and_dispatch(sftp_session sftp)
 {
     sftp_packet packet = NULL;
@@ -902,14 +979,17 @@ static sftp_message sftp_dequeue(sftp_se
   return NULL;
 }
 
-/*
- * 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
+ * @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;
-}
+int sftp_get_new_id(sftp_session sftp, uint32_t *id_out);
 
 static sftp_status_message parse_status_msg(sftp_message msg){
   sftp_status_message status;
@@ -1022,6 +1102,11 @@ sftp_dir sftp_opendir(sftp_session sftp,
         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);
@@ -1029,8 +1114,6 @@ sftp_dir sftp_opendir(sftp_session sftp,
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(payload,
                          "ds",
                          id,
@@ -1564,6 +1647,11 @@ sftp_attributes sftp_readdir(sftp_sessio
     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);
@@ -1571,8 +1659,6 @@ sftp_attributes sftp_readdir(sftp_sessio
             return NULL;
         }
 
-        id = sftp_get_new_id(sftp);
-
         rc = ssh_buffer_pack(payload,
                              "dS",
                              id,
@@ -1697,6 +1783,11 @@ static int sftp_handle_close(sftp_sessio
     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);
@@ -1704,8 +1795,6 @@ static int sftp_handle_close(sftp_sessio
         return -1;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "dS",
                          id,
@@ -1808,6 +1897,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);
@@ -1834,8 +1928,8 @@ sftp_file sftp_open(sftp_session sftp,
     if ((flags & O_APPEND) == O_APPEND) {
         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);
+    SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",
+            file, sftp_flags);
 
     rc = ssh_buffer_pack(buffer,
                          "dsd",
@@ -1946,7 +2040,10 @@ ssize_t sftp_read(sftp_file handle, void
     return -1;
   }
 
-  id = sftp_get_new_id(handle->sftp);
+  rc = sftp_get_new_id(handle->sftp, &id);
+  if (rc != SSH_OK) {
+    return -1;
+  }
 
   rc = ssh_buffer_pack(buffer,
                        "dSqd",
@@ -2047,7 +2144,10 @@ int sftp_async_read_begin(sftp_file file
     return -1;
   }
 
-  id = sftp_get_new_id(sftp);
+  rc = sftp_get_new_id(sftp, &id);
+  if (rc != SSH_OK) {
+      return -1;
+  }
 
   rc = ssh_buffer_pack(buffer,
                        "dSqd",
@@ -2173,7 +2273,10 @@ ssize_t sftp_write(sftp_file file, const
     return -1;
   }
 
-  id = sftp_get_new_id(file->sftp);
+  rc = sftp_get_new_id(file->sftp, &id);
+  if (rc != SSH_OK) {
+    return -1;
+  }
 
   rc = ssh_buffer_pack(buffer,
                        "dSqdP",
@@ -2284,6 +2387,11 @@ int sftp_unlink(sftp_session sftp, const
   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);
@@ -2291,8 +2399,6 @@ int sftp_unlink(sftp_session sftp, const
     return -1;
   }
 
-  id = sftp_get_new_id(sftp);
-
   rc = ssh_buffer_pack(buffer,
                        "ds",
                        id,
@@ -2359,6 +2465,11 @@ int sftp_rmdir(sftp_session sftp, const
   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);
@@ -2366,8 +2477,6 @@ int sftp_rmdir(sftp_session sftp, const
     return -1;
   }
 
-  id = sftp_get_new_id(sftp);
-
   rc = ssh_buffer_pack(buffer,
                        "ds",
                        id,
@@ -2432,6 +2541,11 @@ int sftp_mkdir(sftp_session sftp, const
     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);
@@ -2443,8 +2557,6 @@ int sftp_mkdir(sftp_session sftp, const
     attr.permissions = mode;
     attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "ds",
                          id,
@@ -2531,6 +2643,11 @@ int sftp_rename(sftp_session sftp, const
   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);
@@ -2538,8 +2655,6 @@ int sftp_rename(sftp_session sftp, const
     return -1;
   }
 
-  id = sftp_get_new_id(sftp);
-
   rc = ssh_buffer_pack(buffer,
                        "dss",
                        id,
@@ -2615,6 +2730,11 @@ int sftp_setstat(sftp_session sftp, cons
     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);
@@ -2622,8 +2742,6 @@ int sftp_setstat(sftp_session sftp, cons
         return -1;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "ds",
                          id,
@@ -2745,6 +2863,11 @@ int sftp_symlink(sftp_session sftp, cons
     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);
@@ -2752,8 +2875,6 @@ int sftp_symlink(sftp_session sftp, cons
     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,
@@ -2843,6 +2964,12 @@ char *sftp_readlink(sftp_session sftp, c
         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);
@@ -2850,8 +2977,6 @@ char *sftp_readlink(sftp_session sftp, c
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "ds",
                          id,
@@ -2969,6 +3094,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session
         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);
@@ -2976,8 +3106,6 @@ sftp_statvfs_t sftp_statvfs(sftp_session
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "dss",
                          id,
@@ -3044,6 +3172,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);
@@ -3051,8 +3184,6 @@ int sftp_fsync(sftp_file file)
         return -1;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "dsS",
                          id,
@@ -3144,6 +3275,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file f
     }
     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);
@@ -3151,8 +3287,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file f
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "dsS",
                          id,
@@ -3231,6 +3365,11 @@ char *sftp_canonicalize_path(sftp_sessio
         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);
@@ -3238,8 +3377,6 @@ char *sftp_canonicalize_path(sftp_sessio
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "ds",
                          id,
@@ -3322,6 +3459,11 @@ static sftp_attributes sftp_xstat(sftp_s
         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);
@@ -3329,8 +3471,6 @@ static sftp_attributes sftp_xstat(sftp_s
         return NULL;
     }
 
-    id = sftp_get_new_id(sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "ds",
                          id,
@@ -3400,6 +3540,11 @@ sftp_attributes sftp_fstat(sftp_file fil
         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);
@@ -3407,8 +3552,6 @@ sftp_attributes sftp_fstat(sftp_file fil
         return NULL;
     }
 
-    id = sftp_get_new_id(file->sftp);
-
     rc = ssh_buffer_pack(buffer,
                          "dS",
                          id,
Index: libssh-0.10.6/tests/client/CMakeLists.txt
===================================================================
--- libssh-0.10.6.orig/tests/client/CMakeLists.txt
+++ libssh-0.10.6/tests/client/CMakeLists.txt
@@ -49,6 +49,7 @@ if (WITH_SFTP)
         torture_sftp_dir
         torture_sftp_read
         torture_sftp_fsync
+        torture_sftp_request_id
         ${SFTP_BENCHMARK_TESTS})
 endif (WITH_SFTP)
 
Index: libssh-0.10.6/tests/client/torture_sftp_request_id.c
===================================================================
--- /dev/null
+++ libssh-0.10.6/tests/client/torture_sftp_request_id.c
@@ -0,0 +1,182 @@
+#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 receive the response should fail */
+    rc = sftp_read_and_dispatch(sftp);
+    assert_int_equal(rc, SSH_ERROR);
+}
+
+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;
+}
