aboutsummaryrefslogtreecommitdiffstats
path: root/tests/fuzz/fuzz_expr.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_expr.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_expr.c')
-rw-r--r--tests/fuzz/fuzz_expr.c71
1 files changed, 71 insertions, 0 deletions
diff --git a/tests/fuzz/fuzz_expr.c b/tests/fuzz/fuzz_expr.c
new file mode 100644
index 0000000..3942524
--- /dev/null
+++ b/tests/fuzz/fuzz_expr.c
@@ -0,0 +1,71 @@
+/* Fuzz target: the shader expression language (src/expr.c).
+ *
+ * Compiles arbitrary text, and when a program comes back actually runs it:
+ * once per hand-picked environment (zeros, a plausible pane, and extremes)
+ * and once as an amount map over a small rect. The language is documented as
+ * total -- no way to hang, div/mod by zero is 0 -- so anything ASan/UBSan
+ * catches in eval is a real bug, including signed overflow in the arithmetic.
+ */
+#include "expr.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ char *text = malloc(size + 1);
+ if (!text) return 0;
+ memcpy(text, data, size);
+ text[size] = '\0';
+
+ char err[256];
+ expr_prog_t *p = expr_compile(text, err, sizeof err);
+ if (p) {
+ unsigned deps = expr_deps(p);
+ if (deps == 0) (void)expr_constant(p);
+ (void)expr_source(p);
+
+ const expr_env_t envs[] = {
+ {0},
+ {.x = 79,
+ .y = 23,
+ .cols = 80,
+ .rows = 24,
+ .curx = 40,
+ .cury = 12,
+ .cursor = 1,
+ .focused = 1,
+ .t = 1234567890123LL,
+ .since = 42,
+ .above = 10000,
+ .below = 3},
+ {.x = INT_MAX,
+ .y = INT_MIN,
+ .cols = INT_MAX,
+ .rows = INT_MAX,
+ .curx = -1,
+ .cury = -1,
+ .cursor = -1,
+ .focused = -1,
+ .t = INT64_MAX,
+ .since = INT64_MIN,
+ .above = INT_MAX,
+ .below = INT_MIN},
+ };
+ for (size_t i = 0; i < sizeof envs / sizeof envs[0]; i++)
+ (void)expr_eval(p, &envs[i]);
+
+ /* the cached per-cell map, over a rect small enough to fill twice */
+ expr_env_t env = {.cols = 8, .rows = 4, .cursor = 1, .t = 17};
+ uint8_t *map = NULL;
+ (void)expr_amount_map(p, &env, &map);
+ env.curx = 3;
+ env.cury = 2;
+ (void)expr_amount_map(p, &env, &map); /* map is owned by the program */
+
+ expr_free(p);
+ }
+ free(text);
+ return 0;
+}