@samitouri / QOSamiQemu / commits / bc22c7e418

hw/net/fsl_etsec: validate FCB offsets in process_tx_fcb()

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: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3517 Signed-off-by: Feifan Qian <bea1e@proton.me> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Feifan Qian committed Apr 15, 2026 at 03:27 UTC bc22c7e4187a619bf9ff7776288b588322e9ee41
1 file changed +20 -5
hw/net/fsl_etsec/rings.c
+20 -5
@@ -177,15 +177,30 @@ static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len)
177 static void process_tx_fcb(eTSEC *etsec)
178 {
179 uint8_t flags = (uint8_t)(*etsec->tx_buffer);
180 - /* L3 header offset from start of frame */
180 + /* L3 header offset from start of frame (FCB byte 3) */
181 uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3);
182 - /* L4 header offset from start of L3 header */
182 + /* L4 header offset from start of L3 header (FCB byte 2) */
183 uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2);
184 + uint8_t *l3_header;
185 + uint8_t *l4_header;
186 + int csum = 0;
187 +
188 + /*
189 + * Validate FCB header offsets before pointer arithmetic. The highest
190 + * byte accessed is l4_header[7], at offset
191 + * 8 (FCB size) + l3_header_offset + l4_header_offset + 7
192 + * from tx_buffer. Drop the frame if this exceeds the buffer length.
193 + */
194 + if (etsec->tx_buffer_len < 8u + l3_header_offset + l4_header_offset + 8u) {
195 + qemu_log_mask(LOG_GUEST_ERROR,
196 + "eTSEC: FCB offsets exceed frame length, dropping\n");
197 + return;
198 + }
199 +
200 /* L3 header */
185 - uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset;
201 + l3_header = etsec->tx_buffer + 8 + l3_header_offset;
202 /* L4 header */
187 - uint8_t *l4_header = l3_header + l4_header_offset;
188 - int csum = 0;
203 + l4_header = l3_header + l4_header_offset;
204
205 /* if packet is IP4 and IP checksum is requested */
206 if (flags & FCB_TX_IP && flags & FCB_TX_CIP) {