aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorCédric Le Goater <clg@kaod.org>2020-09-01 14:21:50 +0200
committerCédric Le Goater <clg@kaod.org>2020-09-01 14:21:50 +0200
commitc2ab73fcbe79e0c751ab44cb6f42672f37a060e7 (patch)
tree58bb832393e4e57e5bc8d69912b1a6dd51d1f097 /hw
parentaf6d66e23557a36491a06857a447d016f6cf9f33 (diff)
downloadqemu-c2ab73fcbe79e0c751ab44cb6f42672f37a060e7.zip
qemu-c2ab73fcbe79e0c751ab44cb6f42672f37a060e7.tar.gz
qemu-c2ab73fcbe79e0c751ab44cb6f42672f37a060e7.tar.bz2
ftgmac100: Fix integer overflow in ftgmac100_do_tx()
When inserting the VLAN tag in packets, memmove() can generate an integer overflow for packets whose length is less than 12 bytes. Move the VLAN insertion when the last segment of the frame is reached and check length against the size of the ethernet header (14 bytes) to avoid the crash. Return FTGMAC100_INT_XPKT_LOST status if the frame is too small. This seems like a good modeling choice even if Aspeed does not specify anything in that case. Cc: Frederic Konrad <konrad.frederic@yahoo.fr> Cc: Mauro Matteo Cascella <mcascell@redhat.com> Reported-by: Ziming Zhang <ezrakiez@gmail.com> Message-Id: <20200819100956.2216690-15-clg@kaod.org> Signed-off-by: Cédric Le Goater <clg@kaod.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/net/ftgmac100.c55
1 files changed, 39 insertions, 16 deletions
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index 280aa3d..7c9fa72 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -481,6 +481,37 @@ static int ftgmac100_write_bd(FTGMAC100Desc *bd, dma_addr_t addr)
return 0;
}
+static int ftgmac100_insert_vlan(FTGMAC100State *s, int frame_size,
+ uint8_t vlan_tci)
+{
+ uint8_t *vlan_hdr = s->frame + (ETH_ALEN * 2);
+ uint8_t *payload = vlan_hdr + sizeof(struct vlan_header);
+
+ if (frame_size < sizeof(struct eth_header)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: frame too small for VLAN insertion : %d bytes\n",
+ __func__, frame_size);
+ s->isr |= FTGMAC100_INT_XPKT_LOST;
+ goto out;
+ }
+
+ if (frame_size + sizeof(struct vlan_header) > sizeof(s->frame)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: frame too big : %d bytes\n",
+ __func__, frame_size);
+ s->isr |= FTGMAC100_INT_XPKT_LOST;
+ frame_size -= sizeof(struct vlan_header);
+ }
+
+ memmove(payload, vlan_hdr, frame_size - (ETH_ALEN * 2));
+ stw_be_p(vlan_hdr, ETH_P_VLAN);
+ stw_be_p(vlan_hdr + 2, vlan_tci);
+ frame_size += sizeof(struct vlan_header);
+
+out:
+ return frame_size;
+}
+
static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
uint32_t tx_descriptor)
{
@@ -530,25 +561,17 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
break;
}
- /* Check for VLAN */
- if (bd.des0 & FTGMAC100_TXDES0_FTS &&
- bd.des1 & FTGMAC100_TXDES1_INS_VLANTAG &&
- be16_to_cpu(PKT_GET_ETH_HDR(ptr)->h_proto) != ETH_P_VLAN) {
- if (frame_size + len + 4 > sizeof(s->frame)) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n",
- __func__, len);
- s->isr |= FTGMAC100_INT_XPKT_LOST;
- len = sizeof(s->frame) - frame_size - 4;
- }
- memmove(ptr + 16, ptr + 12, len - 12);
- stw_be_p(ptr + 12, ETH_P_VLAN);
- stw_be_p(ptr + 14, bd.des1);
- len += 4;
- }
-
ptr += len;
frame_size += len;
if (bd.des0 & FTGMAC100_TXDES0_LTS) {
+
+ /* Check for VLAN */
+ if (flags & FTGMAC100_TXDES1_INS_VLANTAG &&
+ be16_to_cpu(PKT_GET_ETH_HDR(s->frame)->h_proto) != ETH_P_VLAN) {
+ frame_size = ftgmac100_insert_vlan(s, frame_size,
+ FTGMAC100_TXDES1_VLANTAG_CI(flags));
+ }
+
if (flags & FTGMAC100_TXDES1_IP_CHKSUM) {
net_checksum_calculate(s->frame, frame_size);
}