From: Pavitra Jha <jhapavitra98@gmail.com>
To: idryomov@gmail.com, amarkuze@redhat.com, slava@dubeyko.com
Cc: ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org, Pavitra Jha <jhapavitra98@gmail.com>
Subject: [PATCH v4] ceph: fix OOB read in ceph_osdc_list_watchers via uncapped outdata_len
Date: Tue, 7 Jul 2026 06:57:27 -0400 [thread overview]
Message-ID: <20260707105727.43352-1-jhapavitra98@gmail.com> (raw)
In-Reply-To: <20260702114034.917507-13-amarkuze@redhat.com>
The OSD reply header field op->payload_len is wire-controlled and is
copied directly into m->outdata_len[i] without any bounds check:
m->outdata_len[i] = le32_to_cpu(op->payload_len);
This value propagates unchecked to req->r_ops[0].outdata_len and is
then used to set the decode boundary in ceph_osdc_list_watchers():
void *const end = p + req->r_ops[0].outdata_len;
The actual data allocation is always exactly one page:
ceph_alloc_page_vector(1, GFP_NOIO)
ceph_osd_data_pages_init(..., PAGE_SIZE, ...)
The messenger caps the copy to PAGE_SIZE bytes, but the decode window
end is set from the uncapped wire value. A malicious OSD can send
outdata_len=0x10000, causing _safe decoder boundary checks to pass
while the physical reads cross the slab allocation boundary.
KASAN report (kernel 7.0.0-rc7, QEMU/x86_64, KASLR disabled):
==================================================================
BUG: KASAN: slab-out-of-bounds in ceph_oob2_init+0x23d/0xff0 [ceph_oob2_poc]
Read of size 4 at addr ffff88800a229f9e by task insmod/57
CPU: 0 UID: 0 PID: 57 Comm: insmod Tainted: G O 7.0.0-rc7-g9c2abf69da83-dirty #15 PREEMPT(lazy)
Tainted: [O]=OOT_MODULE
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x4d/0x70
print_report+0x170/0x4f3
kasan_report+0xda/0x110
ceph_oob2_init+0x23d/0xff0 [ceph_oob2_poc]
do_one_initcall+0x9a/0x3a0
do_init_module+0x27c/0x790
load_module+0x4a9a/0x6350
init_module_from_file+0x15c/0x180
idempotent_init_module+0x21f/0x750
__x64_sys_finit_module+0xba/0x120
do_syscall_64+0xe2/0x570
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Allocated by task 57:
kasan_save_stack+0x30/0x50
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x7f/0x90
ceph_oob2_init+0x44/0xff0 [ceph_oob2_poc]
do_one_initcall+0x9a/0x3a0
do_init_module+0x27c/0x790
load_module+0x4a9a/0x6350
init_module_from_file+0x15c/0x180
idempotent_init_module+0x21f/0x750
__x64_sys_finit_module+0xba/0x120
do_syscall_64+0xe2/0x570
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff88800a229000
which belongs to the cache kmalloc-4k of size 4096
The buggy address is located 3998 bytes inside of
allocated 4000-byte region [ffff88800a229000, ffff88800a229fa0)
Memory state around the buggy address:
ffff88800a229e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88800a229f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88800a229f80: 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc fc
^
ffff88800a22a000: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88800a22a080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
val=0xccccaaaa (OOB garbage from KASAN redzone)
Fix by introducing buf_len to hold the allocation size, using it in
both ceph_osd_data_pages_init() and the min_t() decode boundary cap,
so the two are guaranteed to stay in sync if the buffer size changes.
buf_len is declared as u32 to match the type of outdata_len used in
the min_t() expression.
Attacker model: a malicious or compromised OSD in a multi-tenant
Ceph deployment can trigger this against any client issuing
CEPH_OSD_OP_LIST_WATCHERS without further privileges beyond OSD
session establishment.
Fixes: a4ed38d7a180 ("libceph: support for CEPH_OSD_OP_LIST_WATCHERS")
Cc: stable@vger.kernel.org
Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
---
v4: Rebase against current linux/master and ceph-testing/testing,
confirmed clean git am -3 apply against both. No functional
changes from Slava's reviewed v3.
v3: Change buf_len type from size_t to u32 to match outdata_len type
in min_t(), per Viacheslav Dubeyko's review.
v2: Introduce buf_len variable instead of hardcoding PAGE_SIZE
independently in ceph_osd_data_pages_init() and the min_t() cap,
per Viacheslav Dubeyko's review.
---
net/ceph/osd_client.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 2ff00070c..62bec6ad8 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -5060,6 +5060,7 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc,
struct ceph_osd_request *req;
struct page **pages;
int ret;
+ const u32 buf_len = PAGE_SIZE;
req = ceph_osdc_alloc_request(osdc, NULL, 1, false, GFP_NOIO);
if (!req)
@@ -5078,7 +5079,7 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc,
osd_req_op_init(req, 0, CEPH_OSD_OP_LIST_WATCHERS, 0);
ceph_osd_data_pages_init(osd_req_op_data(req, 0, list_watchers,
response_data),
- pages, PAGE_SIZE, 0, false, true);
+ pages, buf_len, 0, false, true);
ret = ceph_osdc_alloc_messages(req, GFP_NOIO);
if (ret)
@@ -5088,7 +5089,8 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc,
ret = ceph_osdc_wait_request(osdc, req);
if (ret >= 0) {
void *p = page_address(pages[0]);
- void *const end = p + req->r_ops[0].outdata_len;
+ void *const end = p +
+ min_t(u32, req->r_ops[0].outdata_len, buf_len);
ret = decode_watchers(&p, end, watchers, num_watchers);
}
--
2.53.0
prev parent reply other threads:[~2026-07-07 10:58 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-22 18:02 [PATCH] ceph: fix OOB read in ceph_osdc_list_watchers via uncapped outdata_len Pavitra Jha
2026-05-26 18:45 ` Viacheslav Dubeyko
2026-05-28 12:29 ` [PATCH v2] " Pavitra Jha
2026-05-28 18:18 ` Viacheslav Dubeyko
2026-06-02 4:54 ` [PATCH v3] " Pavitra Jha
2026-06-02 16:53 ` Viacheslav Dubeyko
2026-06-09 5:00 ` Pavitra Jha
2026-06-11 4:14 ` Viacheslav Dubeyko
2026-07-02 11:40 ` [v3] " Alex Markuze
2026-07-07 10:57 ` Pavitra Jha [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260707105727.43352-1-jhapavitra98@gmail.com \
--to=jhapavitra98@gmail.com \
--cc=amarkuze@redhat.com \
--cc=ceph-devel@vger.kernel.org \
--cc=idryomov@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=slava@dubeyko.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.