From 58f1e8b471d8f9d85087b8129911f9f1306ad43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Thu, 26 Mar 2026 00:08:24 +0000 Subject: [PATCH] feat(fuzz): add corpus seed generator with seeds for all targets Co-Authored-By: Claude Opus 4.6 (1M context) --- .../fuzz_handle_client/seed-devlist-only | Bin 0 -> 8 bytes .../seed-devlist-then-import | Bin 0 -> 48 bytes .../fuzz_handle_client/seed-import-enumerate | Bin 0 -> 280 bytes .../fuzz_handle_client/seed-import-hid-full | Bin 0 -> 568 bytes .../corpus/fuzz_parse_command/seed-devlist | Bin 0 -> 8 bytes .../corpus/fuzz_parse_command/seed-import | Bin 0 -> 40 bytes lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-bulk-in | Bin 0 -> 288 bytes .../corpus/fuzz_urb_cdc/seed-cdc-bulk-out | Bin 0 -> 301 bytes .../fuzz_urb_cdc/seed-cdc-class-requests | Bin 0 -> 343 bytes .../corpus/fuzz_urb_cdc/seed-cdc-interrupt-in | Bin 0 -> 288 bytes lib/fuzz/corpus/fuzz_urb_cdc/seed-enumerate | Bin 0 -> 240 bytes lib/fuzz/corpus/fuzz_urb_hid/seed-enumerate | Bin 0 -> 240 bytes lib/fuzz/corpus/fuzz_urb_hid/seed-get-status | Bin 0 -> 48 bytes .../fuzz_urb_hid/seed-hid-class-requests | Bin 0 -> 384 bytes .../corpus/fuzz_urb_hid/seed-hid-interrupt-in | Bin 0 -> 432 bytes lib/fuzz/corpus/fuzz_urb_hid/seed-unlink | Bin 0 -> 96 bytes lib/fuzz/corpus/fuzz_urb_uac/seed-enumerate | Bin 0 -> 240 bytes lib/fuzz/corpus/fuzz_urb_uac/seed-uac-iso-in | Bin 0 -> 352 bytes lib/fuzz/corpus/fuzz_urb_uac/seed-uac-iso-out | Bin 0 -> 544 bytes .../fuzz_urb_uac/seed-uac-iso-out-multi | Bin 0 -> 960 bytes .../fuzz_urb_uac/seed-uac-set-interface | Bin 0 -> 336 bytes lib/fuzz/gen_corpus.rs | 309 ++++++++++++++++++ 22 files changed, 309 insertions(+) create mode 100644 lib/fuzz/corpus/fuzz_handle_client/seed-devlist-only create mode 100644 lib/fuzz/corpus/fuzz_handle_client/seed-devlist-then-import create mode 100644 lib/fuzz/corpus/fuzz_handle_client/seed-import-enumerate create mode 100644 lib/fuzz/corpus/fuzz_handle_client/seed-import-hid-full create mode 100644 lib/fuzz/corpus/fuzz_parse_command/seed-devlist create mode 100644 lib/fuzz/corpus/fuzz_parse_command/seed-import create mode 100644 lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-bulk-in create mode 100644 lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-bulk-out create mode 100644 lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-class-requests create mode 100644 lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-interrupt-in create mode 100644 lib/fuzz/corpus/fuzz_urb_cdc/seed-enumerate create mode 100644 lib/fuzz/corpus/fuzz_urb_hid/seed-enumerate create mode 100644 lib/fuzz/corpus/fuzz_urb_hid/seed-get-status create mode 100644 lib/fuzz/corpus/fuzz_urb_hid/seed-hid-class-requests create mode 100644 lib/fuzz/corpus/fuzz_urb_hid/seed-hid-interrupt-in create mode 100644 lib/fuzz/corpus/fuzz_urb_hid/seed-unlink create mode 100644 lib/fuzz/corpus/fuzz_urb_uac/seed-enumerate create mode 100644 lib/fuzz/corpus/fuzz_urb_uac/seed-uac-iso-in create mode 100644 lib/fuzz/corpus/fuzz_urb_uac/seed-uac-iso-out create mode 100644 lib/fuzz/corpus/fuzz_urb_uac/seed-uac-iso-out-multi create mode 100644 lib/fuzz/corpus/fuzz_urb_uac/seed-uac-set-interface create mode 100644 lib/fuzz/gen_corpus.rs diff --git a/lib/fuzz/corpus/fuzz_handle_client/seed-devlist-only b/lib/fuzz/corpus/fuzz_handle_client/seed-devlist-only new file mode 100644 index 0000000000000000000000000000000000000000..d31d05c72904de985dc577e51d1299c84813ac5f GIT binary patch literal 8 PcmZP+Y+z+zU|;|M1E2ty literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_handle_client/seed-devlist-then-import b/lib/fuzz/corpus/fuzz_handle_client/seed-devlist-then-import new file mode 100644 index 0000000000000000000000000000000000000000..5b75057540cd7d50057f1c303b5b8aa8e0b3d6f4 GIT binary patch literal 48 dcmZP+Y+z+zU|?VrY+we_2D%2i1|S9w3;;~}0v7-P literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_handle_client/seed-import-enumerate b/lib/fuzz/corpus/fuzz_handle_client/seed-import-enumerate new file mode 100644 index 0000000000000000000000000000000000000000..c7894466ee43333d06e3834defe0f8b79a14cc6f GIT binary patch literal 280 zcmZP+Y+zqr>1_lNYW`fc%l@2flh;Cp5sR0YY)S{~w0;z*xhRF-cp|V&gPLMJN F1^}|&27Ukl literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_handle_client/seed-import-hid-full b/lib/fuzz/corpus/fuzz_handle_client/seed-import-hid-full new file mode 100644 index 0000000000000000000000000000000000000000..0094ccd5d1c7d4ae33ce3ab1641445efa04c2c18 GIT binary patch literal 568 zcmZP+Y+zqr>1_lNYW`fc%l@2flh;Cp5sR0YY)S{~w0;z*xhRF-cp|V&gPLMLN zIEcXpRS#l=1sNC^8ej|%-N?qE1Qi0QVTY;*Nnyo`TySBKdJd?15F3O+k{~e<4Z;Z3 Wkgx}N92P#rs0ZmMMLjpM?g0P>BnapL literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_parse_command/seed-devlist b/lib/fuzz/corpus/fuzz_parse_command/seed-devlist new file mode 100644 index 0000000000000000000000000000000000000000..d31d05c72904de985dc577e51d1299c84813ac5f GIT binary patch literal 8 PcmZP+Y+z+zU|;|M1E2ty literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_parse_command/seed-import b/lib/fuzz/corpus/fuzz_parse_command/seed-import new file mode 100644 index 0000000000000000000000000000000000000000..008e8770cbc1666d717d7a3be932964ba2c3d9c9 GIT binary patch literal 40 XcmZP+Y+zqi2F~R|W literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-bulk-in b/lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-bulk-in new file mode 100644 index 0000000000000000000000000000000000000000..ee9104b7601aed652a7a2d60e1d92a62e99e5bcb GIT binary patch literal 288 zcmZQzU|@t|CMeAS1rAUeOf|59)PaRS>OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_d| literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-interrupt-in b/lib/fuzz/corpus/fuzz_urb_cdc/seed-cdc-interrupt-in new file mode 100644 index 0000000000000000000000000000000000000000..b84548355c028da22abe58a22f1bee9617b2463e GIT binary patch literal 288 zcmZQzU|@t|CMeAS1rAUeOf|59)PaRS>OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov q6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov q6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_Cw?8g{69kQ7#|$ORV$spo*I2eDxiATb!5VIdOpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_Ll4mDTJ;*GWe?j^|=77XNGzg=c5Arxs>H!9W1$h7f literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_urb_hid/seed-unlink b/lib/fuzz/corpus/fuzz_urb_hid/seed-unlink new file mode 100644 index 0000000000000000000000000000000000000000..ad09680431a3ab318e5070af8265245e19218da8 GIT binary patch literal 96 pcmZQzU|@t|CMXT!GcYhPaDWIX=7Q273ZxE(L3|K~=>f4(FaQV703ZMW literal 0 HcmV?d00001 diff --git a/lib/fuzz/corpus/fuzz_urb_uac/seed-enumerate b/lib/fuzz/corpus/fuzz_urb_uac/seed-enumerate new file mode 100644 index 0000000000000000000000000000000000000000..0197efca4884a73cc43d999fe88d0bb5d8a85aaf GIT binary patch literal 240 zcmZQzU|@t|CMeAS1rAUeOf|59)PaRS>OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov q6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_OpEj7{mt4GcYg+!5APKq8=;+Qp3!^z<{ov z6QmA?8`wbdU?Grt7A)%j!_/seed- +fn write_seed(target: &str, name: &str, data: &[u8]) { + let dir = format!("corpus/{target}"); + fs::create_dir_all(&dir).unwrap(); + let path = format!("{dir}/seed-{name}"); + fs::write(&path, data).unwrap(); + println!(" {path} ({} bytes)", data.len()); +} + +/// OP_REQ_DEVLIST: version(2) + command(2) + status(4) = 8 bytes +fn op_req_devlist() -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&USBIP_VERSION.to_be_bytes()); + buf.extend_from_slice(&OP_REQ_DEVLIST.to_be_bytes()); + buf.extend_from_slice(&0u32.to_be_bytes()); + buf +} + +/// OP_REQ_IMPORT: version(2) + command(2) + status(4) + busid(32) = 40 bytes +fn op_req_import(busid: &str) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&USBIP_VERSION.to_be_bytes()); + buf.extend_from_slice(&OP_REQ_IMPORT.to_be_bytes()); + buf.extend_from_slice(&0u32.to_be_bytes()); + let mut busid_bytes = [0u8; 32]; + let len = busid.len().min(32); + busid_bytes[..len].copy_from_slice(&busid.as_bytes()[..len]); + buf.extend_from_slice(&busid_bytes); + buf +} + +/// USBIP_CMD_SUBMIT: 48-byte header + data (OUT only) + iso descriptors +fn cmd_submit( + seqnum: u32, + direction: u32, + ep: u32, + transfer_buffer_length: u32, + number_of_packets: u32, + interval: u32, + setup: [u8; 8], + data: &[u8], + iso_descriptors: &[u8], +) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&0u16.to_be_bytes()); // version (0 for URB phase) + buf.extend_from_slice(&USBIP_CMD_SUBMIT.to_be_bytes()); // command + buf.extend_from_slice(&seqnum.to_be_bytes()); // seqnum + buf.extend_from_slice(&2u32.to_be_bytes()); // devid + buf.extend_from_slice(&direction.to_be_bytes()); // direction + buf.extend_from_slice(&ep.to_be_bytes()); // ep + buf.extend_from_slice(&0u32.to_be_bytes()); // transfer_flags + buf.extend_from_slice(&transfer_buffer_length.to_be_bytes()); // transfer_buffer_length + buf.extend_from_slice(&0u32.to_be_bytes()); // start_frame + buf.extend_from_slice(&number_of_packets.to_be_bytes()); // number_of_packets + buf.extend_from_slice(&interval.to_be_bytes()); // interval + buf.extend_from_slice(&setup); // setup (8 bytes) + buf.extend_from_slice(data); + buf.extend_from_slice(iso_descriptors); + buf +} + +/// USBIP_CMD_UNLINK: 48 bytes total +fn cmd_unlink(seqnum: u32, unlink_seqnum: u32) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&0u16.to_be_bytes()); + buf.extend_from_slice(&USBIP_CMD_UNLINK.to_be_bytes()); + buf.extend_from_slice(&seqnum.to_be_bytes()); + buf.extend_from_slice(&2u32.to_be_bytes()); // devid + buf.extend_from_slice(&0u32.to_be_bytes()); // direction + buf.extend_from_slice(&0u32.to_be_bytes()); // ep + buf.extend_from_slice(&unlink_seqnum.to_be_bytes()); + buf.extend_from_slice(&[0u8; 24]); // padding + buf +} + +/// ISO packet descriptor: offset(4) + length(4) + actual_length(4) + status(4) = 16 bytes +fn iso_desc(offset: u32, length: u32) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&offset.to_be_bytes()); + buf.extend_from_slice(&length.to_be_bytes()); + buf.extend_from_slice(&0u32.to_be_bytes()); // actual_length (client sets to 0) + buf.extend_from_slice(&0u32.to_be_bytes()); // status (client sets to 0) + buf +} + +/// Control IN setup: GET_DESCRIPTOR +fn setup_get_desc(desc_type: u8, desc_index: u8, length: u16) -> [u8; 8] { + [ + 0x80, 0x06, + desc_index, desc_type, + 0x00, 0x00, + (length & 0xFF) as u8, (length >> 8) as u8, + ] +} + +/// Control IN setup: GET_DESCRIPTOR with interface recipient +fn setup_get_desc_iface(desc_type: u8, desc_index: u8, iface: u16, length: u16) -> [u8; 8] { + [ + 0x81, 0x06, + desc_index, desc_type, + (iface & 0xFF) as u8, (iface >> 8) as u8, + (length & 0xFF) as u8, (length >> 8) as u8, + ] +} + +/// Control OUT setup: SET_CONFIGURATION +fn setup_set_config(config: u16) -> [u8; 8] { + [ + 0x00, 0x09, + (config & 0xFF) as u8, (config >> 8) as u8, + 0x00, 0x00, + 0x00, 0x00, + ] +} + +/// Control OUT setup: SET_INTERFACE (alternate setting) +fn setup_set_interface(iface: u16, alt: u16) -> [u8; 8] { + [ + 0x01, 0x0B, + (alt & 0xFF) as u8, (alt >> 8) as u8, + (iface & 0xFF) as u8, (iface >> 8) as u8, + 0x00, 0x00, + ] +} + +/// Build a standard USB enumeration sequence (5 CMD_SUBMITs, seqnums 1-5) +fn enumeration_sequence() -> Vec { + let mut buf = Vec::new(); + buf.extend(cmd_submit(1, DIR_IN, 0, 64, 0, 0, setup_get_desc(1, 0, 64), &[], &[])); + buf.extend(cmd_submit(2, DIR_IN, 0, 18, 0, 0, setup_get_desc(1, 0, 18), &[], &[])); + buf.extend(cmd_submit(3, DIR_IN, 0, 9, 0, 0, setup_get_desc(2, 0, 9), &[], &[])); + buf.extend(cmd_submit(4, DIR_IN, 0, 255, 0, 0, setup_get_desc(2, 0, 255), &[], &[])); + buf.extend(cmd_submit(5, DIR_OUT, 0, 0, 0, 0, setup_set_config(1), &[], &[])); + buf +} + +fn gen_parse_command() { + println!("fuzz_parse_command:"); + write_seed("fuzz_parse_command", "devlist", &op_req_devlist()); + write_seed("fuzz_parse_command", "import", &op_req_import("0-0-0")); +} + +fn gen_urb_hid() { + println!("fuzz_urb_hid:"); + + // Seed 1: Standard enumeration sequence + write_seed("fuzz_urb_hid", "enumerate", &enumeration_sequence()); + + // Seed 2: Enumeration + HID-specific requests + let mut buf = enumeration_sequence(); + let seq = 6; + // GET_DESCRIPTOR(HID Report Descriptor, type=0x22, 128 bytes) on interface 0 + buf.extend(cmd_submit(seq, DIR_IN, 0, 128, 0, 0, + setup_get_desc_iface(0x22, 0, 0, 128), &[], &[])); + // SET_IDLE (class request: bmRequestType=0x21, bRequest=0x0A) + buf.extend(cmd_submit(seq + 1, DIR_OUT, 0, 0, 0, 0, + [0x21, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], &[], &[])); + // GET_REPORT (class request: bmRequestType=0xA1, bRequest=0x01, report type=Input(1)) + buf.extend(cmd_submit(seq + 2, DIR_IN, 0, 8, 0, 0, + [0xA1, 0x01, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00], &[], &[])); + write_seed("fuzz_urb_hid", "hid-class-requests", &buf); + + // Seed 3: Interrupt IN polls on ep 0x81 (keyboard reports) + let mut buf = enumeration_sequence(); + for i in 0..4u32 { + buf.extend(cmd_submit(6 + i, DIR_IN, 0x01, 8, 0, 10, [0u8; 8], &[], &[])); + } + write_seed("fuzz_urb_hid", "hid-interrupt-in", &buf); + + // Seed 4: CMD_SUBMIT followed by CMD_UNLINK + let mut buf = cmd_submit(1, DIR_IN, 0x01, 8, 0, 10, [0u8; 8], &[], &[]); + buf.extend(cmd_unlink(2, 1)); + write_seed("fuzz_urb_hid", "unlink", &buf); + + // Seed 5: Zero-length control transfer (GET_STATUS) + write_seed("fuzz_urb_hid", "get-status", + &cmd_submit(1, DIR_IN, 0, 2, 0, 0, + [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00], &[], &[])); +} + +fn gen_urb_cdc() { + println!("fuzz_urb_cdc:"); + + // Seed 1: Standard enumeration + write_seed("fuzz_urb_cdc", "enumerate", &enumeration_sequence()); + + // Seed 2: Enumeration + CDC class requests + let mut buf = enumeration_sequence(); + let seq = 6; + // SET_LINE_CODING: bmRequestType=0x21, bRequest=0x20 + // Data: 115200 baud (LE), 1 stop bit, no parity, 8 data bits + let line_coding: [u8; 7] = [0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08]; + buf.extend(cmd_submit(seq, DIR_OUT, 0, 7, 0, 0, + [0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00], &line_coding, &[])); + // SET_CONTROL_LINE_STATE: DTR + RTS active (wValue=0x0003) + buf.extend(cmd_submit(seq + 1, DIR_OUT, 0, 0, 0, 0, + [0x21, 0x22, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00], &[], &[])); + write_seed("fuzz_urb_cdc", "cdc-class-requests", &buf); + + // Seed 3: Bulk OUT data transfer on ep 0x02 + let mut buf = enumeration_sequence(); + let test_data = b"Hello, CDC!\r\n"; + buf.extend(cmd_submit(6, DIR_OUT, 0x02, test_data.len() as u32, 0, 0, + [0u8; 8], test_data, &[])); + write_seed("fuzz_urb_cdc", "cdc-bulk-out", &buf); + + // Seed 4: Bulk IN on ep 0x82 (ep=2, direction=IN) + let mut buf = enumeration_sequence(); + buf.extend(cmd_submit(6, DIR_IN, 0x02, 512, 0, 0, [0u8; 8], &[], &[])); + write_seed("fuzz_urb_cdc", "cdc-bulk-in", &buf); + + // Seed 5: Interrupt IN on ep 0x81 (notification, ep=1, direction=IN) + let mut buf = enumeration_sequence(); + buf.extend(cmd_submit(6, DIR_IN, 0x01, 8, 0, 10, [0u8; 8], &[], &[])); + write_seed("fuzz_urb_cdc", "cdc-interrupt-in", &buf); +} + +fn gen_urb_uac() { + println!("fuzz_urb_uac:"); + + // Seed 1: Standard enumeration + write_seed("fuzz_urb_uac", "enumerate", &enumeration_sequence()); + + // Seed 2: Enumeration + SET_INTERFACE to activate audio streaming + let mut buf = enumeration_sequence(); + let seq = 6; + buf.extend(cmd_submit(seq, DIR_OUT, 0, 0, 0, 0, setup_set_interface(1, 1), &[], &[])); + buf.extend(cmd_submit(seq + 1, DIR_OUT, 0, 0, 0, 0, setup_set_interface(2, 1), &[], &[])); + write_seed("fuzz_urb_uac", "uac-set-interface", &buf); + + // Seed 3: Isochronous OUT on ep 0x01 (playback, 1 frame of 48kHz 16-bit stereo = 192 bytes) + let audio_frame = vec![0x80u8; 192]; + let iso = iso_desc(0, 192); + let mut buf = enumeration_sequence(); + buf.extend(cmd_submit(6, DIR_OUT, 0, 0, 0, 0, setup_set_interface(1, 1), &[], &[])); + buf.extend(cmd_submit(7, DIR_OUT, 0x01, 192, 1, 1, [0u8; 8], &audio_frame, &iso)); + write_seed("fuzz_urb_uac", "uac-iso-out", &buf); + + // Seed 4: Isochronous IN on ep 0x82 (capture) + let iso = iso_desc(0, 192); + let mut buf = enumeration_sequence(); + buf.extend(cmd_submit(6, DIR_OUT, 0, 0, 0, 0, setup_set_interface(2, 1), &[], &[])); + buf.extend(cmd_submit(7, DIR_IN, 0x02, 192, 1, 1, [0u8; 8], &[], &iso)); + write_seed("fuzz_urb_uac", "uac-iso-in", &buf); + + // Seed 5: Multi-packet ISO OUT (3 frames) + let audio_data = vec![0x80u8; 576]; + let mut iso = Vec::new(); + iso.extend(iso_desc(0, 192)); + iso.extend(iso_desc(192, 192)); + iso.extend(iso_desc(384, 192)); + let mut buf = enumeration_sequence(); + buf.extend(cmd_submit(6, DIR_OUT, 0, 0, 0, 0, setup_set_interface(1, 1), &[], &[])); + buf.extend(cmd_submit(7, DIR_OUT, 0x01, 576, 3, 1, [0u8; 8], &audio_data, &iso)); + write_seed("fuzz_urb_uac", "uac-iso-out-multi", &buf); +} + +fn gen_handle_client() { + println!("fuzz_handle_client:"); + + // Seed 1: OP_REQ_DEVLIST only (tests early negotiation path) + write_seed("fuzz_handle_client", "devlist-only", &op_req_devlist()); + + // Seed 2: OP_REQ_DEVLIST then OP_REQ_IMPORT (two-step negotiation) + let mut buf = op_req_devlist(); + buf.extend(op_req_import("0-0-0")); + write_seed("fuzz_handle_client", "devlist-then-import", &buf); + + // Seed 3: OP_REQ_IMPORT + enumeration sequence + let mut buf = op_req_import("0-0-0"); + buf.extend(enumeration_sequence()); + write_seed("fuzz_handle_client", "import-enumerate", &buf); + + // Seed 4: Full session — import + enumerate + HID class requests + interrupt + let mut buf = op_req_import("0-0-0"); + buf.extend(enumeration_sequence()); + let seq = 6; + buf.extend(cmd_submit(seq, DIR_IN, 0, 128, 0, 0, + setup_get_desc_iface(0x22, 0, 0, 128), &[], &[])); + buf.extend(cmd_submit(seq + 1, DIR_OUT, 0, 0, 0, 0, + [0x21, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], &[], &[])); + for i in 0..4u32 { + buf.extend(cmd_submit(seq + 2 + i, DIR_IN, 0x01, 8, 0, 10, [0u8; 8], &[], &[])); + } + write_seed("fuzz_handle_client", "import-hid-full", &buf); +} + +fn main() { + println!("Generating fuzz corpus seeds...\n"); + gen_parse_command(); + gen_urb_hid(); + gen_urb_cdc(); + gen_urb_uac(); + gen_handle_client(); + println!("\nDone."); +}