Re: [PATCH v4] ceph: fix OOB read in decode_watchers() via missing bounds check
Ilya Dryomov <[email protected]>
| Newsgroups | org.kernel.vger.ceph-devel,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <CAOi1vP-14HiHikaQuiKPjaMd35ujMu+dE0+V__7Km1o=JHvcDg@mail.gmail.com> |
On Wed, Jul 8, 2026 at 7:40 AM Pavitra Jha <[email protected]> wrote: > > ceph_start_decoding() validates that struct_len bytes remain in the > buffer after the encoding header, but accepts struct_len=0 as valid: > ceph_decode_need(p, end, 0, bad) always passes. When a malicious or > compromised OSD sends an obj_list_watch_response_t reply with > struct_len=0, ceph_start_decoding() returns success with p == end, > leaving zero bytes guaranteed for subsequent reads. > > The immediately following ceph_decode_32(p) in decode_watchers() has > no preceding bounds check. With p == end this is a 4-byte read past > the validated buffer boundary. The garbage value is then passed > directly to kzalloc_objs() as the watcher count. > > The sibling function decode_watcher() already uses the safe variants > (ceph_decode_copy_safe, ceph_decode_64_safe, ceph_decode_skip_32) > after its own ceph_start_decoding() call. decode_watchers() is the > only site that uses the bare variant, confirming an oversight. > > Fix by replacing ceph_decode_32(p) with ceph_decode_32_safe(p, end, > *num_watchers, bad), consistent with the established pattern. > > KASAN report (kernel 7.0.0-rc7, QEMU/x86_64, KASLR disabled): > > [ 72.047085] ceph_oob_poc: buf=ffff8880085936c8 end=ffff8880085936ce > [ 72.048685] ceph_oob_poc: ceph_start_decoding OK: struct_v=1 > struct_len=0 p==end: 1 > [ 72.049477] ceph_oob_poc: triggering OOB read past slab boundary... > [ 72.050699] ================================================== > [ 72.051427] BUG: KASAN: slab-out-of-bounds in > ceph_oob_init+0x128/0xff0 [ceph_oob_poc] > [ 72.051427] Read of size 4 at addr ffff8880085936ce by task insmod/61 > [ 72.051427] CPU: 0 UID: 0 PID: 61 Comm: insmod Tainted: G O > [ 72.051427] 7.0.0-rc7-g9c2abf69da83-dirty #14 PREEMPT(lazy) > [ 72.051427] Call Trace: > [ 72.051427] dump_stack_lvl+0x4d/0x70 > [ 72.051427] print_report+0x170/0x4f3 > [ 72.051427] kasan_report+0xda/0x110 > [ 72.051427] kasan_check_range+0x125/0x200 > [ 72.051427] ceph_oob_init+0x128/0xff0 [ceph_oob_poc] > [ 72.051427] do_one_initcall+0x9a/0x310 > [ 72.051427] do_init_module+0x186/0x410 > [ 72.051427] load_module+0x2ba7/0x2e50 > [ 72.051427] init_module_from_file+0x15c/0x180 > [ 72.051427] idempotent_init_module+0x19f/0x430 > [ 72.051427] __x64_sys_finit_module+0x78/0xc0 > [ 72.051427] do_syscall_64+0xe2/0x570 > [ 72.051427] entry_SYSCALL_64_after_hwframe+0x77/0x7f > [ 72.051427] The buggy address belongs to the object at ffff8880085936c8 > [ 72.051427] which belongs to the cache kmalloc-8 of size 8 > [ 72.051427] The buggy address is located 0 bytes to the right of > [ 72.051427] allocated 6-byte region [ffff8880085936c8, ffff8880085936ce) > [ 72.051427] Memory state around the buggy address: > [ 72.051427] >ffff888008593680: fc fc fc fc fc fc fc fc fc 06 fc fc fc fc fc fc > [ 72.051427] ^ > [ 72.051427] ================================================== > [ 72.129720] ceph_oob_poc: num_watchers=3435973836 (OOB garbage) > > 0xCCCCCCCC (3435973836) is KASAN redzone poison, confirming the read > landed in the slab redzone immediately past the 6-byte allocation. > > Attacker model: a malicious or compromised OSD in a multi-tenant Ceph > deployment (e.g. cloud) can trigger this against any kernel client > that calls CEPH_OSD_OP_LIST_WATCHERS, without any further privileges > beyond OSD session establishment. > > Fixes: a4ed38d7a180 ("libceph: support for CEPH_OSD_OP_LIST_WATCHERS") > Cc: [email protected] > Reviewed-by: Viacheslav Dubeyko <[email protected]> > Signed-off-by: Pavitra Jha <[email protected]> > --- > v4: Rebase against current linux/master and ceph-testing/testing. > No functional changes from Slava's reviewed v3. > v3: Rename error label e_inval -> bad per Slava Dubeyko's review. > v2: Correct commit message; retracted overstated impact claims, > verified with follow-up KASAN harness. > --- > net/ceph/osd_client.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > > diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c > index 2ff00070c..2cdac81a6 100644 > --- a/net/ceph/osd_client.c > +++ b/net/ceph/osd_client.c > @@ -5030,7 +5030,7 @@ static int decode_watchers(void **p, void *end, > if (ret) > return ret; > > - *num_watchers = ceph_decode_32(p); > + ceph_decode_32_safe(p, end, *num_watchers, bad); > *watchers = kzalloc_objs(**watchers, *num_watchers, GFP_NOIO); > if (!*watchers) > return -ENOMEM; > @@ -5044,6 +5044,8 @@ static int decode_watchers(void **p, void *end, > } > > return 0; > +bad: > + return -EINVAL; > } > > /* > -- > 2.53.0 > Applied. Thanks, Ilya