Commit 588fab5916 for qemu.org
commit 588fab5916091db9083036345e8fa02c0aebb778
Author: Marcelo Manzo <marcelomanzo@gmail.com>
Date: Mon Nov 9 13:52:08 2026 +0000
hw/net/bcm2838_genet: fit the RX frame within the guest's buffer
The RX path clamped the payload copied into each descriptor only
against the whole RING_BUF_SIZE register (whose upper half is the ring
size, not a buffer length) and against the size of the rx_packet
staging buffer. It did not account for the status block and 2 byte pad
that precede the payload, nor for the 4 byte CRC that is added to the
reported BUFLENGTH when CRC forwarding is enabled. The BUFLENGTH
written to the descriptor could therefore exceed the buffer the guest
supplied.
Clamp the payload to the guest's buffer size minus the status block,
pad and CRC. BUFLENGTH is then at most the buffer size, which is at
most MAX_FRAME_SIZE, so it fits its 12 bit field by construction.
(It cannot overflow today, because receive() drops frames larger than
MAX_PACKET_SIZE, but nothing in the descriptor code made that obvious.)
This also fixes a hang: with a buffer size of 0 the old code copied
zero bytes per iteration and never made progress through the frame.
Treat a buffer too small to hold any payload as a guest error and drop
the frame.
Resolves: Coverity CID 1690161
Signed-off-by: Marcelo Manzo <marcelomanzo@gmail.com>
Message-id: 20261104140058.24068-1-marcelomanzo@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/net/bcm2838_genet.c b/hw/net/bcm2838_genet.c
index 92d550cb63..0d61980a0c 100644
--- a/hw/net/bcm2838_genet.c
+++ b/hw/net/bcm2838_genet.c
@@ -902,15 +902,24 @@ static ssize_t bcm2838_genet_rdma(BCM2838GenetState *s, uint32_t ring_idx,
uint8_t *frame_buf = dma_buf + frame_off;
bool crc_fwd = FIELD_EX32(s->regs.umac.cmd, GENET_UMAC_CMD, CRC_FWD);
+ const size_t crc_len = crc_fwd ? 4 : 0;
size_t buflength;
uint32_t prod_index;
- if (l > ring->ring_buf_size) {
- l = ring->ring_buf_size;
+ /*
+ * The buffer holds the status block, the 2 byte pad, the payload
+ * and (with CRC forwarding) the CRC. buf_size is at most
+ * MAX_FRAME_SIZE, so the BUFLENGTH computed below always fits
+ * its 12 bit field.
+ */
+ if (buf_size <= frame_off + crc_len) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: RX buffer size %zu too small\n",
+ __func__, buf_size);
+ return -1;
}
- /* Never copy more than fits in rx_packet after the status block */
- if (l > sizeof(s->rx_packet) - frame_off) {
- l = sizeof(s->rx_packet) - frame_off;
+ if (l > buf_size - frame_off - crc_len) {
+ l = buf_size - frame_off - crc_len;
}
memcpy(frame_buf, buf + len, l);
@@ -921,10 +930,7 @@ static ssize_t bcm2838_genet_rdma(BCM2838GenetState *s, uint32_t ring_idx,
GENET_RDMA_LENGTH_STATUS,
EOP, !!(len >= size));
- buflength = l + sizeof(BCM2838GenetXmitStatus) + 2;
- if (crc_fwd) {
- buflength += 4;
- }
+ buflength = frame_off + l + crc_len;
desc->length_status = FIELD_DP32(desc->length_status,
GENET_RDMA_LENGTH_STATUS,