From e5339e9df6cb0eff94afc63e2bf7787222fe14a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Wed, 25 Mar 2026 22:39:56 +0000 Subject: [PATCH] fix: reject non-zero status in OP_REQ_DEVLIST/IMPORT requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- CLAUDE.md | 8 ++++++++ lib/src/usbip_protocol.rs | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5433e14..a4b8e73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,6 +34,14 @@ nix run .#fuzz-clean-usbip -- fuzz_urb_hid # Prune fixed artifacts Crash artifacts: `lib/fuzz/artifacts//`. 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 diff --git a/lib/src/usbip_protocol.rs b/lib/src/usbip_protocol.rs index 9f2cc94..47192c3 100644 --- a/lib/src/usbip_protocol.rs +++ b/lib/src/usbip_protocol.rs @@ -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?;