feat: add production-hardened native UdeCx transport - #9
Draft
hbashton wants to merge 240 commits into
Draft
Conversation
Establish the isolated UdeCx bus design, ViGEmBus-inspired lifecycle invariants, packed C protocol, and a platform-independent Go codec with bounds, layout, copy-ownership, malformed-input, and fuzz tests.
Add the root-enumerated KMDF/UdeCx controller skeleton, admin-only device interface, exclusive handle-scoped broker ownership, negotiation and diagnostic IOCTLs, purge/restart cleanup, INF packaging, WDK NuGet build, and isolated CI gate.
Discover the controller by device-interface GUID, open it exclusively with overlapped I/O, negotiate and validate session nonces and limits, make close/cancel handle-safe, lock IOCTL lifetimes, test MULTI_SZ parsing and IOCTL parity, and correct WDK package restore discovery.
Add all executable directories from the verified NuGet WDK payload to the subsequent build step and fail explicitly if StampInf is absent.
Add explicit per-session UdeCx device and endpoint lifecycle, strict descriptor validation, owner teardown, dynamic endpoint queues, SuperSpeed power callbacks, and queue purge ordering. Correct the WDK/UdeCx header contract and use a 64-bit MSBuild host so InfVerif loads the matching native DLL. The endpoint URB path remains deliberately unsupported until the versioned broker completion path is connected.
Use WDK-native fixed-width types in the shared kernel ABI, include the public USB capability and URB declarations, and use the public UdeCx opaque initializer typedefs. This removes CRT/kernel macro collisions and aligns the skeleton with the supported WDK surface.
Add create, destroy, dequeue, complete, and statistics operations to the Windows client; preserve direct-I/O framing for variable transfer tails; and fix Close so CancelIoEx can actually interrupt blocking dequeue calls before waiting for in-flight operations. Add ABI layout tests for device identity and counters.
Route UdeCx endpoint URBs through a single-owner user-mode broker with preallocated O(1) token slots, strict size validation, direct-I/O payloads, and isochronous packet metadata. Serialize cancellation, endpoint purge, owner cleanup, and late completion handling so requests have one completion owner across teardown races. Explicitly initialize the nonpaged broker table and avoid duplicate purge ownership.
Build immutable UDE descriptor snapshots from the same configuration encoder used by USB/IP, and route native control, interrupt, and isochronous operations through the existing device engines. Assign monotonic sequences per device endpoint so parallel overlapped dequeues cannot reorder USB traffic. Add an allocation-free kernel cancellation event ring keyed by the original URB token and a bounded host token-state table so Windows purge/cancel interrupts in-flight device work without timeouts or double completion. Carry explicit ISO transfer spans separately from packet actual lengths, preserve sparse ISO-IN layouts, expose cancellation telemetry, and test out-of-order dequeue, stale generations, rollback, pre-dispatch cancellation, and active processor cancellation.
Implement segment-safe transfer copies for contiguous and chained-MDL URBs, matching the capability the controller advertises and avoiding truncated audio/HID buffers or unsafe access across MDL boundaries. Keep devices registered until UdeCx confirms plug-out, restore the purging state on failure, and move active-device accounting into object cleanup so deletion and cleanup races cannot leak slots or double-decrement telemetry. Also initialize cancel-event records explicitly to satisfy the WDK warning-as-error release gate.
Put endpoint reset, purge, start, and device power notifications on the same per-endpoint sequence clock as URBs. Generalize the cancellation ring into a bounded notification ring, reset the native USB engine at lifecycle boundaries, and ensure lifecycle messages are never completed as transfer URBs. This prevents stale HID/audio state from crossing Windows endpoint transitions while preserving parallel dequeue with FIFO endpoint execution.
Add an explicit native-ude service mode that opens the exclusive UdeCx broker and skips USB-IP prerequisites and localhost attachment. Publish and remove API-created devices transactionally across the virtual bus and UDE child, retain failed plug-outs for retry, and route reconnect cleanup through the same owner. Preserve usbip as the default mode while the new bus is validated.
Validate ISO packet ranges against the original transfer buffer for OUT URBs instead of the intentionally empty IN payload tail. Add native processor coverage for EP0 descriptors, sparse ISO capture layouts, and zero-copy-style ISO playback completion metadata.
Stop changing the class-extension-owned endpoint queue state, expose the standard USB host-controller interface without stealing broker ownership, and serialize dynamic SET_INTERFACE transitions through the ordered device lane. ABI 1.2 uses the former reserved bytes for interface/alternate-setting identity so DualSense audio interfaces enter the same device-engine state as USB/IP. Tests cover parsing and valid/invalid native alternate-setting transitions.
Use the reference value from Microsoft's UDE host-controller sample so the standard HCD interface is buildable and remains distinguishable from the exclusive VIIPER broker handle.
Validate ABI 1.2 exactly across every broker message, parse descriptor topology with overflow-safe bounds, and register device, configuration, BOS, and localized string descriptors before virtual-device creation. Previously the descriptors were validated then discarded, so Windows could not enumerate the controller.
Follow the UdeCx and usbip-win2 lifecycle contract: block endpoint admission, cancel broker-held operations, asynchronously purge the associated WDF queue, and acknowledge purge only after its completion callback. Keep admission closed until the matching endpoint-start callback. Also resolve the WDK descriptor-length signedness diagnostic.
Route successful, failed, cancelled, and purged broker-owned URBs through one bounded controller completion DPC so every UdeCx completion occurs at DISPATCH_LEVEL and never synchronously on the endpoint callback stack. Preserve slot ownership until final completion and use a one-shot DPC only for pre-admission failures. This follows Microsoft's UDE completion contract and usbip-win2's dispatch-level behavior without adding a media queue.
Keep the standard UDE host-controller default queue alive when the exclusive VIIPER broker exits. Only owner-specific pending operations and dequeue waiters are purged, preventing broker reconnects from disrupting Windows HCD clients. Continue completion scans from the last claimed slot so high-rate endpoint traffic does not repeatedly rescan the entire preallocated table.
Build a small x64 SetupAPI helper that creates at most one exact ROOT\\VIIPER\\UDE controller, validates the USB class, rolls back a newly registered node if driver update fails, verifies started state, and removes matching nodes idempotently. Package it beside the test-signed driver for clean-machine lifecycle testing.
Match the proven Microsoft-compatible UdeCx convention by leaving GUID_DEVINTERFACE_USB_HOST_CONTROLLER unqualified. Put the reference string on VIIPER's private interface instead, so broker opens remain distinguishable and exclusive without changing the standard host-controller device path consumed by Windows.
Hold an explicit reference to each broker owner until asynchronous UdeCx child cleanup finishes. Permit a new generation to attach while the prior generation is asynchronously purging, skip purging generations during exact removal, reject duplicate descriptor roots, and preserve audio alternate settings across D0 link-power transitions while reanchoring only media clocks.
Make duplicate operation tokens, repeated or regressed endpoint sequences, reorder-window overflow, lifecycle failures, and completion-channel failures terminate the native host session instead of leaving a live kernel queue without a consumer. Clear per-session operation state during teardown so a restarted broker cannot inherit stale tokens. Add adversarial coverage and run each case repeatedly.
Enumerate both present and phantom ROOT\\VIIPER\\UDE nodes, remove exact stale phantoms before install, reject duplicate present controllers, verify the selected ViiperUde service, and roll back newly-created devnodes when post-update validation or startup fails. Status and removal now cover non-present remnants instead of hiding them.
Treat a valid completion carrying a device NTSTATUS as an accepted broker message, so ordinary stalls and device errors cannot tear down the native host. Make endpoint purge and cancellation win atomically over an in-flight user reply, accept only the expected late-abort race idempotently, and continue rejecting duplicate or unknown completions. Return a hard error if a completion cannot be staged to the DISPATCH_LEVEL DPC.
Include initguid before devpkey so DEVPKEY_Device_IsPresent is defined in the standalone helper instead of leaving an unresolved external at link time.
Run the full VIIPER Go test and vet suites for every native-bus change instead of checking only the ABI package. Execute the compiled devnode helper's read-only status path on the clean Windows runner before publishing the test-signed artifact.
Mark the generation's operations cancelled before stopping endpoint contexts, join every affected endpoint lane, and only then reset shared device state. This removes a normal-unplug race that could report a false completion failure or mutate a device while its processor was still active. Add a repeated teardown-order regression test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Validation completed offline
Live validation
Not run on the current workstation. The next step is the source-bound Windows 11 laptop/VM validation kit; this PR remains draft until those artifacts are attached.