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 }