fix: close connection on devid mismatch, matching kernel behavior

The kernel's valid_request() in stub_rx.c tears down the TCP connection
when devid doesn't match (SDEV_EVENT_ERROR_TCP). Previously we sent an
error response and continued, which is non-standard. Now we break out
of the loop to close the connection, matching the kernel.

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

View file

@ -326,23 +326,14 @@ pub async fn handle_urb_loop<T: AsyncReadExt + AsyncWriteExt + Unpin + Send + 's
let out = header.direction == 0;
// Validate devid matches the imported device. The Linux
// kernel performs this same check in stub_rx.c:293.
// kernel's valid_request() in stub_rx.c performs this same
// check and tears down the connection on mismatch.
if header.devid != expected_devid {
warn!(
"devid mismatch: got {:#x}, expected {:#x}",
"devid mismatch: got {:#x}, expected {:#x} — closing connection",
header.devid, expected_devid
);
header.command = USBIP_RET_SUBMIT.into();
let res = UsbIpResponse::usbip_ret_submit_fail(&header);
match res.to_bytes() {
Ok(bytes) => {
if response_tx.send(bytes).await.is_err() {
break Err(std::io::Error::other("Response channel closed"));
}
}
Err(e) => warn!("Failed to serialize response for devid mismatch: {e}"),
}
continue;
break Err(std::io::Error::other("devid mismatch"));
}
// Validate endpoint number (must be 0-15 per USB spec).
@ -575,29 +566,15 @@ 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.
// Validate devid matches the imported device. The Linux
// kernel's valid_request() in stub_rx.c performs this same
// check and tears down the connection on mismatch.
if header.devid != expected_devid {
warn!(
"UNLINK devid mismatch: got {:#x}, expected {:#x}",
"UNLINK devid mismatch: got {:#x}, expected {:#x} — closing connection",
header.devid, expected_devid
);
header.command = USBIP_RET_UNLINK.into();
let res = UsbIpResponse::UsbIpRetUnlink {
header: header.clone(),
status: 0,
};
match res.to_bytes() {
Ok(bytes) => {
if response_tx.send(bytes).await.is_err() {
break Err(std::io::Error::other("Response channel closed"));
}
}
Err(e) => warn!("Failed to serialize UNLINK devid mismatch response: {e}"),
}
continue;
break Err(std::io::Error::other("devid mismatch"));
}
header.command = USBIP_RET_UNLINK.into();