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,