[PATCH] ceph: fail readdir when the MDS reply carries no dirfrag
From: Yuanfu Xie
Date: Wed Sep 30 2026 - 03:14:31 EST
A ceph mount oopses in ceph_readdir() when the MDS answers a READDIR
request with a reply whose extra blob is empty. parse_reply_info()
skips reply blobs whose length is zero, so parse_reply_info_readdir()
never runs, r_reply_info.dir_dir stays NULL, and the reply still
parses as successful. ceph_mdsc_do_request() returns 0 and
ceph_readdir() then reads rinfo->dir_dir->frag with no NULL check:
/* extra */
ceph_decode_32_safe(&p, end, len, bad);
if (len > 0) {
...
err = parse_reply_info_extra(&p, p + len, req, features, s);
...
}
The oops on v7.3.0-rc4-00537-ga3ff15db6820, one getdents64(2) on the
mount root after an ms_mode=legacy mount, trimmed:
BUG: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
CPU: 1 UID: 0 PID: 266 Comm: ls Tainted: G N 7.3.0-rc4-00537-ga3ff15db6820 #1 PREEMPT(lazy)
RIP: 0010:ceph_readdir+0xbff/0x3ea0
Call Trace:
<TASK>
? __pfx_ceph_readdir+0x10/0x10
wrap_directory_iterator+0xa2/0xe0
iterate_dir+0x1da/0x5d0
__x64_sys_getdents64+0x13b/0x2a0
do_syscall_64+0xdd/0x4a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
</TASK>
The unconditional frag read came in with 81c6aea5275ea ("ceph: handle
frag mismatch between readdir request and reply"), so the bug is old.
Treat a reply with no dirfrag as a protocol violation: warn once,
drop the request and return -EIO, the same way ceph_readdir() already
handles the two malformed-reply cases later in the function.
Tested on v7.3.0-rc4-00537-ga3ff15db6820 (KASAN). Unpatched, the
getdents64(2) above takes the machine down (oops=panic) on the first
READDIR reply. With the patch it warns once and getdents64(2)
returns -EIO, no oops, no hang. A conformant reply for an empty
directory — dirfrag present, zero entries, FRAG_END|FRAG_COMPLETE —
still lists "." and ".." and returns EOF on both kernels, so normal
readdir behavior is unchanged. The oops is still present in
v7.3-rc5 (72d3fcf802c45).
Fixes: 81c6aea5275ea ("ceph: handle frag mismatch between readdir request and reply")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yuanfu Xie <yuanfuxie@xxxxxxxxxxxxxx>
---
fs/ceph/dir.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 2e5c0ccb1b346..d7d48d3c3baf0 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -466,6 +466,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
(int)req->r_reply_info.hash_order);
rinfo = &req->r_reply_info;
+ if (WARN_ON_ONCE(!rinfo->dir_dir)) {
+ ceph_mdsc_put_request(req);
+ return -EIO;
+ }
if (le32_to_cpu(rinfo->dir_dir->frag) != frag) {
frag = le32_to_cpu(rinfo->dir_dir->frag);
if (!rinfo->hash_order) {
--
2.43.0