etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
fuzz_urb.c (3669B)
1 /* 2 * fuzz_urb.c — USB/IP URB PDU header parsing harness (CVE-2016-3955 class). 3 * 4 * struct usbip_header is a kernel-internal type (not in userspace UAPI), so 5 * its wire layout is defined in usbip_fuzz.h. We receive it via usbip_net_recv() 6 * and byte-swap fields manually to exercise the validation paths that the 7 * kernel's vhci_rx.c / stub_rx.c would perform. 8 * 9 * CVE-2016-3955: USBIP_RET_SUBMIT with actual_length > transfer_buffer_length 10 * causes a heap overflow in usbip_recv_xbuff() (kernel module). 11 * 12 * Bug classes: transfer_buffer_length negative / INT_MIN, number_of_packets 13 * overflow, actual_length > transfer_buffer_length, ep > 15, bad command type. 14 * 15 * Input format: 16 * [command:4 BE][seqnum:4 BE][devid:4 BE][direction:4 BE][ep:4 BE] = 20 bytes 17 * [transfer_flags:4][transfer_buffer_length:4][start_frame:4] = 12 bytes 18 * [number_of_packets:4][interval:4][setup:8] = 16 bytes 19 * [... optional data payload ...] 20 */ 21 22 #include "fuzz-include/usbip_fuzz.h" 23 #include "usbip-src/libsrc/usbip_common.h" 24 #include "usbip-src/src/usbip_network.h" 25 26 #include <stdint.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <arpa/inet.h> 30 31 #define FAKE_FD 42 32 #define MAX_XFER_LEN (1 << 16) /* 64 KB cap to avoid slow-but-legal allocs */ 33 34 static int fuzz_one(const uint8_t *data, size_t size) 35 { 36 if (size < USBIP_PDU_HDR_SIZE) 37 return 0; 38 39 uint8_t buf[FUZZ_BUF_MAX]; 40 size_t n = size < FUZZ_BUF_MAX ? size : FUZZ_BUF_MAX; 41 memcpy(buf, data, n); 42 43 /* 44 * Clamp transfer_buffer_length (bytes 24-27 in the PDU) so we don't 45 * spend time on legal multi-MB allocations. Still allows testing 46 * negative values and overflow conditions. 47 */ 48 int32_t tbl_be; 49 memcpy(&tbl_be, buf + 24, 4); 50 int32_t tbl = (int32_t)ntohl((uint32_t)tbl_be); 51 if (tbl > MAX_XFER_LEN) { 52 tbl = MAX_XFER_LEN; 53 uint32_t clamped = htonl((uint32_t)tbl); 54 memcpy(buf + 24, &clamped, 4); 55 } 56 57 fuzz_reset(buf, n); 58 59 struct usbip_header hdr; 60 memset(&hdr, 0, sizeof(hdr)); 61 if (usbip_net_recv(FAKE_FD, &hdr, sizeof(hdr)) < 0) 62 return 0; 63 64 /* Byte-swap base header fields from network order */ 65 hdr.base.command = ntohl(hdr.base.command); 66 hdr.base.seqnum = ntohl(hdr.base.seqnum); 67 hdr.base.devid = ntohl(hdr.base.devid); 68 hdr.base.direction = ntohl(hdr.base.direction); 69 hdr.base.ep = ntohl(hdr.base.ep); 70 71 uint32_t cmd = hdr.base.command; 72 73 if (cmd == USBIP_CMD_SUBMIT || cmd == USBIP_RET_SUBMIT) { 74 /* 75 * For submit/return, read the payload indicated by transfer_buffer_length. 76 * This exercises the CVE-2016-3955 overflow class. 77 */ 78 int32_t xfer_len = (int32_t)ntohl( 79 (uint32_t)hdr.u.cmd_submit.transfer_buffer_length); 80 if (xfer_len > 0 && xfer_len <= MAX_XFER_LEN) { 81 void *payload = malloc((size_t)xfer_len); 82 if (payload) { 83 usbip_net_recv(FAKE_FD, payload, (size_t)xfer_len); 84 free(payload); 85 } 86 } 87 } else if (cmd == USBIP_CMD_UNLINK || cmd == USBIP_RET_UNLINK) { 88 /* Unlink just has a seqnum — already in hdr.u.cmd_unlink.seqnum */ 89 hdr.u.cmd_unlink.seqnum = ntohl(hdr.u.cmd_unlink.seqnum); 90 } 91 92 return 0; 93 } 94 95 #ifdef __AFL_FUZZ_TESTCASE_LEN 96 __AFL_FUZZ_INIT(); 97 98 int main(void) 99 { 100 __AFL_INIT(); 101 uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF; 102 while (__AFL_LOOP(10000)) { 103 fuzz_one(buf, __AFL_FUZZ_TESTCASE_LEN); 104 } 105 return 0; 106 } 107 108 #else 109 110 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 111 { 112 return fuzz_one(data, size); 113 } 114 115 #endif