From: Feifan Qian <[email protected]> The TX Frame Control Block (FCB) is prepended to a TX frame when BD_TX_TOEUN is set. It contains two guest-controlled u8 offset fields that process_tx_fcb() uses to locate L3/L4 headers within the frame buffer:
l3_header_offset = FCB byte 3 (0..255) l4_header_offset = FCB byte 2 (0..255) These offsets are applied without any bounds check. When the UDP-no-CTU branch is taken, the function writes zero to l4_header[6] and l4_header[7]. With both offsets set to 0xFF the write target is: tx_buffer + 8 + 255 + 255 + 6/7 = tx_buffer + 525 A malicious guest can therefore corrupt up to 509 bytes of heap memory beyond a minimally-sized (16 B) TX frame. Fix: reject the frame and log a guest error when the minimum required buffer length 8 (FCB) + l3_header_offset + l4_header_offset + 8 exceeds tx_buffer_len. Move the l3_header and l4_header pointer declarations past the new guard so that out-of-bounds pointers are never materialised. Cc: [email protected] Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3517 Signed-off-by: Feifan Qian <[email protected]> Reviewed-by: Peter Maydell <[email protected]> Signed-off-by: Peter Maydell <[email protected]> (cherry picked from commit bc22c7e4187a619bf9ff7776288b588322e9ee41) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 22660c32b84..e60303f52cf 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -176,15 +176,30 @@ static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) static void process_tx_fcb(eTSEC *etsec) { uint8_t flags = (uint8_t)(*etsec->tx_buffer); - /* L3 header offset from start of frame */ + /* L3 header offset from start of frame (FCB byte 3) */ uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); - /* L4 header offset from start of L3 header */ + /* L4 header offset from start of L3 header (FCB byte 2) */ uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); + uint8_t *l3_header; + uint8_t *l4_header; + int csum = 0; + + /* + * Validate FCB header offsets before pointer arithmetic. The highest + * byte accessed is l4_header[7], at offset + * 8 (FCB size) + l3_header_offset + l4_header_offset + 7 + * from tx_buffer. Drop the frame if this exceeds the buffer length. + */ + if (etsec->tx_buffer_len < 8u + l3_header_offset + l4_header_offset + 8u) { + qemu_log_mask(LOG_GUEST_ERROR, + "eTSEC: FCB offsets exceed frame length, dropping\n"); + return; + } + /* L3 header */ - uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; + l3_header = etsec->tx_buffer + 8 + l3_header_offset; /* L4 header */ - uint8_t *l4_header = l3_header + l4_header_offset; - int csum = 0; + l4_header = l3_header + l4_header_offset; /* if packet is IP4 and IP checksum is requested */ if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { -- 2.47.3
