usb: a device has as many interfaces as it declares
max_interfaces was 4. A composite device — a headset, a webcam with audio, a dock, a multifunction printer — routinely has more, and the fifth did not merely go missing. parseConfiguration's cap branch had no `else`, so when the count was reached `current` kept pointing at interface 3 and the fifth interface's endpoint descriptors were appended to interface 3's array. A class driver bound to interface 3 could then be handed an endpoint belonging to something else entirely, and subscribe or bulk-transfer on it. The alternate-setting arm one line above cleared `current` correctly, which is what the cap branch should have done. Interfaces are now counted from the block in a first pass and allocated to exactly that number, so the ceiling is bNumInterfaces' u8 — the USB specification's. The missing `else` is added too, though after this the bug is unreachable by construction: interface_count cannot reach interfaces.len mid-parse when the list was sized from the same walk. max_configured_endpoints was max_interfaces * max_endpoints_per_interface = 16, a derived guess that moved whenever either input moved. It is now 31, which is the xHCI specification's own limit: a Device Context holds a slot context plus at most 31 endpoint contexts, because the Context Entries field addressing them is 5 bits. max_endpoints_per_interface stays at 4 with its reason recorded — the usb-transfer wire protocol reports exactly max_reported_endpoints (4) per interface, so widening it alone would change nothing a class driver sees. Lifting it is a protocol change. No direct test, and that is written down as open question 5 rather than glossed. The parser is pure and wants a host unit test, but usb-xhci-library.zig imports memory, mmio and time so it cannot be a standalone test root, and QEMU offers nothing that reaches the path — the largest device available is usb-audio,multi=on at 2 interfaces and 211 bytes. The alternate-setting path that shares the same `current = null` logic is exercised by that device. Suite 116/116.
This commit is contained in:
@@ -16,7 +16,7 @@ next one starts.*
|
||||
| L2 | Bounds build check + allowlist; declare what we have already touched | **done** — `zig build bounds`, 273 allowlisted, 5 declared |
|
||||
| L3 | xHCI: slot count from `HCSPARAMS1.MaxSlots`, not 8 | **done** — QEMU reports 64; the driver tracked 8 |
|
||||
| L4 | USB: configuration descriptor sized by `wTotalLength`, not 512 | **done** — QEMU tops out at 211 bytes, so the case catches the class, not the original trigger |
|
||||
| L5 | USB: interfaces from the descriptor, and the misattributed-endpoint bug | not started |
|
||||
| L5 | USB: interfaces from the descriptor, and the misattributed-endpoint bug | **done** — fix is by construction; no direct test, see open question 5 |
|
||||
| L6 | xHCI: a failed `allocateDevice` stops leaking an enabled slot | not started |
|
||||
|
||||
**Suite:** 115/115 at the start of the run.
|
||||
@@ -73,6 +73,27 @@ question down instead of inventing an answer.
|
||||
answered first (tombstone-and-reuse aliases stale ids held by another process;
|
||||
generation-tagged ids change the id encoding, which is ABI). Not an unattended
|
||||
decision.
|
||||
5. **Driver descriptor parsing cannot be host-tested, so L5's correctness fix ships
|
||||
without a direct test.** The endpoint-misattribution bug lives in
|
||||
`parseConfiguration`, a pure function over a byte blob — exactly the shape a host
|
||||
unit test wants, and `usb-storage/scsi.zig` and `usb-hid/hid-report.zig` already do
|
||||
this. But `usb-xhci-library.zig` imports `memory`, `mmio` and `time`, so it cannot
|
||||
be a standalone host-test root, and QEMU offers no device that would exercise the
|
||||
path anyway: the largest available is `usb-audio,multi=on` at 2 interfaces and 211
|
||||
bytes, against a cap of 4.
|
||||
|
||||
Three ways out, and picking one is a judgement about house style rather than a
|
||||
mechanical step: extract the parser to its own file and wire `usb-abi` into a test
|
||||
module (build-support currently resolves module names only for `userBinary`);
|
||||
extract it and import `usb-abi` by relative path (against the import-by-name
|
||||
convention); or accept QEMU-only coverage and say so.
|
||||
|
||||
Mitigating, and the reason this is recorded rather than blocking: after the fix the
|
||||
bug is unreachable **by construction**, not by the added `else`. Interfaces are now
|
||||
allocated to exactly the count the descriptor declares, so `interface_count` can
|
||||
never reach `interfaces.len` mid-parse. The `else` is belt-and-braces for the
|
||||
255-interface clamp. The alternate-setting path that shares it *is* exercised —
|
||||
`usb-audio` has alternate settings, and the `usb-large-descriptor` case walks them.
|
||||
|
||||
### Working rules for the run
|
||||
|
||||
|
||||
Reference in New Issue
Block a user