etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
fuzz_ws_fragments.c (4539B)
1 /* 2 * AFL++ / LibFuzzer harness — WebSocket fragmented message accumulation 3 * 4 * Target: callback_tty RECEIVE path, protocol.c:284-293 5 * 6 * if (pss->buffer == NULL) { 7 * pss->buffer = xmalloc(len); 8 * pss->len = len; 9 * memcpy(pss->buffer, in, len); 10 * } else { 11 * pss->buffer = xrealloc(pss->buffer, pss->len + len); <-- integer overflow 12 * memcpy(pss->buffer + pss->len, in, len); 13 * pss->len += len; 14 * } 15 * 16 * The existing fuzz_websocket_auth harness never reaches the xrealloc branch: 17 * lws_is_final_fragment() and lws_remaining_packet_payload() are hardcoded to 18 * 1 and 0 respectively, so the early-return at protocol.c:303 is never taken. 19 * 20 * This harness makes both values fuzzer-controlled so AFL++ can explore: 21 * - Multi-fragment accumulation across N RECEIVE calls 22 * - Integer overflow: pss->len + len → xrealloc(buf, 0) → heap overflow 23 * - Command dispatch on the fully-assembled multi-fragment message 24 * 25 * Input format: 26 * [num_frags:1] number of fragments, 1–8 (bottom 3 bits) 27 * [cmd:1] WebSocket command byte prepended to the first fragment 28 * [payload...] raw bytes split evenly across fragments 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <stdint.h> 35 #include <stdbool.h> 36 #include <json-c/json.h> 37 38 #include "fuzz_lws_mock.h" 39 #include "ttyd-src/src/protocol.c" 40 41 extern struct server _fuzz_server; 42 extern void fuzz_server_reset(void); 43 44 int fuzz_target(const uint8_t *data, size_t size) { 45 /* need: num_frags(1) + cmd(1) + at least 1 payload byte */ 46 if (size < 3) return 0; 47 48 int num_frags = (data[0] & 0x07) + 1; /* 1–8 */ 49 uint8_t cmd = data[1]; 50 const uint8_t *payload = data + 2; 51 size_t payload_size = size - 2; 52 53 /* Build the full virtual message: [cmd][payload...] */ 54 size_t total = payload_size + 1; 55 char *msg = malloc(total); 56 if (!msg) return 0; 57 msg[0] = (char)cmd; 58 memcpy(msg + 1, payload, payload_size); 59 60 fuzz_lws_reset(); 61 fuzz_server_reset(); 62 _fuzz_server.writable = true; 63 _fuzz_server.command = "bash"; 64 _fuzz_server.prefs_json = "{}"; 65 strncpy(_fuzz_server.terminal_type, "xterm-256color", 66 sizeof(_fuzz_server.terminal_type) - 1); 67 fuzz_lws_set_uri_path("/ws", 3); 68 69 struct lws mock_wsi = {0}; 70 struct pss_tty pss; 71 memset(&pss, 0, sizeof(pss)); 72 73 if (callback_tty(&mock_wsi, LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, 74 &pss, NULL, 0) != 0) 75 goto cleanup; 76 77 if (callback_tty(&mock_wsi, LWS_CALLBACK_ESTABLISHED, 78 &pss, NULL, 0) != 0) 79 goto cleanup; 80 81 /* Burn through the two initial messages + initialization sentinel */ 82 for (int i = 0; i < 3; i++) 83 callback_tty(&mock_wsi, LWS_CALLBACK_SERVER_WRITEABLE, &pss, NULL, 0); 84 85 /* Split msg into num_frags fragments and send each as a RECEIVE call */ 86 size_t frag_size = total / (size_t)num_frags; 87 if (frag_size == 0) frag_size = 1; 88 89 size_t off = 0; 90 for (int i = 0; i < num_frags && off < total; i++) { 91 size_t this_len = (i == num_frags - 1) ? (total - off) : frag_size; 92 if (off + this_len > total) this_len = total - off; 93 94 int is_final = (i == num_frags - 1) ? 1 : 0; 95 size_t remaining = is_final ? 0 : (total - off - this_len); 96 97 fuzz_lws_set_fragment(is_final, remaining); 98 int ret = callback_tty(&mock_wsi, LWS_CALLBACK_RECEIVE, 99 &pss, msg + off, this_len); 100 if (ret != 0) break; 101 off += this_len; 102 } 103 104 cleanup: 105 free(msg); 106 fuzz_lws_set_fragment(1, 0); 107 callback_tty(&mock_wsi, LWS_CALLBACK_CLOSED, &pss, NULL, 0); 108 return 0; 109 } 110 111 #ifdef __AFL_FUZZ_TESTCASE_LEN 112 __AFL_FUZZ_INIT(); 113 int main(void) { 114 #ifdef __AFL_HAVE_MANUAL_CONTROL 115 __AFL_INIT(); 116 #endif 117 unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; 118 while (__AFL_LOOP(10000)) { 119 fuzz_target(buf, __AFL_FUZZ_TESTCASE_LEN); 120 } 121 return 0; 122 } 123 124 #else 125 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 126 return fuzz_target(data, size); 127 } 128 129 #ifndef LIBFUZZER 130 int main(int argc, char **argv) { 131 if (argc != 2) { fprintf(stderr, "Usage: %s <input_file>\n", argv[0]); return 1; } 132 FILE *f = fopen(argv[1], "rb"); 133 if (!f) { perror("fopen"); return 1; } 134 fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); 135 uint8_t *buf = malloc(sz); 136 fread(buf, 1, sz, f); fclose(f); 137 fuzz_target(buf, sz); 138 free(buf); 139 return 0; 140 } 141 #endif 142 #endif