[PATCH] media: cxd2099: bound the CAM reply length in read_data
From: Maoyi Xie
Date: Wed Jul 01 2026 - 09:17:52 EST
read_data() drains the CAM FIFO into ci->rbuf with a length the CAM
controls:
len = ((u16)msb << 8) | lsb;
if (len > ecount || len < 2) {
/* read it anyway or cxd may hang */
read_block(ci, 0x12, ci->rbuf, len);
msb and lsb come from two CAM registers, so len can be as large as 65535.
ci->rbuf is u8 rbuf[1028], and wbuf[1028] follows it as the last member of
struct cxd. read_block() advances the destination pointer for each chunk it
reads, and it never checks the buffer size. A len above 1028 runs past rbuf
into wbuf, then off the end of the struct.
The drain branch runs whenever len is larger than ecount, and the caller
caps ecount at 512. A CAM length from 1029 to 65535 writes up to about 63KB
past the buffer.
This only happens with buffermode=1, which is not the default. A faulty CAM
reaches the path the same as a malicious one.
Three sibling frontends already guard this. s5h1420, stb0899 and tda10071
bound the device length to the destination before they copy. read_data()
now clamps len to sizeof(ci->rbuf) before the drain read. The FIFO still
drains and the write stays in bounds.
Fixes: 2748e76ddb29 ("media: staging: cxd2099: Activate cxd2099 buffer mode")
Cc: stable@xxxxxxxxxxxxxxx
Co-developed-by: Kaixuan Li <kaixuan.li@xxxxxxxxxx>
Signed-off-by: Kaixuan Li <kaixuan.li@xxxxxxxxxx>
Signed-off-by: Maoyi Xie <maoyixie.tju@xxxxxxxxx>
---
I do not have the hardware. The out-of-bounds write was shown with a small
harness that models the rbuf and wbuf tail, not on a real device.
drivers/media/dvb-frontends/cxd2099.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c
index f95950a613..67b2ac4464 100644
--- a/drivers/media/dvb-frontends/cxd2099.c
+++ b/drivers/media/dvb-frontends/cxd2099.c
@@ -560,6 +560,8 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
len = ((u16)msb << 8) | lsb;
if (len > ecount || len < 2) {
/* read it anyway or cxd may hang */
+ if (len > sizeof(ci->rbuf))
+ len = sizeof(ci->rbuf);
read_block(ci, 0x12, ci->rbuf, len);
mutex_unlock(&ci->lock);
return -EIO;
--
2.34.1