etc.git


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