Skip to content

test(libSceUsbd): race the cancel on an interrupt endpoint the host can read - #12448

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
simnJS:test/usbd-cancel-race-readable-endpoint
Oct 29, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
simnJS:test/usbd-cancel-race-readable-endpoint

Conversation

@simnJS

@simnJS simnJS commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

On main, the libSceUsbd device test (guest_usbd_tests device) fails with the cancel race failed on a Logitech LIGHTSPEED receiver (046D:C54D), in 10 runs out of 10. The cancel is not what fails. The race's first sceUsbdSubmitTransfer returns 0x80240001, and the test reports every failed submission as a failed race.

  • The test races on the first interrupt IN endpoint of the device. On this receiver that is 0x81, on interface 0, a mouse.
  • Windows opens mouse and keyboard collections for its exclusive use. libusb's HID backend then opens the interface without read or write access, its ReadFile is refused, and it returns LIBUSB_ERROR_IO. With LIBUSB_DEBUG=4 the log shows [hid_open] could not open HID device in R/W mode (keyboard or mouse?) - trying without, then [hid_submit_bulk_transfer] matched endpoint 81 with interface 0 and HID transfer failed: [5] Access is denied.
  • 1462:7D75 and 31E3:1402 pass because their first interrupt IN endpoint is on a vendor-defined collection.

The library answers correctly: the refused submission returns the host's I/O error and leaves nothing in flight. So the test changes, and Export.cpp does not:

  • It lists every interrupt IN endpoint and tries them in order. Each refusal must be 0x80240001, and the transfer must not be left in flight (sceUsbdCancelTransfer returns 0x80240005).
  • On the first endpoint the host accepts, it cancels the transfer once. The cancel must return 0 or 0x80240005, and the callback must report the transfer completed or cancelled. The 300-round race then runs on that endpoint, unchanged.
  • The interrupt buffer is sized from the endpoint's wMaxPacketSize, with the high-bandwidth multiplier. It was a 64-byte array, while the requested length was the raw wMaxPacketSize, which can be larger for a high-speed endpoint.
  • The checks before the race (packet sizes, alternate setting, halt, kernel driver) still use the first interrupt IN endpoint.

A debt entry records the Windows limit a title would meet: a transfer on a mouse or keyboard collection fails with 0x80240001. What the console answers for a keyboard or mouse its own kernel drives is unknown.

There is no deterministic regression test. The refusal comes from the host's HID stack, and the no-device path cannot produce it; it needs a device whose first interrupt IN endpoint is a mouse or keyboard collection, such as this receiver.

Tested

Locally: Windows 11 Pro 10.0.26200, WinLibs GCC 15.2.0 r7 (x86_64-ucrt-posix-seh), Release, BUILD_TESTING=ON, on main f3eb573e6f. The device runs below were local only; Linux ran only on GitHub's runners.

guest_usbd_tests device with APS5_USBD_DEVICE set to each device attached to this machine, with main's test and with this branch's:

Device Product string main This branch Race endpoint on this branch
046D:C54D Logitech LIGHTSPEED receiver USB Receiver 0/10, the cancel race failed 20/20 0x82, after 0x81 was refused
1462:7D75 MSI Mystic Light controller MYSTIC LIGHT 10/10 20/20 0x81
31E3:1402 Wooting 80HE Wooting 80HE 10/10 20/20 0x81
  • guest_usbd (no device): passes 10 runs out of 10 (ctest --repeat until-fail:10).
  • public_headers and public_headers_compile_*: 65/65 pass.
  • Full suites on GitHub's runners, with this branch merged into main 04fefebe90 and a fork-only Linux workflow commit: Windows through this repository's build.yml (full), 2837 of 2837 tests pass (run); Linux, Ubuntu 24.04 with lavapipe, 2721 tests and no failure (run). Those runners have no USB device, so only the no-device guest_usbd case ran there, and it passed on both.
  • python tools/check_conventions.py --base origin/main with this title and body: all checks pass.
  • python tools/pr_overlap.py --branch test/usbd-cancel-race-readable-endpoint: no conflicting lines. git merge-tree of this branch and feat(libSceUsbd): handle events with FreeBSD libusb's event lock and handler thread #9339's head 6c208252d7 is clean. fix(libSceUsbd): retain devices during active USB operations #9803 already conflicts with main in the same three files, so it could not be compared.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

…an read

The device test raced sceUsbdCancelTransfer on the first interrupt IN endpoint of the device. On Windows the first one can belong to a mouse or keyboard collection, which the system opens exclusively, so libusb's HID backend cannot read it and the submission failed, reported as a failed cancel race. The test now tries each interrupt IN endpoint, requires a refusal to be the host's I/O error with nothing in flight, and races on the first one the host accepts, with a buffer sized from that endpoint.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head 692cc3e025 and syntax-compiled GuestUsbd.cpp with GCC 12/C++20 (-fsyntax-only): it passes. The endpoint buffer now uses the decoded payload size and high-bandwidth multiplier, and the candidate loop verifies refused submits are not left in flight before racing cancellation on an accepted endpoint. The author reports repeatable red/green runs on the affected Logitech receiver and two controls. I cannot reproduce the device-specific Windows behavior without those USB devices; hosted convention/eligibility/intake checks were queued at my last refresh. I found no concrete defect in the changed test.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at exact head 692cc3e025. I reviewed the endpoint scan and cancellation flow and syntax-compiled the exact GuestUsbd.cpp test with GCC 12/C++20. Refused submissions are required to return the reported host I/O error and leave no transfer in flight; the accepted endpoint is canceled and its callback awaited before the buffer is reused, after which the existing 300-round race runs on that endpoint. The PR reports repeated device runs on the affected Logitech receiver and two controls; I could not reproduce the USB/HID behavior without those devices. Hosted Windows, conventions, eligibility, intake, and labels now pass.

@adriadam10 adriadam10 self-assigned this Oct 28, 2026
@github-actions

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 9803 core/libs/tests/GuestUsbd.cpp
docs/dev/debt/libSceUsbd.md

@github-actions github-actions Bot added the docs label Oct 28, 2026
@adriadam10
adriadam10 merged commit 8958e22 into boykopovar:main Oct 29, 2026
12 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the endpoint scan (refused submits must return 0x80240001 and leave nothing in flight, cancel on the first accepted endpoint, then the 300-round race), the buffer sizing from wMaxPacketSize with the high-bandwidth multiplier, and the debt entry; Windows CI is green and the device runs in the PR body cover the affected receiver; merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants