fix: use correct RET_UNLINK status codes matching kernel behavior

The CMD_UNLINK handler returned -EINVAL for devid mismatch and -ENOENT
for not-found URBs, but the Linux kernel's stub_recv_cmd_unlink() returns
status 0 in both cases. Non-standard status codes could confuse the host
kernel's vhci_hcd driver. Fixes 64 fuzzer crash artifacts across
fuzz_urb_hid and fuzz_urb_cdc targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-26 00:18:47 +00:00
parent 32daaa5e2f
commit d5ddb48eff

View file

@ -576,6 +576,9 @@ pub async fn handle_urb_loop<T: AsyncReadExt + AsyncWriteExt + Unpin + Send + 's
trace!("Got USBIP_CMD_UNLINK for seqnum={unlink_seqnum:10x?}");
// Validate devid matches the imported device.
// On mismatch, respond with status 0 ("not found"), matching
// the Linux kernel's stub_recv_cmd_unlink() which returns 0
// when the target URB doesn't exist.
if header.devid != expected_devid {
warn!(
"UNLINK devid mismatch: got {:#x}, expected {:#x}",
@ -584,7 +587,7 @@ pub async fn handle_urb_loop<T: AsyncReadExt + AsyncWriteExt + Unpin + Send + 's
header.command = USBIP_RET_UNLINK.into();
let res = UsbIpResponse::UsbIpRetUnlink {
header: header.clone(),
status: (-22i32) as u32, // EINVAL
status: 0,
};
match res.to_bytes() {
Ok(bytes) => {
@ -618,10 +621,12 @@ pub async fn handle_urb_loop<T: AsyncReadExt + AsyncWriteExt + Unpin + Send + 's
// URB already completed and removed from in_flight.
// Its RET_SUBMIT is already in the channel, so our
// RET_UNLINK will follow it — safe to send now.
// Status 0 matches the Linux kernel's stub_recv_cmd_unlink()
// behavior when the target URB is not found.
trace!("UNLINK: seqnum={unlink_seqnum} not found in-flight (already completed)");
let res = UsbIpResponse::UsbIpRetUnlink {
header: header.clone(),
status: (-2i32) as u32, // ENOENT
status: 0,
};
match res.to_bytes() {
Ok(bytes) => {