aboutsummaryrefslogtreecommitdiffstats
path: root/tests/fuzz/fuzz_input.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-19 22:49:46 +0200
committeruser <user@clank>2026-08-19 22:49:57 +0200
commit9c06e643cf5b8fe735b8088aef9ea686087e2997 (patch)
tree6896d20a827f0e453507cb10bd95c25594ce8209 /tests/fuzz/fuzz_input.c
parentparsers: fix seven bugs found by fuzzing (diff)
downloadslosh-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.c45
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;
+}