summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Korczynski <david@adalogics.com>2023-11-27 16:19:56 +0000
committerEmanuele Torre <torreemanuele6@gmail.com>2023-11-28 20:36:59 +0100
commit44300e43101a7b937ac2f5cd482c9e9a9d262fdb (patch)
tree1e223516867772ec942d6d440a8d761f8e5592de
parent88f01a741c8d63c4d1b5bc3ef61520c6eb93edaa (diff)
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 <david@adalogics.com>
-rw-r--r--src/jv.h1
-rw-r--r--src/jv_parse.c43
-rw-r--r--tests/jq_fuzz_parse_extended.c29
-rw-r--r--tests/jq_fuzz_parse_stream.c21
4 files changed, 94 insertions, 0 deletions
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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}