etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
fuzz_json_recv.c (4612B)
1 /* 2 * AFL++ / LibFuzzer harness — RECEIVE command state machine 3 * 4 * Target: callback_tty RECEIVE handler, protocol.c:307-354 5 * 6 * The existing fuzz_websocket_auth harness sends exactly one RECEIVE call. 7 * This harness sends up to 4 sequential RECEIVE calls, exercising: 8 * 9 * Phase 1 (pss->process == NULL): 10 * JSON_DATA ('{') → parse_window_size() + auth token check + spawn_process() 11 * RESIZE_TERMINAL ('1') → parse_window_size(buf+1, len-1) — no process, no-op 12 * 13 * Phase 2 (pss->process != NULL, after JSON_DATA): 14 * RESIZE_TERMINAL → parse_window_size(buf+1, len-1) + pty_resize() 15 * INPUT ('0') → pty_write() (mock returns -1 → callback returns -1) 16 * PAUSE ('2') / RESUME ('3') → pty_pause/pty_resume (mock no-ops) 17 * 18 * Key targets: 19 * - parse_window_size() with arbitrary JSON (json-c attack surface) 20 * - Integer truncation: (uint16_t)json_object_get_int() with extreme values 21 * - State machine transitions across multiple messages 22 * - Buffer management: pss->buffer malloc/free per RECEIVE, no carry-over 23 * 24 * Input format: 25 * [num_msgs:1] 1–4 messages (bottom 2 bits + 1) 26 * per message: 27 * [cmd:1] command byte ('0', '1', '2', '3', '{', or other) 28 * [len:2 BE] payload length (0 is valid but produces len=1 buffer) 29 * [payload...] raw bytes 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <stdint.h> 36 #include <stdbool.h> 37 #include <json-c/json.h> 38 39 #include "fuzz_lws_mock.h" 40 #include "ttyd-src/src/protocol.c" 41 42 extern struct server _fuzz_server; 43 extern void fuzz_server_reset(void); 44 45 #define MAX_MSGS 4 46 47 int fuzz_target(const uint8_t *data, size_t size) { 48 if (size < 4) return 0; 49 50 int num_msgs = (data[0] & 0x03) + 1; 51 size_t off = 1; 52 53 struct { 54 uint8_t cmd; 55 const uint8_t *payload; 56 size_t len; 57 } msgs[MAX_MSGS]; 58 int parsed = 0; 59 60 for (int i = 0; i < num_msgs && off + 3 <= size; i++) { 61 uint8_t cmd = data[off++]; 62 uint16_t plen = (uint16_t)((data[off] << 8) | data[off + 1]); 63 off += 2; 64 if (plen > size - off) plen = (uint16_t)(size - off); 65 msgs[parsed].cmd = cmd; 66 msgs[parsed].payload = data + off; 67 msgs[parsed].len = plen; 68 off += plen; 69 parsed++; 70 } 71 72 if (parsed == 0) return 0; 73 74 fuzz_lws_reset(); 75 fuzz_server_reset(); 76 _fuzz_server.writable = true; 77 _fuzz_server.command = "bash"; 78 _fuzz_server.prefs_json = "{}"; 79 strncpy(_fuzz_server.terminal_type, "xterm-256color", 80 sizeof(_fuzz_server.terminal_type) - 1); 81 fuzz_lws_set_uri_path("/ws", 3); 82 83 struct lws mock_wsi = {0}; 84 struct pss_tty pss; 85 memset(&pss, 0, sizeof(pss)); 86 87 if (callback_tty(&mock_wsi, LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, 88 &pss, NULL, 0) != 0) 89 goto cleanup; 90 91 if (callback_tty(&mock_wsi, LWS_CALLBACK_ESTABLISHED, 92 &pss, NULL, 0) != 0) 93 goto cleanup; 94 95 for (int i = 0; i < 3; i++) 96 callback_tty(&mock_wsi, LWS_CALLBACK_SERVER_WRITEABLE, &pss, NULL, 0); 97 98 for (int i = 0; i < parsed; i++) { 99 /* Full RECEIVE buffer: [cmd_byte][payload...] */ 100 size_t buf_len = 1 + msgs[i].len; 101 char *buf = malloc(buf_len); 102 if (!buf) break; 103 buf[0] = (char)msgs[i].cmd; 104 if (msgs[i].len > 0) 105 memcpy(buf + 1, msgs[i].payload, msgs[i].len); 106 107 fuzz_lws_set_fragment(1, 0); 108 int ret = callback_tty(&mock_wsi, LWS_CALLBACK_RECEIVE, 109 &pss, buf, buf_len); 110 free(buf); 111 112 if (ret != 0) break; 113 } 114 115 cleanup: 116 fuzz_lws_set_fragment(1, 0); 117 callback_tty(&mock_wsi, LWS_CALLBACK_CLOSED, &pss, NULL, 0); 118 return 0; 119 } 120 121 #ifdef __AFL_FUZZ_TESTCASE_LEN 122 __AFL_FUZZ_INIT(); 123 int main(void) { 124 #ifdef __AFL_HAVE_MANUAL_CONTROL 125 __AFL_INIT(); 126 #endif 127 unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; 128 while (__AFL_LOOP(10000)) { 129 fuzz_target(buf, __AFL_FUZZ_TESTCASE_LEN); 130 } 131 return 0; 132 } 133 134 #else 135 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 136 return fuzz_target(data, size); 137 } 138 139 #ifndef LIBFUZZER 140 int main(int argc, char **argv) { 141 if (argc != 2) { fprintf(stderr, "Usage: %s <input_file>\n", argv[0]); return 1; } 142 FILE *f = fopen(argv[1], "rb"); 143 if (!f) { perror("fopen"); return 1; } 144 fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); 145 uint8_t *buf = malloc(sz); 146 fread(buf, 1, sz, f); fclose(f); 147 fuzz_target(buf, sz); 148 free(buf); 149 return 0; 150 } 151 #endif 152 #endif