From 5029328d35f3e60037970d27f350a742af41aa02 Mon Sep 17 00:00:00 2001 From: David Korczynski Date: Wed, 6 Dec 2023 22:46:45 +0000 Subject: tests: add new fuzzer Signed-off-by: David Korczynski --- tests/jq_fuzz_fixed.cpp | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 tests/jq_fuzz_fixed.cpp diff --git a/tests/jq_fuzz_fixed.cpp b/tests/jq_fuzz_fixed.cpp new file mode 100644 index 00000000..3dd955bb --- /dev/null +++ b/tests/jq_fuzz_fixed.cpp @@ -0,0 +1,297 @@ +#include +#include + +#include "jq.h" +#include "jv.h" + + +const char *jq_progs[] = { + ". / \", \"", + ".[]", + "$ENV.PAGER", + ".[0]", + ". < 0.12345678901234567890123456788", + ".[] == 1", + ".[] | (1 / .)?", + "10 / . * 3", + "[1,2,empty,3]", + "1, empty, 2", + "[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]", + ".[-2:]", + ".[-2]", + ".[2]", + "[ .[] | . * 2]", + ".[2:4]", + "(. + 2) * 5", + ".[:3]", + ".[4,2]", + "42 and \"a string\"", + "4 - .a", + ". < 5", + ".. | .a?", + "[.[] | .a?]", + ".a + 1", + "{a: 1} + {b: 2} + {c: 3} + {a: 42}", + ".a + .b", + ".a = .b", + ".a |= .b", + "add", + "all", + ".a + null", + "any", + ".[] as [$a, $b] | {a: $a, b: $b}", + ". as [$a, $b, {c: $c}] | $a + $b + $c", + ".[] as {$a, $b, c: {$d}} ?// {$a, $b, c: [{$e}]} | {$a, $b, $d, $e}", + ".[] as {$a, $b, c: {$d, $e}} ?// {$a, $b, c: [{$d, $e}]} | {$a, $b, " + "$d, $e}", + ".[] as [$a] ?// [$b] | if $a != null then error(\"err: \($a)\") else " + "{$a,$b} end", + ". as $big | [$big, $big + 1] | map(. > " + "10000000000000000000000000000000)", + ". as $dot|fromstream($dot|tostream)|.==$dot", + ". as $i|[(.*2|. as $i| $i), $i]", + "ascii_upcase", + ".bar as $x | .foo | . + $x", + "@base64", + "@base64d", + ". == {\"b\": {\"d\": (4 + 1e-20), \"c\": 3}, \"a\":1}", + "bsearch(0)", + "bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end", + "capture(\"(?[a-z]+)-(?[0-9]+)\")", + "capture(\"(?[a-z]+)-(?[0-9]+)\")", + "combinations", + "combinations(2)", + "contains(\"bar\")", + "contains([\"baz\", \"bar\"])", + "contains([\"bazzzzz\", \"bar\"])", + "contains({foo: 12, bar: [{barp: 15}]})", + "def addvalue(f): f as $x | map(. + $x); addvalue(.[0])", + "def addvalue(f): . + [f]; map(addvalue(.[0]))", + "def while(cond; update): def _while: if cond then ., (update | " + "_while) else empty end; _while; [while(.<100; .*2)]", + "del(.[1, 2])", + "del(.foo)", + "delpaths([[\"a\",\"b\"]])", + "empty // 42", + "[.[]|endswith(\"foo\")]", + "env.PAGER", + "explode", + ". == false", + "(false, null, 1) // 42", + "(false, null, 1) | . // 42", + "flatten", + "flatten(1)", + "floor", + ".[\"foo\"]", + ".[\"foo\"]?", + ".foo", + ".foo?", + ".foo[]", + "[.foo?]", + ".foo += 1", + ".foo // 42", + ".foo, .bar", + "foreach .[] as $item (0; . + $item)", + "foreach .[] as $item (0; . + $item; [$item, . * 2])", + "foreach .[] as $item (0; . + 1; {index: ., $item})", + "fromdate", + "from_entries", + "fromstream(1|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))", + "getpath([\"a\",\"b\"])", + "[getpath([\"a\",\"b\"], [\"a\",\"c\"])]", + "group_by(.foo)", + "[.[] | gsub(\", \"; \":\")]", + "gsub(\"$\"; \"a\"; \"g\")", + "gsub(\"^\"; \"a\")", + "[gsub(\"(?.)\"; \"\(.a|ascii_upcase)\", \"\(.a|ascii_downcase)\", " + "\"c\")]", + "gsub(\"^.*?a\"; \"b\")", + "gsub(\"^.*a\"; \"b\")", + "gsub(\"a\";\"b\")", + "gsub(\"\"; \"a\"; \"g\")", + "gsub(\"\"; \"a\"; \"g\")", + "gsub(\"[^a-z]*(?[a-z]*)\"; \"Z\(.x)\")", + "gsub(\"\\b(?.)\"; \"\(.x|ascii_downcase)\")", + "gsub(\"(?\\d)\"; \":\(.d);\")", + "gsub(\"^\"; \"\"; \"g\")", + "[gsub(\"p\"; \"a\", \"b\")]", + "gsub(\"(?=u)\"; \"u\")", + "gsub(\"(.*)\"; \"\"; \"x\")", + "gsub(\"(?.)[^a]*\"; \"+\(.x)-\")", + "gsub(\"(?.)(?[0-9])\"; \"\(.x|ascii_downcase)\(.y)\")", + "@html", + "if . == 0 then \"zero\" elif . == 1 then \"one\" else \"many\" " + "end", + "implode", + "index(\", \")", + "index(1)", + "index([1,2])", + "indices(\", \")", + "indices(1)", + "indices([1,2])", + ".[] | (infinite * .) < 0", + "infinite, nan | type", + ".[] | in({\"foo\": 42})", + "inside({\"foo\": 12, \"bar\":[1,2,{\"barp\":12, \"blip\":13}]})", + "inside({\"foo\": 12, \"bar\":[1,2,{\"barp\":12, \"blip\":13}]})", + "inside(\"foobar\")", + "inside([\"foobar\", \"foobaz\", \"blarp\"])", + "inside([\"foobar\", \"foobaz\", \"blarp\"])", + "isempty(.[])", + "isempty(.[])", + "isempty(empty)", + "join(\" \")", + "join(\", \")", + "keys", + "keys", + ".[] | length", + "[limit(3;.[])]", + "[.[]|ltrimstr(\"foo\")]", + "map(., .)", + "map(.+1)", + "map([., . == 1]) | tojson", + "map(abs)", + "map(has(2))", + "map(has(\"foo\"))", + "map(in([0,1]))", + "map(select(. >= 2))", + "map(type)", + "map_values(.+1)", + "map_values(. // empty)", + "match(\"(abc)+\"; \"g\")", + "[match(\"a\"; \"gi\")]", + "[match(\".+?\\b\")]", + "[match([\"(bar)\"])]", + "match(\"foo\")", + "[match([\"foo (?bar)? foo\", \"ig\"])]", + "match(\"foo (?bar)? foo\"; \"ig\")", + "match([\"foo\", \"ig\"])", + "[match(\"\"; \"g\")]", + "[ match(\".\"; \"g\")] | length", + "[match(\"( )*\"; \"gn\")]", + "max_by(.foo)", + "min", + ".[] | .name", + ".[]|numbers", + "[path(..)]", + "path(.a[0].b)", + "[paths]", + "[paths(type == \"number\")]", + "pick(.[2], .[0], .[0])", + "pick(.a, .b.c, .x)", + "[range(0; 10; -1)]", + "[range(0; 10; 3)]", + "[range(0; -5; -1)]", + "[range(2; 4)]", + "range(2; 4)", + "[range(4)]", + "[range(.)]|[first, last, nth(5)]", + "recurse", + "recurse(. * .; . < 20)", + "recurse(.foo[])", + "reduce .[] as [$i,$j] (0; . + $i * $j)", + "reduce .[] as $item (0; . + $item)", + "reduce .[] as {$x,$y} (null; .x += $x | .y += [$y])", + "[repeat(.*2, error)?]", + "reverse", + "rindex(\", \")", + "rindex(1)", + "rindex([1,2])", + "[.[]|rtrimstr(\"foo\")]", + "[.[] | scan(\", \")]", + "[.[] | scan(\"b+\"; \"i\")]", + "scan(\"c\")", + ".[] | select(.id == \"second\")", + "(..|select(type==\"boolean\")) |= if . then 1 else 0 end", + "setpath([0,\"a\"]; 1)", + "setpath([\"a\",\"b\"]; 1)", + "setpath([\"a\",\"b\"]; 1)", + "@sh \"echo \(.)\"", + "sort", + "sort_by(.foo)", + "sort_by(.foo, .bar)", + "split(\", *\"; null)", + "splits(\", *\")", + "sqrt", + "[.[]|startswith(\"foo\")]", + "strptime(\"%Y-%m-%dT%H:%M:%SZ\")", + "strptime(\"%Y-%m-%dT%H:%M:%SZ\")|mktime", + "[sub(\"(?.)\"; \"\(.a|ascii_upcase)\", \"\(.a|ascii_downcase)\")]", + "[sub(\"(?.)\"; \"\(.a|ascii_upcase)\", \"\(.a|ascii_downcase)\", " + "\"c\")]", + "[sub(\"a\"; \"b\", \"c\")]", + "sub(\"[^a-z]*(?[a-z]+)\"; \"Z\(.x)\"; \"g\")", + "[.[]|[[sub(\", *\";\":\")], [gsub(\", *\";\":\")], [scan(\", *\")]]]", + "[.[]|[[sub(\", +\";\":\")], [gsub(\", +\";\":\")], [scan(\", +\")]]]", + "sub(\"^(?.)\"; \"Head=\(.head) Tail=\")", + "[test(\"ā\")]", + ".[] | test(\"a b c # spaces are ignored\"; \"ix\")", + "test(\"foo\")", + "to_entries", + "[., tojson]", + "[.[]|tojson]", + "[.[]|tojson|fromjson]", + ".[] | tonumber", + "[.[] | tonumber?]", + ".[] | tostring", + "[.[]|tostring]", + "transpose", + "[true, false | not]", + "(true, false) or false", + "(true, true) and (true, false)", + "truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])", + "[.[]|try .a]", + "unique", + "unique_by(.foo)", + "unique_by(length)", + ".user, .projects[]", + "[.user, .projects[]]", + "{(.user): .titles}", + "{user, title: .titles[]}", + "utf8bytelength", + "walk(if type == \"array\" then sort else . end)", + "walk( if type == \"object\" then with_entries( .key |= sub( \"^_+\"; " + "\"\") ) else . end )", + "[while(.<100; .*2)]", + "with_entries(.key |= \"KEY_\" + .)", + ". - [\"xml\", \"yaml\"]", +}; + +// Fuzzer inspired by /src/jq_test.c +// The goal is to have the fuzzer execute the functions: +// jq_compile -> jv_parse -> jq_next. +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + std::string parse_payload1 = fdp.ConsumeRandomLengthString(); + std::string parse_payload2 = fdp.ConsumeRandomLengthString(); + + int idx = fdp.ConsumeIntegralInRange( + 0, (sizeof(jq_progs) / sizeof(char *)) - 1); + + jq_state *jq = NULL; + jq = jq_init(); + if (jq != NULL) { + jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string("/tmp/")); + + if (jq_compile(jq, jq_progs[idx])) { + // Process to jv_parse and then jv_next + jv input = jv_parse(parse_payload1.c_str()); + if (jv_is_valid(input)) { + jq_start(jq, input, 0); + jv next = jv_parse(parse_payload2.c_str()); + if (jv_is_valid(next)) { + jv actual = jq_next(jq); + jv_free(actual); + } + jv_free(next); + } else { + // Only free if input is invalid as otherwise jq_teardown + // frees it. + jv_free(input); + } + } + } + jq_teardown(&jq); + + return 0; +} -- cgit v1.2.3