media: cxd2099: possible heap overflow from a CAM length in read_data

From: Maoyi Xie

Date: Fri Jun 19 2026 - 02:15:02 EST


Hi all,

I think read_data() in drivers/media/dvb-frontends/cxd2099.c can write past
ci->rbuf when the CAM reports a large length. I would appreciate it if you
could take a look.

read_data() builds a 16-bit length from two CAM registers.

read_reg(ci, 0x0f, &msb);
read_reg(ci, 0x10, &lsb);
len = ((u16)msb << 8) | lsb;
...
if (len > ecount || len < 2) {
/* read it anyway or cxd may hang */
read_block(ci, 0x12, ci->rbuf, len);
mutex_unlock(&ci->lock);
return -EIO;
}

len comes straight from the CAM and can be up to 65535. ci->rbuf is u8
rbuf[1028], and it is followed by u8 wbuf[1028], the last member of struct
cxd. On the drain branch the code reads len bytes into ci->rbuf with no check
against the buffer size. The caller passes an ecount of at most 512
(HOST_LINK_BUF_SIZE), so any len from 1029 to 65535 takes this branch. A len
over rbuf overruns into wbuf, and a len over rbuf plus wbuf runs off the end
of struct cxd, a heap out of bounds write of up to about 63KB.

read_block() itself only chunks the transfer by the I2C limit, it never
bounds against the destination size.

The sibling frontends clamp this same pattern. s5h1420, stb0899 and tda10071
all reject or clamp the length to the size of the destination before they
copy. The cxd2099 drain branch is the one place that missed it.

The path runs only when the driver is loaded with buffermode=1, which is not
the default, so the reach is narrow. Within that mode the length comes from
the CAM in the CI slot. A malfunctioning CAM that reports a bad length
triggers this just as a malicious one would. The ca poll thread reads from it
with no extra privilege.

I checked this two ways on 7.1-rc7. On the real driver, with a mock CAM, the
drain branch passes the full 65535 length to read_block, so the unbounded
length does reach the copy. The regmap layer buffers that read, so KASAN does
not splat on the live path. I also built a small harness that models the rbuf
and wbuf tail and runs the same copy. There a length of 65535 reports a slab
out of bounds write past the tail, and a length that fits stays inside.

The fix I tried clamps len to sizeof(ci->rbuf) before the drain read. The
FIFO still gets drained so the device does not hang, and the write stays in
bounds.

Does this look like a real bug to you? If it does I am happy to send a proper
patch with a Fixes tag pointing at 2748e76ddb29 ("media: staging: cxd2099:
Activate cxd2099 buffer mode").

Kaixuan Li and I found this together.

Thanks,
Maoyi
https://maoyixie.com/