# DF-5407 — uplcom attach-time control-request chain stall (14 × 5 s)

**File:** `sys/bus/u4b/serial/uplcom.c:404-436` (chain), `:460-525` (request bodies)
**Severity:** Medium (crafted-USB-device local DoS of per-bus USB enumeration)
**PoC status: reproduced** — KLD-harness-with-production-code evidence class
(no QEMU USB device model can present a PL2303 VID:PID; the emulated
usb-serial is 0525:7701 and binds `uftdi`, not `uplcom`).

## What the bug is

`uplcom_attach()` runs **14 sequential synchronous EP0 control requests**
on the per-bus USB explore thread before the device finishes attaching:

| # | call | site |
|---|------|------|
| 1 | `uplcom_reset()` | uplcom.c:412 → 460-473 |
| 10 | init sequence 0x8484/0x0404/0x8383… | uplcom.c:502-511 |
| 1 | chiptype constant (`2/0x44` HX, `2/0x24`) | uplcom.c:514-519 |
| 2 | `8,0,0` and `9,0,0` | uplcom.c:521-522 |

Every request is `usbd_do_request()` → **USB_DEFAULT_TIMEOUT = 5000 ms**
(usbdi.h:87, 511-512; clamped [50,30000] ms at usb_request.c:424-430).
USB puts **no protocol bound** on how long a device may NAK a control
transfer's data/status stage — the only bound is the host timeout,
enforced at usb_request.c:700-708 while the requester sleeps
**uninterruptibly** (`lksleep(xfer, lock, 0, "WXFER", hz)`,
usb_request.c:633-636). The `||`-chain at uplcom.c:502-511 aborts on the
first *error*, but a device that **succeeds slowly** (answers each request
after just under 5 s) keeps the chain going through all 14 requests:

> **~70 s of explore-thread stall per insertion.**

That thread (`uhub_explore_sub`, usb_hub.c:530/1103 →
`usb_probe_and_attach`, usb_device.c:1316-1406, enum lock at :1329)
serially handles **all** hub events for the bus — hotplug *and* unplug
processing for every other device on the bus freezes with it. A
gadget-class device can drop off the bus and re-enumerate itself
(Facedancer-style), so the stall is repeatable to near-continuity.

This is the same defect family as DF-5359 (uipaq, 12.8 s, 64×100 ms),
DF-5367 (ufoma, ~16-18 s), DF-5382/DF-5383 (umcs, 500 ms-class chains) —
but with the **5 s default timeout**, giving the largest per-insertion
ceiling of the family. FreeBSD's uplcom has the same code shape
(upstream candidate).

## Reproduction (KLD harness, production code verbatim)

`build.sh` extracts `uplcom_reset` / `uplcom_pl2303_do` /
`uplcom_pl2303_init` **verbatim** from the guest's clean
`/usr/src/sys/bus/u4b/serial/uplcom.c` (sha256 recorded) and compiles
them into a KLD; only the transport is intercepted —
`usbd_do_request()`/`usbd_do_request_flags()` are re-targeted to an
evil-device model that answers every EP0 request *successfully* after
just-under-timeout ms of NAK-ing (USB-legal), sleeping uninterruptibly
exactly like the production WXFER wait.

```sh
# on the guest (files pushed to /root/df5407):
sh /root/df5407/build.sh     # also applies fix.diff to a scratch copy and
                             # compiles the whole patched file -Werror-clean
sh /root/df5407/run.sh       # kldload blocks ~70 s in phase A — that IS the bug
```

**Decisive output** (3 independent kldload executions, run.log/run.2.log):

```
HX5407[A]: uplcom_attach chain DONE: 67337 ms pinned, 14 sync EP0 requests
HX5407[A]: uplcom_attach chain DONE: 67320 ms pinned, 14 sync EP0 requests
HX5407[B]: uplcom_attach chain DONE:  2170 ms pinned, 14 sync EP0 requests   <- fix.diff mechanism
```

Phase A = production code (5 s default timeout): **67.3 s** pinned, exactly
the 14-request production sequence (request trace in run.log shows the
0x8484/0x0404/0x8383 dance, `2/0x44` for PL2303HX, `8`/`9`). Phase B =
same functions extracted from the **fix.diff-patched** file (250 ms bound
per request): **2.2 s** — a 31× reduction of the stall ceiling.

Side observation recorded by the harness: every request had
`wLength ≤ 1 ≤ sizeof(buf[4])` (0 overruns) — the `buf[4]` stack buffer in
`uplcom_pl2303_do` (uplcom.c:481) is NOT overflown by this path.

## Fix

`fix.diff` — bound the attach-path requests to 250 ms each
(`UPLCOM_INIT_TIMEOUT` via `usbd_do_request_flags()`), exactly the
mechanism already applied to umcs (DF-5382/5383). A healthy PL2303
answers these vendor requests in well under a millisecond; 250 ms keeps
two orders of magnitude of margin while capping the whole attach chain at
3.5 s worst-case. Validated: git-apply-able, compiles -Werror-clean as a
kmod (`fix_compile.log`), and the patched chain measures 2.2 s in phase B.
