summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryam Vargas <hexlabsecurity@proton.me>2026-05-29 00:37:24 +0000
committerIlya Dryomov <idryomov@gmail.com>2026-07-23 20:29:41 +0200
commit4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 (patch)
treeb14e73191e97a3e1875cf3f089947df941a72bdb
parent40480eee361ed9676b3f844d532ac28b47251634 (diff)
downloadlinux-4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02.tar.gz
linux-4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02.tar.bz2
linux-4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02.zip
ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps()
ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case: snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); p = snaptrace + snaptrace_len; ... case CEPH_CAP_OP_IMPORT: if (snaptrace_len) { ... if (ceph_update_snap_trace(mdsc, snaptrace, snaptrace + snaptrace_len, false, &realm)) { ... } ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays. The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced. Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path. Cc: stable@vger.kernel.org Fixes: a8599bd821d0 ("ceph: capability management") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
-rw-r--r--fs/ceph/caps.c1
1 files changed, 1 insertions, 0 deletions
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 4b37d9ffdf7f..77b23fe51425 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -4375,6 +4375,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
snaptrace = h + 1;
snaptrace_len = le32_to_cpu(h->snap_trace_len);
+ ceph_decode_need(&snaptrace, end, snaptrace_len, bad);
p = snaptrace + snaptrace_len;
if (msg_version >= 2) {