diff options
| author | user <user@clank> | 2026-08-19 22:49:46 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-19 22:49:57 +0200 |
| commit | 9c06e643cf5b8fe735b8088aef9ea686087e2997 (patch) | |
| tree | 6896d20a827f0e453507cb10bd95c25594ce8209 /tests/fuzz/fuzz_input.c | |
| parent | parsers: fix seven bugs found by fuzzing (diff) | |
| download | slosh-9c06e643cf5b8fe735b8088aef9ea686087e2997.tar.gz slosh-9c06e643cf5b8fe735b8088aef9ea686087e2997.tar.xz slosh-9c06e643cf5b8fe735b8088aef9ea686087e2997.zip | |
fuzz: libFuzzer harness for all six parser surfaces
Targets: kdl, jsonval, expr, proto, osc5577, input — each compiled
standalone against its own .c with ASan + UBSan, no vendor deps.
Includes seed corpora from shipped configs/themes/chrome, real control
API lines, terminal escape sequences, and regression inputs from the
bugs already fixed. standalone_main.c replays files without the fuzzer
runtime for debugging and CI.
Top-level: make fuzz (run all, TIME=15 default), make fuzz-corpus
(replay seeds only, fast CI gate).
Diffstat (limited to 'tests/fuzz/fuzz_input.c')
| -rw-r--r-- | tests/fuzz/fuzz_input.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/tests/fuzz/fuzz_input.c b/tests/fuzz/fuzz_input.c new file mode 100644 index 0000000..3d6dde3 --- /dev/null +++ b/tests/fuzz/fuzz_input.c @@ -0,0 +1,45 @@ +/* Fuzz target: the input decoder (src/input.c). + * + * Decodes the bytes the outer terminal sends: CSI, SS3, kitty keyboard, + * SGR mouse, bracketed paste, focus reports -- and any terminal can send any + * garbage. Feed in data-chosen chunks so split escape sequences are hit, + * then resolve the pending-ESC path via input_timeout, and render every + * event with input_event_describe so the event's fields are all read. + */ +#include "input.h" + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +static void on_event(const input_event_t *ev, void *ud) { + (void)ud; + char buf[256]; + input_event_describe(ev, buf, sizeof buf); + input_event_describe(ev, buf, 4); /* tiny cap must truncate, not overrun */ + if (ev->kind == EV_PASTE && ev->paste_len) { + volatile char sink = ev->paste[0]; + sink = ev->paste[ev->paste_len - 1]; + (void)sink; + } + if (ev->kind == EV_KEY && ev->text_len) { + if (ev->text_len > sizeof ev->text) abort(); + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + input_parser_t *p = input_new(); + if (!p) return 0; + + size_t i = 0; + while (i < size) { + size_t chunk = (size_t)(data[i] % 11) + 1; /* small, split-heavy */ + if (chunk > size - i) chunk = size - i; + input_feed(p, data + i, chunk, on_event, NULL); + i += chunk; + } + + if (input_pending(p)) input_timeout(p, on_event, NULL); + input_free(p); + return 0; +} |
