fix: reject non-zero status in OP_REQ_DEVLIST/IMPORT requests

Replace debug_assert!(status == 0) with proper error returns. Per the
USB/IP protocol spec, the status field in these requests is "unused,
shall be set to 0" — a non-zero value indicates a non-compliant client
and should be rejected at the parsing boundary.

Also document fuzzer crash triage guidelines in CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-25 22:39:56 +00:00
parent d97381396a
commit e5339e9df6
2 changed files with 18 additions and 2 deletions

View file

@ -34,6 +34,14 @@ nix run .#fuzz-clean-usbip -- fuzz_urb_hid # Prune fixed artifacts
Crash artifacts: `lib/fuzz/artifacts/<target>/`. Response validation is in `lib/src/fuzz_helpers.rs`.
### Fixing fuzzer crashes
1. **Priority**: Protect the host process and host kernel from untrusted client gaining code execution or privilege escalation. DoS is not a concern — the client would only be DoSing its own service.
2. **Check reachability**: Determine whether the crashing state can be reached by a normal, well-behaved client (check the Linux kernel USB/IP source at `../linux/drivers/usb/usbip/` and `../linux/tools/usb/usbip/`). If not reachable by a well-behaved client, return an error rather than continuing to process garbage.
3. **No `unsafe`** in parsing or sanitization paths.
4. **Validate at the boundary**: Check constraints immediately after deserialization, not deep in business logic.
5. **Update fuzz assertions**: Tighten the invariant assertions in `lib/src/fuzz_helpers.rs` whenever you add or change a constraint — the fuzzer can only find violations it can check.
## Key architecture
- `handle_urb_loop()` — main URB dispatch loop, generic over async transport

View file

@ -182,13 +182,21 @@ impl UsbIpCommand {
match command {
OP_REQ_DEVLIST => {
let status = socket.read_u32().await?;
debug_assert!(status == 0);
if status != 0 {
return Err(std::io::Error::other(format!(
"OP_REQ_DEVLIST: non-zero status {status:#010X}, expected 0"
)));
}
Ok(UsbIpCommand::OpReqDevlist { status })
}
OP_REQ_IMPORT => {
let status = socket.read_u32().await?;
debug_assert!(status == 0);
if status != 0 {
return Err(std::io::Error::other(format!(
"OP_REQ_IMPORT: non-zero status {status:#010X}, expected 0"
)));
}
let mut busid = [0; 32];
socket.read_exact(&mut busid).await?;