From 44300e43101a7b937ac2f5cd482c9e9a9d262fdb Mon Sep 17 00:00:00 2001 From: David Korczynski Date: Mon, 27 Nov 2023 16:19:56 +0000 Subject: [PATCH] Extend fuzzing set up Adds a parse function ins `jv_parse.c` that enables parsing using custom flags for the parser. This is then used by two fuzzers added as well. This is to make sure fuzzing hits various code parts currently not fuzzed, e.g. `stream_token`: https://storage.googleapis.com/oss-fuzz-coverage/jq/reports/20231125/linux/src/jq/src/jv_parse.c.html#L241 Signed-off-by: David Korczynski --- src/jv.h | 1 + src/jv_parse.c | 43 ++++++++++++++++++++++++++++++++++ tests/jq_fuzz_parse_extended.c | 29 +++++++++++++++++++++++ tests/jq_fuzz_parse_stream.c | 21 +++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 tests/jq_fuzz_parse_extended.c create mode 100644 tests/jq_fuzz_parse_stream.c diff --git a/src/jv.h b/src/jv.h index eb313f8a..2cc63c79 100644 --- a/src/jv.h +++ b/src/jv.h @@ -234,6 +234,7 @@ enum { jv jv_parse(const char* string); jv jv_parse_sized(const char* string, int length); +jv jv_parse_custom_flags(const char* string, int flags); typedef void (*jv_nomem_handler_f)(void *); void jv_nomem_handler(jv_nomem_handler_f, void *); diff --git a/src/jv_parse.c b/src/jv_parse.c index 3be93608..0c427eae 100644 --- a/src/jv_parse.c +++ b/src/jv_parse.c @@ -901,3 +901,46 @@ jv jv_parse_sized(const char* string, int length) { jv jv_parse(const char* string) { return jv_parse_sized(string, strlen(string)); } + +jv jv_parse_sized_custom_flags(const char* string, int length, int flags) { + struct jv_parser parser; + parser_init(&parser, flags); + jv_parser_set_buf(&parser, string, length, 0); + jv value = jv_parser_next(&parser); + if (jv_is_valid(value)) { + jv next = jv_parser_next(&parser); + if (jv_is_valid(next)) { + // multiple JSON values, we only wanted one + jv_free(value); + jv_free(next); + value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values")); + } else if (jv_invalid_has_msg(jv_copy(next))) { + // parser error after the first JSON value + jv_free(value); + value = next; + } else { + // a single valid JSON value + jv_free(next); + } + } else if (jv_invalid_has_msg(jv_copy(value))) { + // parse error, we'll return it + } else { + // no value at all + jv_free(value); + value = jv_invalid_with_msg(jv_string("Expected JSON value")); + } + parser_free(&parser); + + if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) { + jv msg = jv_invalid_get_msg(value); + value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", + jv_string_value(msg), + string)); + jv_free(msg); + } + return value; +} + +jv jv_parse_custom_flags(const char* string, int flags) { + return jv_parse_sized_custom_flags(string, strlen(string), flags); +} diff --git a/tests/jq_fuzz_parse_extended.c b/tests/jq_fuzz_parse_extended.c new file mode 100644 index 00000000..af6ceccc --- /dev/null +++ b/tests/jq_fuzz_parse_extended.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "jv.h" + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + if (size < 4) { + return 0; + } + + int fuzz_flags = *(int*)data; + data += 4; + size -= 4; + + // Creat null-terminated string + char *null_terminated = (char *)malloc(size + 1); + memcpy(null_terminated, (char *)data, size); + null_terminated[size] = '\0'; + + // Fuzzer entrypoint + jv res = jv_parse_custom_flags(null_terminated, fuzz_flags); + jv_free(res); + + // Free the null-terminated string + free(null_terminated); + + return 0; +} diff --git a/tests/jq_fuzz_parse_stream.c b/tests/jq_fuzz_parse_stream.c new file mode 100644 index 00000000..5b51c599 --- /dev/null +++ b/tests/jq_fuzz_parse_stream.c @@ -0,0 +1,21 @@ +#include +#include +#include + +#include "jv.h" + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + // Creat null-terminated string + char *null_terminated = (char *)malloc(size + 1); + memcpy(null_terminated, (char *)data, size); + null_terminated[size] = '\0'; + + // Fuzzer entrypoint + jv res = jv_parse_custom_flags(null_terminated, JV_PARSE_STREAMING); + jv_free(res); + + // Free the null-terminated string + free(null_terminated); + + return 0; +}