From df01168bb3863306ba0f35b50e5b2e5dd00ba9f6 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 11 Dec 2025 13:21:23 +0100
Subject: [PATCH 2/2] Reproducer for out of bounds read of SFTP extensions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

CVE: CVE-2026-3731
Upstream-Status: Backport [https://git.libssh.org/projects/libssh.git/commit/?id=02c6f5f7ec8629a7cff6a28cde9701ab10304540]

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit b90b7f24517efa7ab21506db9379aa3dce9fee7d)
(cherry picked from commit 02c6f5f7ec8629a7cff6a28cde9701ab10304540)
Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 tests/client/torture_sftp_init.c | 62 +++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/tests/client/torture_sftp_init.c b/tests/client/torture_sftp_init.c
index a17f01fe..cdc24426 100644
--- a/tests/client/torture_sftp_init.c
+++ b/tests/client/torture_sftp_init.c
@@ -72,6 +72,63 @@ static void session_setup_channel(void **state)
     assert_non_null(s->ssh.tsftp);
 }

+static void session_setup_extensions(void **state)
+{
+    struct torture_state *s = *state;
+    struct passwd *pwd = NULL;
+    int rc, count;
+    const char *name = NULL, *data = NULL;
+    sftp_session sftp = NULL;
+
+    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);
+    sftp = s->ssh.tsftp->sftp;
+
+    /* null parameter */
+    count = sftp_extensions_get_count(NULL);
+    assert_int_equal(count, 0);
+
+    count = sftp_extensions_get_count(sftp);
+    assert_int_not_equal(count, 0);
+
+    /* first null parameter */
+    name = sftp_extensions_get_name(NULL, 0);
+    assert_null(name);
+    data = sftp_extensions_get_data(NULL, 0);
+    assert_null(data);
+
+    /* First extension */
+    name = sftp_extensions_get_name(sftp, 0);
+    assert_non_null(name);
+    data = sftp_extensions_get_data(sftp, 0);
+    assert_non_null(data);
+
+    /* Last extension */
+    name = sftp_extensions_get_name(sftp, count - 1);
+    assert_non_null(name);
+    data = sftp_extensions_get_data(sftp, count - 1);
+    assert_non_null(data);
+
+    /* Overrun */
+    name = sftp_extensions_get_name(sftp, count);
+    assert_null(name);
+    data = sftp_extensions_get_data(sftp, count);
+    assert_null(data);
+}
+
 static int session_teardown(void **state)
 {
     struct torture_state *s = *state;
@@ -92,7 +149,10 @@ int torture_run_tests(void) {
                                         session_teardown),
         cmocka_unit_test_setup_teardown(session_setup_channel,
                                         NULL,
-                                        session_teardown)
+                                        session_teardown),
+        cmocka_unit_test_setup_teardown(session_setup_extensions,
+                                        NULL,
+                                        session_teardown),
     };

     ssh_init();
--
2.35.6
