Skip to content

Commit fd885f0

Browse files
authored
Merge pull request #349 from abhinavagarwal07/fix-sftp-read-buffer-safety
fix malformed SFTP reply handling
2 parents d749988 + 43b2708 commit fd885f0

2 files changed

Lines changed: 61 additions & 15 deletions

File tree

sshfs.c

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,23 +1441,24 @@ static int do_read(struct conn *conn, struct buffer *buf)
14411441

14421442
static int sftp_read(struct conn *conn, uint8_t *type, struct buffer *buf)
14431443
{
1444-
int res;
1444+
int res = -1;
14451445
struct buffer buf2;
14461446
uint32_t len;
14471447
buf_init(&buf2, 5);
1448-
res = do_read(conn, &buf2);
1449-
if (res != -1) {
1450-
if (buf_get_uint32(&buf2, &len) == -1)
1451-
return -1;
1452-
if (len > MAX_REPLY_LEN) {
1453-
fprintf(stderr, "reply len too large: %u\n", len);
1454-
return -1;
1455-
}
1456-
if (buf_get_uint8(&buf2, type) == -1)
1457-
return -1;
1458-
buf_init(buf, len - 1);
1459-
res = do_read(conn, buf);
1448+
if (do_read(conn, &buf2) == -1)
1449+
goto out;
1450+
if (buf_get_uint32(&buf2, &len) == -1)
1451+
goto out;
1452+
if (len < 1 || len > MAX_REPLY_LEN) {
1453+
fprintf(stderr, "bad reply len: %u\n", len);
1454+
goto out;
14601455
}
1456+
if (buf_get_uint8(&buf2, type) == -1)
1457+
goto out;
1458+
buf_init(buf, len - 1);
1459+
res = do_read(conn, buf);
1460+
1461+
out:
14611462
buf_free(&buf2);
14621463
return res;
14631464
}
@@ -1530,10 +1531,14 @@ static int process_one_request(struct conn *conn)
15301531

15311532
buf_init(&buf, 0);
15321533
res = sftp_read(conn, &type, &buf);
1533-
if (res == -1)
1534+
if (res == -1) {
1535+
buf_free(&buf);
15341536
return -1;
1535-
if (buf_get_uint32(&buf, &id) == -1)
1537+
}
1538+
if (buf_get_uint32(&buf, &id) == -1) {
1539+
buf_free(&buf);
15361540
return -1;
1541+
}
15371542

15381543
pthread_mutex_lock(&sshfs.lock);
15391544
req = (struct request *)

test/test_sshfs.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,44 @@ def test_direct_io(tmpdir, capfd):
851851
raise
852852
else:
853853
umount(mount_process, mnt_dir)
854+
855+
856+
def test_bad_sftp_reply_len(tmpdir):
857+
"""sshfs must reject a zero-length SFTP reply instead of underflowing."""
858+
helper = tmpdir.join("bad_sftp.py")
859+
helper.write(
860+
'#!/usr/bin/env python3\n'
861+
'import os, struct, sys\n'
862+
'def read_pkt():\n'
863+
' hdr = os.read(0, 4)\n'
864+
' if len(hdr) < 4: sys.exit(0)\n'
865+
' n = struct.unpack(">I", hdr)[0]\n'
866+
' while n:\n'
867+
' c = os.read(0, n)\n'
868+
' if not c: sys.exit(0)\n'
869+
' n -= len(c)\n'
870+
'read_pkt()\n'
871+
'os.write(1, struct.pack(">IBI", 5, 2, 3))\n' # SSH_FXP_VERSION v3
872+
'read_pkt()\n'
873+
'os.write(1, struct.pack(">IB", 0, 0))\n' # len=0 reply (5 bytes on wire)
874+
)
875+
helper.chmod(0o755)
876+
877+
mnt_dir = str(tmpdir.mkdir("mnt"))
878+
cmdline = base_cmdline + [
879+
pjoin(basename, "sshfs"),
880+
"-f",
881+
"dummy:/",
882+
mnt_dir,
883+
"-o", f"ssh_command={helper}",
884+
]
885+
res = subprocess.run(
886+
cmdline,
887+
stdin=subprocess.DEVNULL,
888+
stdout=subprocess.PIPE,
889+
stderr=subprocess.PIPE,
890+
timeout=10,
891+
text=True,
892+
)
893+
assert res.returncode != 0
894+
assert "bad reply len: 0" in res.stderr

0 commit comments

Comments
 (0)