etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

fuzz_vhci_server.c (30554B)

      1 /*
      2  * fuzz_vhci_server.c — Two-sided USB-IP fuzzer: malicious server targeting vhci-hcd.
      3  *
      4  * WHAT THIS TESTS (vs. fuzz_urb.c)
      5  * ─────────────────────────────────
      6  * fuzz_urb.c exercises the *userspace* copy of the URB parsing code (linked from
      7  * tools/usb/usbip/).  This binary exercises the *kernel* copy — specifically the
      8  * vhci-hcd driver's receive path (vhci_rx.c → usbip_recv_xbuff / usbip_recv_iso)
      9  * which runs without any sandbox and directly handles untrusted network data.
     10  *
     11  * CVE-2016-3955: a server sends USBIP_RET_SUBMIT with actual_length >
     12  * transfer_buffer_length → heap overflow in usbip_recv_xbuff().  The server
     13  * has full control over actual_length.  fuzz_urb.c can't trigger this in kernel
     14  * memory; this binary can (with KASAN enabled in the QEMU kernel).
     15  *
     16  * ISO OVERFLOW BUGS (usbip_recv_iso / usbip_pad_iso)
     17  * ───────────────────────────────────────────────────
     18  * The server controls urb->number_of_packets via the RET_SUBMIT response.
     19  * In usbip_recv_iso():
     20  *   int np   = urb->number_of_packets;   // ← server sets this
     21  *   int size = np * sizeof(*iso);        // integer overflow if np >= 0x08000001
     22  *   buff     = kzalloc(size, GFP_KERNEL); // undersized allocation
     23  *   usbip_net_recv(sock, buff, size);    // receives only `size` bytes
     24  *   for (i = 0; i < np; i++)            // loops np times → OOB read from buff
     25  *
     26  * ARCHITECTURE
     27  * ─────────────
     28  *   Host  : fuzz_vhci_server <testcase> <host> <port>  (AFL++ target)
     29  *   VM    : vhci-hcd loaded, usbip-attach-watchdog running (see qemu/init-vhci)
     30  *
     31  *   1. Host server listens on <host>:<port>.
     32  *   2. VM's vhci-hcd client connects (driven by usbip-attach-watchdog in /init-vhci).
     33  *   3. Server completes USB-IP handshake: read OP_REQ_IMPORT → send OP_REP_IMPORT.
     34  *   4. USB core enumerates device via EP0 control transfers (GET_DESCRIPTOR etc.).
     35  *      Server responds with a valid USB Audio Class descriptor chain so that the
     36  *      snd_usb_audio driver binds and submits ISO URBs.
     37  *   5. For non-EP0 URBs, server responds with USBIP_RET_SUBMIT packets whose
     38  *      ret_submit fields (status, actual_length, number_of_packets, etc.) are
     39  *      overlaid from the AFL++ test case bytes.  ISO descriptor data is appended.
     40  *   6. If the kernel panics (KASAN report + panic_on_oops=1), the VM reboots,
     41  *      vhci-hcd drops the connection → ECONNRESET → server exits 1 → AFL++ crash.
     42  *   7. Watchdog in VM immediately retries `usbip attach` for the next iteration.
     43  *
     44  * AFL++ INVOCATION (from run-fuzzers.sh or manually)
     45  * ────────────────────────────────────────────────────
     46  *   # in one tmux pane: start QEMU VM in vhci-client mode
     47  *   qemu-system-x86_64 -kernel bzImage -initrd initramfs-vhci.cpio.gz \
     48  *     -nographic -append "console=ttyS0 quiet" -m 512M \
     49  *     -net nic,model=e1000 -net user
     50  *
     51  *   # in another pane: run AFL++ against the server
     52  *   afl-fuzz -S vhci -i corpus/vhci -o output -t 3000 -x dictionaries/usbip.dict \
     53  *     -- ./fuzz_vhci_server @@ 0.0.0.0 13241
     54  *
     55  * COMPILE
     56  * ────────
     57  *   afl-clang-fast -O2 -o fuzz_vhci_server fuzz_vhci_server.c
     58  */
     59 
     60 #include <arpa/inet.h>
     61 #include <errno.h>
     62 #include <fcntl.h>
     63 #include <netinet/in.h>
     64 #include <netinet/tcp.h>
     65 #include <signal.h>
     66 #include <stdint.h>
     67 #include <stdio.h>
     68 #include <stdlib.h>
     69 #include <string.h>
     70 #include <sys/socket.h>
     71 #include <sys/stat.h>
     72 #include <sys/time.h>
     73 #include <time.h>
     74 #include <unistd.h>
     75 
     76 /* ── USB/IP protocol constants ─────────────────────────────────────────────── */
     77 #define USBIP_VERSION       0x0111
     78 
     79 #define OP_REQ_IMPORT       0x8003
     80 #define OP_REP_IMPORT       0x0003
     81 
     82 #define USBIP_CMD_SUBMIT    0x00000001
     83 #define USBIP_CMD_UNLINK    0x00000002
     84 #define USBIP_RET_SUBMIT    0x00000003
     85 #define USBIP_RET_UNLINK    0x00000004
     86 
     87 #define SYSFS_BUS_ID_SIZE   32
     88 #define SYSFS_PATH_MAX      256
     89 
     90 /* Maximum actual_length / ISO descriptor data we'll allocate+send (to avoid OOM) */
     91 #define MAX_ACTUAL_LEN      (1 << 16)   /* 64 KB */
     92 #define MAX_ISO_DESC_LEN    (1 << 16)   /* 64 KB — kernel won't get more */
     93 
     94 /* sizeof(struct usbip_iso_packet_descriptor) in the kernel */
     95 #define ISO_DESC_SIZE       16
     96 
     97 /*
     98  * ISO endpoint number on our virtual audio device.
     99  * EP1 IN is declared as isochronous in the config descriptor below.
    100  */
    101 #define ISO_EP_NUM          1
    102 
    103 /* ── wire-format structures (all fields in big-endian / network order) ──────── */
    104 
    105 struct op_common {
    106     uint16_t version;
    107     uint16_t code;
    108     uint32_t status;
    109 } __attribute__((packed));
    110 
    111 struct op_req_import {
    112     struct op_common hdr;
    113     char             busid[SYSFS_BUS_ID_SIZE];
    114 } __attribute__((packed));
    115 
    116 struct usbip_usb_device {
    117     char     path[SYSFS_PATH_MAX];
    118     char     busid[SYSFS_BUS_ID_SIZE];
    119     uint32_t busnum;
    120     uint32_t devnum;
    121     uint32_t speed;
    122     uint16_t idVendor;
    123     uint16_t idProduct;
    124     uint16_t bcdDevice;
    125     uint8_t  bDeviceClass;
    126     uint8_t  bDeviceSubClass;
    127     uint8_t  bDeviceProtocol;
    128     uint8_t  bConfigurationValue;
    129     uint8_t  bNumConfigurations;
    130     uint8_t  bNumInterfaces;
    131 } __attribute__((packed));
    132 
    133 struct op_rep_import {
    134     struct op_common       hdr;
    135     struct usbip_usb_device dev;
    136 } __attribute__((packed));
    137 
    138 struct usbip_header_basic {
    139     uint32_t command;
    140     uint32_t seqnum;
    141     uint32_t devid;
    142     uint32_t direction;
    143     uint32_t ep;
    144 } __attribute__((packed));
    145 
    146 struct usbip_header_cmd_submit {
    147     uint32_t transfer_flags;
    148     int32_t  transfer_buffer_length;
    149     int32_t  start_frame;
    150     int32_t  number_of_packets;
    151     int32_t  interval;
    152     uint8_t  setup[8];
    153 } __attribute__((packed));
    154 
    155 struct usbip_header_ret_submit {
    156     int32_t status;
    157     int32_t actual_length;
    158     int32_t start_frame;
    159     int32_t number_of_packets;
    160     int32_t error_count;
    161     uint8_t pad[8];
    162 } __attribute__((packed));
    163 
    164 struct usbip_header {
    165     struct usbip_header_basic base;
    166     union {
    167         struct usbip_header_cmd_submit  cmd_submit;
    168         struct usbip_header_ret_submit  ret_submit;
    169         uint8_t                         raw[28];
    170     } u;
    171 } __attribute__((packed));
    172 
    173 /* ── USB Descriptors for virtual USB Audio Class device ────────────────────────
    174  *
    175  * We present a minimal USB Audio 1.0 device with:
    176  *   Interface 0: AudioControl
    177  *   Interface 1 alt-0: AudioStreaming (zero bandwidth, 0 endpoints)
    178  *   Interface 1 alt-1: AudioStreaming (active, 1 ISO IN endpoint → EP1)
    179  *
    180  * The snd_usb_audio driver (built into typical kernels) will bind to this device
    181  * and start submitting isochronous URBs on EP1 for audio streaming.
    182  * Those ISO URBs come back to us as CMD_SUBMIT on ep==1, and we respond with
    183  * fuzz-controlled RET_SUBMIT.number_of_packets to trigger usbip_recv_iso() bugs.
    184  *
    185  * Descriptor chain layout (wTotalLength = 79):
    186  *   Config(9) + Iface0-AC(9) + AC-Header(9) +
    187  *   Iface1-alt0(9) + Iface1-alt1(9) + AS-General(7) + FormatType(11) +
    188  *   EP-ISO-IN(9) + CS-EP(7) = 79
    189  */
    190 
    191 /* Device Descriptor (18 bytes, USB 2.0, per-interface class) */
    192 static const uint8_t usb_device_desc[] = {
    193     18,         /* bLength */
    194     0x01,       /* bDescriptorType: DEVICE */
    195     0x00, 0x02, /* bcdUSB: 2.0 (little-endian) */
    196     0x00,       /* bDeviceClass: defined per-interface */
    197     0x00,       /* bDeviceSubClass */
    198     0x00,       /* bDeviceProtocol */
    199     64,         /* bMaxPacketSize0: 64 bytes for HS control */
    200     0x34, 0x12, /* idVendor: 0x1234 */
    201     0x78, 0x56, /* idProduct: 0x5678 */
    202     0x00, 0x01, /* bcdDevice: 1.0 */
    203     0,          /* iManufacturer */
    204     0,          /* iProduct */
    205     0,          /* iSerialNumber */
    206     1,          /* bNumConfigurations */
    207 };
    208 
    209 /*
    210  * Full Configuration Descriptor chain.
    211  * wTotalLength = 79 (see layout above).
    212  * bNumInterfaces = 2  (AudioControl + AudioStreaming).
    213  */
    214 static const uint8_t usb_config_desc[] = {
    215     /* ── Configuration Descriptor (9 bytes) ── */
    216     9, 0x02,        /* bLength, bDescriptorType: CONFIGURATION */
    217     79, 0,          /* wTotalLength: 79 (LE) */
    218     2,              /* bNumInterfaces */
    219     1,              /* bConfigurationValue */
    220     0,              /* iConfiguration */
    221     0x80,           /* bmAttributes: bus-powered, no remote wakeup */
    222     50,             /* bMaxPower: 100 mA */
    223 
    224     /* ── Interface 0: AudioControl, alt 0, 0 endpoints (9 bytes) ── */
    225     9, 0x04,        /* bLength, bDescriptorType: INTERFACE */
    226     0,              /* bInterfaceNumber: 0 */
    227     0,              /* bAlternateSetting: 0 */
    228     0,              /* bNumEndpoints: 0 (control-only AC interface) */
    229     0x01,           /* bInterfaceClass: Audio */
    230     0x01,           /* bInterfaceSubClass: AudioControl */
    231     0x00,           /* bInterfaceProtocol */
    232     0,              /* iInterface */
    233 
    234     /* ── AudioControl Class-Specific Interface: HEADER (9 bytes) ── */
    235     9, 0x24,        /* bLength, bDescriptorType: CS_INTERFACE */
    236     0x01,           /* bDescriptorSubType: HEADER */
    237     0x00, 0x01,     /* bcdADC: 1.0 (LE) */
    238     9, 0,           /* wTotalLength: 9 bytes AC-only (LE) */
    239     1,              /* bInCollection: 1 streaming interface */
    240     1,              /* baInterfaceNr[0]: interface 1 is the streaming interface */
    241 
    242     /* ── Interface 1: AudioStreaming, alt 0 — zero bandwidth (9 bytes) ── */
    243     9, 0x04,        /* bLength, bDescriptorType: INTERFACE */
    244     1,              /* bInterfaceNumber: 1 */
    245     0,              /* bAlternateSetting: 0 */
    246     0,              /* bNumEndpoints: 0 (idle setting) */
    247     0x01,           /* bInterfaceClass: Audio */
    248     0x02,           /* bInterfaceSubClass: AudioStreaming */
    249     0x00,           /* bInterfaceProtocol */
    250     0,              /* iInterface */
    251 
    252     /* ── Interface 1: AudioStreaming, alt 1 — active ISO streaming (9 bytes) ── */
    253     9, 0x04,        /* bLength, bDescriptorType: INTERFACE */
    254     1,              /* bInterfaceNumber: 1 */
    255     1,              /* bAlternateSetting: 1 */
    256     1,              /* bNumEndpoints: 1 (the ISO IN endpoint) */
    257     0x01,           /* bInterfaceClass: Audio */
    258     0x02,           /* bInterfaceSubClass: AudioStreaming */
    259     0x00,           /* bInterfaceProtocol */
    260     0,              /* iInterface */
    261 
    262     /* ── AudioStreaming Class-Specific Interface: AS_GENERAL (7 bytes) ── */
    263     7, 0x24,        /* bLength, bDescriptorType: CS_INTERFACE */
    264     0x01,           /* bDescriptorSubType: AS_GENERAL */
    265     1,              /* bTerminalLink: Terminal ID 1 (placeholder) */
    266     1,              /* bDelay: 1 frame pipeline delay */
    267     0x01, 0x00,     /* wFormatTag: PCM (LE) */
    268 
    269     /* ── AudioStreaming Class-Specific Interface: FORMAT_TYPE_I (11 bytes) ── */
    270     11, 0x24,       /* bLength, bDescriptorType: CS_INTERFACE */
    271     0x02,           /* bDescriptorSubType: FORMAT_TYPE */
    272     0x01,           /* bFormatType: FORMAT_TYPE_I */
    273     2,              /* bNrChannels: stereo */
    274     2,              /* bSubframeSize: 2 bytes per sample */
    275     16,             /* bBitResolution: 16 bits */
    276     1,              /* bSamFreqType: 1 discrete frequency */
    277     0x44, 0xAC, 0x00, /* tSamFreq[0]: 44100 Hz (3-byte LE: 0x00AC44) */
    278 
    279     /* ── Isochronous IN Endpoint (EP1 IN): bmAttributes=iso/async/data (9 bytes) ── */
    280     9, 0x05,        /* bLength, bDescriptorType: ENDPOINT */
    281     0x81,           /* bEndpointAddress: EP1 IN (0x80 | 1) */
    282     0x01,           /* bmAttributes: isochronous, no sync, data */
    283     196, 0,         /* wMaxPacketSize: 196 bytes (stereo 16-bit 44100Hz @ 1kHz frames, LE) */
    284     1,              /* bInterval: every 1 ms (full-speed) or 1 microframe (high-speed) */
    285     0,              /* bRefresh */
    286     0,              /* bSynchAddress */
    287 
    288     /* ── AudioStreaming Class-Specific Endpoint: EP_GENERAL (7 bytes) ── */
    289     7, 0x25,        /* bLength, bDescriptorType: CS_ENDPOINT */
    290     0x01,           /* bDescriptorSubType: EP_GENERAL */
    291     0x01,           /* bmAttributes: sampling frequency control */
    292     0,              /* bLockDelayUnits */
    293     0x00, 0x00,     /* wLockDelay: 0 */
    294 };
    295 
    296 /* Device Qualifier Descriptor (10 bytes) — required for USB 2.0 high-speed devices */
    297 static const uint8_t usb_devqual_desc[] = {
    298     10,         /* bLength */
    299     0x06,       /* bDescriptorType: DEVICE_QUALIFIER */
    300     0x00, 0x02, /* bcdUSB: 2.0 */
    301     0x00,       /* bDeviceClass */
    302     0x00,       /* bDeviceSubClass */
    303     0x00,       /* bDeviceProtocol */
    304     64,         /* bMaxPacketSize0 */
    305     1,          /* bNumConfigurations */
    306     0,          /* bReserved */
    307 };
    308 
    309 /* ── helpers ────────────────────────────────────────────────────────────────── */
    310 
    311 static int recv_all(int fd, void *buf, size_t len)
    312 {
    313     size_t done = 0;
    314     while (done < len) {
    315         ssize_t r = recv(fd, (char *)buf + done, len - done, 0);
    316         if (r <= 0)
    317             return -1;
    318         done += (size_t)r;
    319     }
    320     return 0;
    321 }
    322 
    323 static int send_all(int fd, const void *buf, size_t len)
    324 {
    325     size_t done = 0;
    326     while (done < len) {
    327         ssize_t r = send(fd, (const char *)buf + done, len - done, MSG_NOSIGNAL);
    328         if (r <= 0)
    329             return -1;
    330         done += (size_t)r;
    331     }
    332     return 0;
    333 }
    334 
    335 /* Send in small fuzz-driven chunks to exercise short-read/reassembly paths. */
    336 static int send_fragmented(int fd, const uint8_t *buf, size_t len,
    337                            const uint8_t *fuzz, size_t fuzz_len, size_t *fuzz_off)
    338 {
    339     size_t done = 0;
    340     while (done < len) {
    341         uint8_t stepb = 0;
    342         if (*fuzz_off < fuzz_len)
    343             stepb = fuzz[(*fuzz_off)++];
    344         size_t chunk = (size_t)(stepb % 31u) + 1u; /* 1..31 bytes */
    345         if (chunk > len - done)
    346             chunk = len - done;
    347         if (send_all(fd, buf + done, chunk) < 0)
    348             return -1;
    349         done += chunk;
    350     }
    351     return 0;
    352 }
    353 
    354 /* Send either as one write or fragmented based on fuzz byte. */
    355 static int send_fuzzed(int fd, const uint8_t *buf, size_t len,
    356                        const uint8_t *fuzz, size_t fuzz_len, size_t *fuzz_off)
    357 {
    358     uint8_t mode = 0;
    359     if (*fuzz_off < fuzz_len)
    360         mode = fuzz[(*fuzz_off)++];
    361     if (mode & 1)
    362         return send_fragmented(fd, buf, len, fuzz, fuzz_len, fuzz_off);
    363     return send_all(fd, buf, len);
    364 }
    365 
    366 /* Copy as many bytes as available from fuzz stream, zero-pad the rest. */
    367 static size_t fill_from_fuzz(uint8_t *dst, size_t want,
    368                              const uint8_t *fuzz, size_t fuzz_len,
    369                              size_t *fuzz_off)
    370 {
    371     size_t copied = 0;
    372     if (*fuzz_off < fuzz_len) {
    373         size_t avail = fuzz_len - *fuzz_off;
    374         copied = want < avail ? want : avail;
    375         memcpy(dst, fuzz + *fuzz_off, copied);
    376         *fuzz_off += copied;
    377     }
    378     if (copied < want)
    379         memset(dst + copied, 0x00, want - copied);
    380     return copied;
    381 }
    382 
    383 /* ── handshake: read OP_REQ_IMPORT, send OP_REP_IMPORT ─────────────────────── */
    384 
    385 static int do_handshake(int sock)
    386 {
    387     struct op_req_import req;
    388     memset(&req, 0, sizeof(req));
    389     if (recv_all(sock, &req, sizeof(req)) < 0)
    390         return -1;
    391 
    392     uint16_t code = ntohs(req.hdr.code);
    393     if (code != OP_REQ_IMPORT)
    394         return -1;
    395 
    396     /*
    397      * Report our virtual device as USB_SPEED_HIGH audio class device.
    398      * bDeviceClass=0 (per-interface) with bNumInterfaces=2 matches the
    399      * config descriptor we return for GET_DESCRIPTOR.
    400      */
    401     struct op_rep_import rep;
    402     memset(&rep, 0, sizeof(rep));
    403     rep.hdr.version = htons(USBIP_VERSION);
    404     rep.hdr.code    = htons(OP_REP_IMPORT);
    405     rep.hdr.status  = htonl(0);
    406 
    407     strncpy(rep.dev.path,  "/sys/devices/pci0000:00/0000:00:01.0/usb1/1-1", SYSFS_PATH_MAX - 1);
    408     strncpy(rep.dev.busid, "1-1", SYSFS_BUS_ID_SIZE - 1);
    409     rep.dev.busnum  = htonl(1);
    410     rep.dev.devnum  = htonl(1);
    411     rep.dev.speed   = htonl(3);       /* USB_SPEED_HIGH */
    412     rep.dev.idVendor  = htons(0x1234);
    413     rep.dev.idProduct = htons(0x5678);
    414     rep.dev.bcdDevice = htons(0x0100);
    415     rep.dev.bDeviceClass       = 0;   /* per-interface */
    416     rep.dev.bDeviceSubClass    = 0;
    417     rep.dev.bDeviceProtocol    = 0;
    418     rep.dev.bConfigurationValue = 1;
    419     rep.dev.bNumConfigurations  = 1;
    420     rep.dev.bNumInterfaces      = 2;  /* AudioControl + AudioStreaming */
    421 
    422     return send_all(sock, &rep, sizeof(rep));
    423 }
    424 
    425 /* ── EP0 control transfer handler ───────────────────────────────────────────── *
    426  *
    427  * Responds to USB control requests from the kernel USB core during enumeration.
    428  * Without valid responses here, USB enumeration fails and no driver ever binds,
    429  * so no bulk/ISO URBs are submitted and the interesting kernel paths are never hit.
    430  *
    431  * Setup packet fields:
    432  *   setup[0]: bmRequestType
    433  *   setup[1]: bRequest
    434  *   setup[2..3]: wValue  (descriptor type in high byte for GET_DESCRIPTOR)
    435  *   setup[4..5]: wIndex
    436  *   setup[6..7]: wLength (max bytes host wants back)
    437  */
    438 
    439 /* Send a RET_SUBMIT for a control transfer with an optional data payload */
    440 static int send_ctrl_response(int sock, struct usbip_header *hdr,
    441                               int32_t status,
    442                               const void *data, uint32_t data_len)
    443 {
    444     struct usbip_header resp;
    445     memset(&resp, 0, sizeof(resp));
    446     resp.base.command   = htonl(USBIP_RET_SUBMIT);
    447     resp.base.seqnum    = hdr->base.seqnum;
    448     resp.base.devid     = hdr->base.devid;
    449     resp.base.direction = hdr->base.direction;
    450     resp.base.ep        = hdr->base.ep;
    451     resp.u.ret_submit.status         = htonl((uint32_t)status);
    452     resp.u.ret_submit.actual_length  = htonl(data_len);
    453     resp.u.ret_submit.start_frame    = 0;
    454     resp.u.ret_submit.number_of_packets = 0;
    455     resp.u.ret_submit.error_count    = 0;
    456 
    457     if (send_all(sock, &resp, sizeof(resp)) < 0)
    458         return -1;
    459     if (data && data_len > 0) {
    460         if (send_all(sock, data, data_len) < 0)
    461             return -1;
    462     }
    463     return 0;
    464 }
    465 
    466 static int handle_ep0(int sock, struct usbip_header *hdr)
    467 {
    468     uint8_t *setup  = hdr->u.cmd_submit.setup;
    469     uint8_t  breq   = setup[1];                        /* bRequest */
    470     uint8_t  dtype  = setup[3];                        /* descriptor type (high byte of wValue) */
    471     uint16_t wlen   = (uint16_t)setup[6] | ((uint16_t)setup[7] << 8);
    472 
    473     /* Drain any OUT data payload the host sent (unlikely for enumeration) */
    474     int32_t tbl = ntohl(hdr->u.cmd_submit.transfer_buffer_length);
    475     if (ntohl(hdr->base.direction) == 0 /* USBIP_DIR_OUT */ && tbl > 0) {
    476         uint8_t drain[MAX_ACTUAL_LEN];
    477         int dlen = tbl < MAX_ACTUAL_LEN ? tbl : MAX_ACTUAL_LEN;
    478         recv_all(sock, drain, (size_t)dlen);
    479     }
    480 
    481     if (breq == 0x06) {
    482         /* GET_DESCRIPTOR */
    483         const uint8_t *desc     = NULL;
    484         uint32_t       desc_len = 0;
    485 
    486         switch (dtype) {
    487         case 0x01: /* DEVICE */
    488             desc     = usb_device_desc;
    489             desc_len = sizeof(usb_device_desc);
    490             break;
    491         case 0x02: /* CONFIGURATION */
    492             desc     = usb_config_desc;
    493             desc_len = sizeof(usb_config_desc);
    494             break;
    495         case 0x06: /* DEVICE QUALIFIER */
    496             desc     = usb_devqual_desc;
    497             desc_len = sizeof(usb_devqual_desc);
    498             break;
    499         default:
    500             /* String, HID, etc. — STALL (no descriptor) */
    501             return send_ctrl_response(sock, hdr, -32 /* -EPIPE */, NULL, 0);
    502         }
    503 
    504         /* Honour the host's wLength limit */
    505         if (desc_len > wlen)
    506             desc_len = wlen;
    507 
    508         return send_ctrl_response(sock, hdr, 0, desc, desc_len);
    509 
    510     } else if (breq == 0x09 || /* SET_CONFIGURATION */
    511                breq == 0x0B || /* SET_INTERFACE */
    512                breq == 0x01 || /* CLEAR_FEATURE */
    513                breq == 0x03 || /* SET_FEATURE */
    514                breq == 0x0A    /* GET_INTERFACE */) {
    515         /* Acknowledge with empty success */
    516         return send_ctrl_response(sock, hdr, 0, NULL, 0);
    517 
    518     } else {
    519         /* Unknown/unsupported request — STALL */
    520         return send_ctrl_response(sock, hdr, -32 /* -EPIPE */, NULL, 0);
    521     }
    522 }
    523 
    524 /* ── main fuzzing loop ──────────────────────────────────────────────────────── *
    525  *
    526  * Dispatch:
    527  *   EP0  → handle_ep0() with valid descriptor responses (enables enumeration)
    528  *   EP1  → fuzz RET_SUBMIT; ISO descriptor data is also sent (enables iso path)
    529  *   EPx  → fuzz RET_SUBMIT (bulk/interrupt — triggers xbuff path)
    530  */
    531 
    532 static int fuzz_loop(int sock, const uint8_t *fuzz, size_t fuzz_len)
    533 {
    534     size_t fuzz_off = 0;
    535 
    536     static uint8_t payload[MAX_ACTUAL_LEN];
    537     memset(payload, 0xcc, sizeof(payload));
    538 
    539     static uint8_t iso_buf[MAX_ISO_DESC_LEN];
    540     memset(iso_buf, 0x00, sizeof(iso_buf));
    541 
    542     for (;;) {
    543         struct usbip_header hdr;
    544         memset(&hdr, 0, sizeof(hdr));
    545         if (recv_all(sock, &hdr, sizeof(hdr)) < 0)
    546             return 0;
    547 
    548         uint32_t cmd = ntohl(hdr.base.command);
    549         uint32_t ep  = ntohl(hdr.base.ep);
    550 
    551         if (cmd == USBIP_CMD_SUBMIT) {
    552 
    553             /* ── EP0: control transfer — respond with valid USB descriptors ── */
    554             if (ep == 0) {
    555                 if (handle_ep0(sock, &hdr) < 0)
    556                     return 1;
    557                 continue;
    558             }
    559 
    560             /* ── Non-EP0: drain OUT payload ── */
    561             int32_t tbl = ntohl(hdr.u.cmd_submit.transfer_buffer_length);
    562             if (ntohl(hdr.base.direction) == 0 /* USBIP_DIR_OUT */ && tbl > 0) {
    563                 int drain_len = tbl < MAX_ACTUAL_LEN ? tbl : MAX_ACTUAL_LEN;
    564                 uint8_t drain[MAX_ACTUAL_LEN];
    565                 recv_all(sock, drain, (size_t)drain_len);
    566             }
    567 
    568             /* ── Build RET_SUBMIT with fuzz bytes overlaid on the union ── */
    569             struct usbip_header resp;
    570             memset(&resp, 0, sizeof(resp));
    571             resp.base.command   = htonl(USBIP_RET_SUBMIT);
    572             resp.base.seqnum    = hdr.base.seqnum;
    573             resp.base.devid     = hdr.base.devid;
    574             resp.base.direction = hdr.base.direction;
    575             resp.base.ep        = hdr.base.ep;
    576 
    577             /* Overlay fuzz bytes onto the 28-byte ret_submit union. */
    578             fill_from_fuzz(resp.u.raw, sizeof(resp.u.raw), fuzz, fuzz_len, &fuzz_off);
    579 
    580             /* Clamp actual_length for the host-side payload send */
    581             int32_t actual_len = ntohl(resp.u.ret_submit.actual_length);
    582             int32_t send_len   = actual_len;
    583             if (send_len < 0)       send_len = 0;
    584             if (send_len > MAX_ACTUAL_LEN) send_len = MAX_ACTUAL_LEN;
    585 
    586             /*
    587              * Fuzz response semantics to hit vhci_rx dispatch/error branches:
    588              *   mode 0: normal RET_SUBMIT (deep path)
    589              *   mode 1: wrong command RET_UNLINK for CMD_SUBMIT
    590              *   mode 2: unknown command value
    591              *   mode 3: wrong seqnum
    592              *   mode 4: truncate header and close
    593              */
    594             uint8_t dispatch_mode = 0;
    595             if (fuzz_off < fuzz_len)
    596                 dispatch_mode = (uint8_t)(fuzz[fuzz_off++] % 5u);
    597 
    598             if (dispatch_mode == 1) {
    599                 resp.base.command = htonl(USBIP_RET_UNLINK);
    600             } else if (dispatch_mode == 2) {
    601                 uint32_t bad = 0xdead0000u;
    602                 if (fuzz_off + 4 <= fuzz_len) {
    603                     memcpy(&bad, fuzz + fuzz_off, 4);
    604                     fuzz_off += 4;
    605                 }
    606                 resp.base.command = htonl(bad);
    607             } else if (dispatch_mode == 3) {
    608                 uint32_t seq = ntohl(resp.base.seqnum);
    609                 seq ^= 1u;
    610                 resp.base.seqnum = htonl(seq);
    611             } else if (dispatch_mode == 4) {
    612                 size_t trunc = 1;
    613                 if (fuzz_off < fuzz_len)
    614                     trunc = (size_t)(fuzz[fuzz_off++] % (sizeof(resp) - 1)) + 1;
    615                 if (send_fuzzed(sock, (const uint8_t *)&resp, trunc, fuzz, fuzz_len, &fuzz_off) < 0)
    616                     return 1;
    617                 return 0;
    618             }
    619 
    620             if (send_fuzzed(sock, (const uint8_t *)&resp, sizeof(resp), fuzz, fuzz_len, &fuzz_off) < 0)
    621                 return 1;
    622 
    623             /* Send xbuff (actual_length bytes) */
    624             if (send_len > 0) {
    625                 if (send_fuzzed(sock, payload, (size_t)send_len, fuzz, fuzz_len, &fuzz_off) < 0)
    626                     return 1;
    627             }
    628 
    629             /*
    630              * ── ISO descriptor data (EP1 only) ──────────────────────────────
    631              *
    632              * For ISO URBs the kernel expects, AFTER the xbuff:
    633              *   number_of_packets × sizeof(struct usbip_iso_packet_descriptor)
    634              * bytes of ISO frame descriptors.
    635              *
    636              * The overflow attack: if number_of_packets is large enough that
    637              *   np * ISO_DESC_SIZE  overflows int32 (e.g. np=0x08000001 → wraps
    638              *   to a small value), the kernel allocates a small buffer but loops
    639              *   np times → KASAN heap OOB.
    640              *
    641              * We mirror the kernel's 32-bit truncated multiplication so we send
    642              * exactly the number of bytes the (buggy) kernel will recv(), keeping
    643              * the connection alive for the subsequent OOB loop to fire.
    644              */
    645             if (ep == ISO_EP_NUM) {
    646                 int32_t np = ntohl(resp.u.ret_submit.number_of_packets);
    647                 if (np > 0) {
    648                     /*
    649                      * Replicate the kernel's 32-bit overflow:
    650                      *   uint32_t size = (uint32_t)np * ISO_DESC_SIZE;
    651                      * If np * 16 wraps to a small value, we send that many bytes
    652                      * and the kernel will allocate the same small buffer, then
    653                      * loop np times reading OOB → KASAN hit.
    654                      */
    655                     uint32_t iso_size = (uint32_t)((uint32_t)np * (uint32_t)ISO_DESC_SIZE);
    656                     uint32_t iso_send = iso_size;
    657                     if (iso_send > MAX_ISO_DESC_LEN)
    658                         iso_send = MAX_ISO_DESC_LEN;
    659                     if (iso_send > 0) {
    660                         /*
    661                          * IMPORTANT: drive ISO descriptor bytes from testcase
    662                          * data (not constant zeros). This reaches additional
    663                          * usbip_pad_iso() branches that depend on per-packet
    664                          * offset/actual_length values.
    665                          */
    666                         fill_from_fuzz(iso_buf, iso_send, fuzz, fuzz_len, &fuzz_off);
    667                         if (send_fuzzed(sock, iso_buf, (size_t)iso_send, fuzz, fuzz_len, &fuzz_off) < 0)
    668                             return 1;
    669                     }
    670                 }
    671             }
    672 
    673             if (fuzz_off >= fuzz_len)
    674                 return 0;
    675 
    676         } else if (cmd == USBIP_CMD_UNLINK) {
    677             struct usbip_header resp;
    678             memset(&resp, 0, sizeof(resp));
    679             resp.base.command = htonl(USBIP_RET_UNLINK);
    680             resp.base.seqnum  = hdr.base.seqnum;
    681             resp.base.devid   = hdr.base.devid;
    682             /* status = -ENOENT (-2), big-endian */
    683             resp.u.raw[0] = 0xff; resp.u.raw[1] = 0xff;
    684             resp.u.raw[2] = 0xff; resp.u.raw[3] = 0xfe;
    685             if (send_all(sock, &resp, sizeof(resp)) < 0)
    686                 return 1;
    687         } else {
    688             return 0;
    689         }
    690     }
    691 }
    692 
    693 /* ── entry point ─────────────────────────────────────────────────────────────── */
    694 
    695 static uint8_t fuzz_buf[1 << 16];
    696 
    697 int main(int argc, char *argv[])
    698 {
    699     if (argc < 4) {
    700         fprintf(stderr, "usage: %s <testcase> <bind-addr> <port>\n"
    701                         "  testcase  — AFL++ @@ input file\n"
    702                         "  bind-addr — address to listen on (e.g. 0.0.0.0)\n"
    703                         "  port      — TCP port (e.g. 13241)\n",
    704                 argv[0]);
    705         return 1;
    706     }
    707 
    708     const char *testcase = argv[1];
    709     const char *bindaddr = argv[2];
    710     int         port     = atoi(argv[3]);
    711 
    712     int fd = open(testcase, O_RDONLY);
    713     if (fd < 0) { perror("open"); return 1; }
    714     ssize_t fuzz_len = read(fd, fuzz_buf, sizeof(fuzz_buf));
    715     close(fd);
    716     if (fuzz_len <= 0)
    717         return 0;
    718 
    719     signal(SIGPIPE, SIG_IGN);
    720 
    721     int srv = socket(AF_INET, SOCK_STREAM, 0);
    722     if (srv < 0) { perror("socket"); return 1; }
    723 
    724     int yes = 1;
    725     setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    726     setsockopt(srv, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
    727 
    728     struct sockaddr_in sa;
    729     memset(&sa, 0, sizeof(sa));
    730     sa.sin_family = AF_INET;
    731     sa.sin_port   = htons((uint16_t)port);
    732     if (inet_pton(AF_INET, bindaddr, &sa.sin_addr) != 1) {
    733         fprintf(stderr, "bad bind address: %s\n", bindaddr);
    734         return 1;
    735     }
    736 
    737     if (bind(srv, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    738         perror("bind"); return 1;
    739     }
    740     if (listen(srv, 1) < 0) { perror("listen"); return 1; }
    741 
    742     /* Wait up to 3 seconds for the VM to connect */
    743     struct timeval tv = { .tv_sec = 3, .tv_usec = 0 };
    744     setsockopt(srv, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    745 
    746     int cli = accept(srv, NULL, NULL);
    747     close(srv);
    748     if (cli < 0)
    749         return 0;  /* timeout — not a crash */
    750 
    751     struct timeval iotv = { .tv_sec = 0, .tv_usec = 500000 };
    752     setsockopt(cli, SOL_SOCKET, SO_RCVTIMEO, &iotv, sizeof(iotv));
    753     setsockopt(cli, SOL_SOCKET, SO_SNDTIMEO, &iotv, sizeof(iotv));
    754 
    755     int one = 1;
    756     setsockopt(cli, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
    757 
    758     if (do_handshake(cli) < 0) {
    759         close(cli); return 0;
    760     }
    761 
    762     int rc = fuzz_loop(cli, fuzz_buf, (size_t)fuzz_len);
    763     close(cli);
    764     return rc;  /* 1 = ECONNRESET = kernel panic → AFL++ crash */
    765 }