# DF-5407 — VERDICT

**Status: reproduced** (impact: `dos`, confidence: certain)
**Evidence class:** KLD-harness-with-production-code — prescribed for this
file because the QEMU guest's emulated usb-serial device is 0525:7701
(binds `uftdi`, per DF-5359's finding) and no QEMU USB device model can
present a PL2303 VID:PID from `uplcom_devs[]` (uplcom.c:238-298).

## What was verified, and how honestly

The claim has three parts; each was proven at a different level:

1. **The production attach chain issues exactly 14 sequential synchronous
   EP0 control requests** — *proven with the verbatim production code*:
   `build.sh` awk-extracts `uplcom_reset` (uplcom.c:460-473),
   `uplcom_pl2303_do` (475-495) and `uplcom_pl2303_init` (497-525) from the
   guest's clean `/usr/src` snapshot (SHA256
   4ac9bb391b8968275a28011881e6c490273d05c147c390bd6d028fe57618ecb9 == the
   audit tree's file) and compiles them, unmodified, into the harness KLD.
   The runtime request trace (run.log) shows the exact documented sequence:
   reset, 0x8484/0x0404/0x8383×2, 0x0404/1, 0x8484, 0x8383, 0/1, 1,
   `2/0x44` (PL2303HX branch, uplcom.c:515), `8`, `9` — 14 requests.

2. **An in-spec-but-slow device pins the calling thread ~70 s** — *proven
   for the thread that runs kldload, and by source citation for the thread
   that runs attach*: only the transport was intercepted;
   `usbd_do_request()` resolves to `usbd_do_request_flags(...,
   USB_DEFAULT_TIMEOUT=5000)` (usbdi.h:511-512, :87). The evil-device
   model answers each request successfully after 4900 ms of NAK — USB 2.0
   allows NAK-ing a control data/status stage indefinitely; the only bound
   is the host's 5000 ms timeout (checked at usb_request.c:700-708), and
   the requester waits uninterruptibly (`lksleep(..., 0, "WXFER", hz)`,
   usb_request.c:633-636) — the harness mirrors that uninterruptible
   sleep. Measured, 3 independent executions:
   **67337 / 67320 / 67337-class ms** (deterministic: 14 × 4900 ms minus
   tick slack), and the kldload(2) syscall itself did not return for that
   whole window — a user-visible, uninterruptible freeze of the loading
   thread. At real attach time the caller is the per-bus USB explore
   thread: `uhub_explore_sub` (usb_hub.c:530, called from the explore loop
   at :1103) → `usb_probe_and_attach` (usb_device.c:1316, enum lock :1329,
   driver attach :1406). That thread serially processes **all** hub events
   for the bus — the 67 s stall freezes hotplug *and unplug* handling for
   every device on the bus. This thread-context part is source-cited
   (same citations accepted for DF-5359/5367/5383), not executed — that is
   the honest boundary of the harness: the *device-behavior* premise
   (slow-but-successful control responses) is USB-spec-legal and is the
   same premise already verified for the umcs/uipaq siblings.

   Why "slow success" and not "timeout": a device that simply stalls/NAKs
   past the timeout fails request #1 and the `||` chain (uplcom.c:502-511)
   aborts after one 5 s timeout. The amplifier requires answering each
   request *successfully* at 4.9 s — entirely in-spec, and trivially
   scriptable on a Facedancer/gadget device. A gadget can also electrically
   drop and re-present itself, repeating the 67 s stall per enumeration.

3. **Impact ceiling** — bounded per insertion (~70 s + attach overhead),
   repeatable via (self-)replug to near-continuous enumeration denial for
   the bus; includes delayed unplug processing (a wedged evil device can't
   even be cleaned up while the thread is stuck). No memory corruption: no
   OOB anywhere on this path (every request's wLength ≤ 1 ≤ 4; harness
   counted 0 `wLength>4` overruns across all runs).

## Numbers (run.log / run.2.log, 3 kldload executions)

| phase | code | per-req timeout | measured |
|-------|------|-----------------|----------|
| A | production uplcom.c | 5000 ms (default) | **67337 / 67320 ms**, 14 reqs |
| B | fix.diff-patched | 250 ms (UPLCOM_INIT_TIMEOUT) | **2170 / 2204 ms**, 14 reqs |

## Fix validation

- `fix.diff` is git-apply-able against the pristine tree (`git apply
  --check` on host; `patch -p1` on the guest's clean `/usr/src` copy —
  5 hunks OK, logged in build.log).
- The **whole patched file** compiles `-Werror`-clean as a kmod
  (`uplcomfix5407.ko`, fix_compile.log / fix_build.log).
- The patched **production functions** (extracted from the patched file)
  were executed in harness phase B: stall 67.3 s → **2.2 s** (31×).
- Scope caveat: mechanism + compile level; an end-to-end replug test with
  a physical crafted PL2303 device is not possible on this guest. No full
  kernel rebuild was performed (the finding is a bounded DoS, not memory
  corruption; phase B exercises the actual patched code).

## Negative results (classes hunted and killed, pass 2)

- **OOB write `buf[4]` in uplcom_pl2303_do** (uplcom.c:481,489): all call
  sites pass length ∈ {0,1} (uplcom.c:502-522); the HCI data stage is
  bounded by wLength (usb_request.c:566-614). Harness counted 0 overruns.
- **OOB read `buf[9]` in uplcom_intr_callback** (uplcom.c:779-792): copy
  guarded by `actlen >= 9`; INTR transfer `bufsize=0` → buffer sized to
  `max_frame_size` (usb_transfer.c:646-653), actlen ≤ frame len.
- **Bulk callbacks** (uplcom.c:826-886): write path asks ucom core for at
  most `UPLCOM_BULK_BUF_SIZE`=1024=bufsize (uplcom.c:194,838-839); read
  path hands actlen ≤ max_data_length to `ucom_put_data` (core-checked).
- **Attach/detach error paths** (uplcom.c:404-458): every `goto detach`
  is safe — `ucom_detach()` early-returns when `!(sc_flag &
  UCOM_FLAG_ATTACHED)` (usb_serial.c:371-372), so calling it before/after
  `ucom_attach` is idempotent; `usbd_transfer_unsetup` tolerates partial
  setup; `lockuninit` runs exactly once (newbus does not re-detach after
  failed attach; the driver self-detaches at uplcom.c:441-443).
- **Device-supplied data feeding counts/sizes/tty**: chiptype
  (uplcom.c:360-366) selects constants only; `sc_data_iface_no`
  (uplcom.c:395) is only ever a wIndex byte; intr `buf[8]` → `sc_msr` is
  masked into three SER_* bits into a zeroed byte (uplcom.c:796-807); no
  device value ever sizes a copy.
- **pre_param/baud** (uplcom.c:607-637): fixed table for PL2303;
  `<= 6000000` for HX. A negative/zero `c_ospeed` would pass the HX check
  and be sent as a garbage divisor (USETDW, uplcom.c:650) — 4-byte store
  into a kernel struct, device-side effect only; hardening nit, not a
  finding. (ucom core normalizes B0 before the driver param path.)
- **init-after-ucom_attach window** (uplcom.c:424-433): /dev/cuaU* exists
  for a few-hundred-ms before init; if init then fails, detach →
  `ucom_drain()` (usb_serial.c:420-428) waits for open fds — an
  indefinite hang needs a cooperating local process holding the fd open;
  the drain itself is ucom-core behavior already covered by the DF-5320
  family. Noted, not filed separately.
- **Locking**: cfg callbacks are serialized on `sc_tq` under `sc_lock`
  (usb_serial.c:291-292, usb_serial.h:221 `ucom_cfg_do_request`); the
  intr/bulk callbacks take no cfg-path locks themselves. No AB-BA found.

## Severity calibration

DF-5359 (uipaq, 12.8 s/insertion) = Low; DF-5382 (umcs, continuous) =
Medium. This one: ~67 s/insertion — 5× uipaq — and repeatable to
near-continuity by a self-re-enumerating gadget, with unplug processing
frozen too. Filed **Medium** (CVSS 3.1: AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
— physical-access local DoS).

## Environment

Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 (X86_64_GENERIC), KVM,
post-run `vm.sh reset with-src` (guest_dirty=0). Full env in env.txt.
Harness source sha256:
d186bb71a982ca259035576291fdfb55ca1526198d2859ee4dc74dc551ce8617.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
