aboutsummaryrefslogtreecommitdiffstats
path: root/tests/fuzz/fuzz_jsonval.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_jsonval.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_jsonval.c')
-rw-r--r--tests/fuzz/fuzz_jsonval.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/tests/fuzz/fuzz_jsonval.c b/tests/fuzz/fuzz_jsonval.c
new file mode 100644
index 0000000..fee4279
--- /dev/null
+++ b/tests/fuzz/fuzz_jsonval.c
@@ -0,0 +1,48 @@
+/* Fuzz target: the JSON reader (src/jsonval.c).
+ *
+ * This is what the control API feeds every MSG_CMD line into, so its input
+ * is exactly "whatever a client wrote on the socket". Parse, then walk the
+ * whole value through the accessors cmd.c uses.
+ */
+#include "jsonval.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void walk(const jv_t *v, int depth) {
+ if (!v || depth > 200) return;
+ (void)jv_str(v, "fallback");
+ (void)jv_int(v, -1);
+ (void)jv_bool(v, false);
+ if (v->kind == JV_OBJ) {
+ (void)jv_get(v, "no-such-key");
+ (void)jv_gets(v, "no-such-key", "fb");
+ (void)jv_geti(v, "no-such-key", 7);
+ (void)jv_getb(v, "no-such-key", true);
+ for (size_t i = 0; i < v->len; i++) {
+ (void)jv_get(v, v->keys[i]);
+ walk(v->kids[i], depth + 1);
+ }
+ } else if (v->kind == JV_ARR) {
+ for (size_t i = 0; i < v->len; i++) walk(v->kids[i], depth + 1);
+ } else if (v->kind == JV_STR) {
+ /* the string must really be len bytes and NUL-terminated */
+ if (v->str && strlen(v->str) > v->len) abort();
+ }
+}
+
+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';
+
+ jv_t *v = jv_parse(text);
+ if (v) {
+ walk(v, 0);
+ jv_free(v);
+ }
+ free(text);
+ return 0;
+}