npcm_gmac out-of-bounds register access
I found an oob access bug in npcm_gmac device
Version
commit: 2339d0a1
tag: v10.2.0-690-g2339d0a1cf
Description of problem
In the NPCM GMAC device model, the MMIO region is larger than the backing register array. Unhandled offsets fall through to a direct array index using offset / sizeof(uint32_t) without a bounds check, so a guest can access beyond gmac->regs[] and corrupt adjacent host-side state.
/*
hw/net/npcm_gmac.c
static uint64_t npcm_gmac_read(void *opaque, hwaddr offset, unsigned size)
static void npcm_gmac_write(void *opaque, hwaddr offset, uint64_t v, unsigned size)
*/
default:
v = gmac->regs[offset / sizeof(uint32_t)]; //<--- unchecked read
...
default:
gmac->regs[offset / sizeof(uint32_t)] = v; //<--- unchecked write
/*
include/hw/net/npcm_gmac.h
*/
#define NPCM_GMAC_NR_REGS (0x1060 / sizeof(uint32_t))
uint32_t regs[NPCM_GMAC_NR_REGS];
/*
hw/net/npcm_gmac.c
*/
memory_region_init_io(&gmac->iomem, OBJECT(gmac), &npcm_gmac_ops, gmac,
TYPE_NPCM_GMAC, 8 * KiB); //<--- larger MMIO aperture
Because NPCM_GMAC_NR_REGS covers only 0x1060 bytes while the MMIO window is 0x2000 bytes, guest-controlled offsets in the tail of the device aperture can index past the register array.
Steps to reproduce
Configuration
CFLAGS="-O0 -g -fno-omit-frame-pointer -fsanitize=undefined,bounds -fno-sanitize-recover=all" \
LDFLAGS="-fsanitize=undefined,bounds -fno-sanitize-recover=all" \
./configure --target-list=arm-softmmu --enable-debug
make -j8
Reproducer
cat << 'EOF' | ./qemu-system-arm \
-machine npcm750-evb \
-nographic \
-nodefaults \
-serial none \
-monitor none \
-accel qtest \
-qtest stdio
writel 0xf0803060 0xdeadbeef
readl 0xf0803060
EOF
Other Information
cat << 'EOF' | ./qemu-system-arm \
-machine npcm750-evb \
-nographic \
-nodefaults \
-serial none \
-monitor none \
-accel qtest \
-qtest stdio
writel 0xf0803060 0xdeadbeef
readl 0xf0803060
EOF
[I 0.000000] OPENED
[R +0.025193] writel 0xf0803060 0xdeadbeef
../hw/net/npcm_gmac.c:835:19: runtime error: index 1048 out of bounds for type 'uint32_t [1048]'