diff --git a/fuzzing/.gitignore b/fuzzing/.gitignore index c82a26201784a79292c0b29b40c31e2d9eb9fd9b..520b91fba32656699eb7bcf0646a812459f7abf8 100644 --- a/fuzzing/.gitignore +++ b/fuzzing/.gitignore @@ -1 +1,2 @@ afl-build +libfuzzer-build diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index fdd7126e98684ce7c88906ac5bdadaa903af15cf..5a72afdca6165bbc5d603ef80415252a9e7f7b82 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -5,23 +5,26 @@ if (ENABLE_FUZZING) message(FATAL_ERROR "Couldn't find afl-fuzz.") endif() + option(ENABLE_LIBFUZZER "Enable fuzzing with libfuzzer (only works with llvm 5 which hasn't been release at this point)" Off) + if (ENABLE_LIBFUZZER) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer") + endif() + + add_library(fuzz-target fuzz-target.c) + target_link_libraries(fuzz-target "${CJSON_LIB}") + add_executable(afl-main afl.c) - target_link_libraries(afl-main "${CJSON_LIB}") + target_link_libraries(afl-main fuzz-target) if (NOT ENABLE_SANITIZERS) message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.") endif() - option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On) - set(fuzz_print_parameter "no") - if (ENABLE_FUZZING_PRINT) - set(fuzz_print_parameter "yes") - endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") add_custom_target(afl - COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}" + COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" DEPENDS afl-main) diff --git a/fuzzing/afl.c b/fuzzing/afl.c index 036ed14bcb1ab257090af031cf718536394b0ac5..22158280f3f78df61b952c2f6d29d2d7ab9b2658 100644 --- a/fuzzing/afl.c +++ b/fuzzing/afl.c @@ -24,54 +24,71 @@ #include <stdlib.h> #include <string.h> -#include "../cJSON.h" +#include "fuzz-target.h" -static char *read_file(const char *filename) +static char *read_file(const char *filename, size_t *size) { FILE *file = NULL; long length = 0; char *content = NULL; size_t read_chars = 0; + if (size == NULL) + { + goto fail; + } + /* open in read binary mode */ file = fopen(filename, "rb"); if (file == NULL) { - goto cleanup; + goto fail; } /* get the length */ if (fseek(file, 0, SEEK_END) != 0) { - goto cleanup; + goto fail; } length = ftell(file); if (length < 0) { - goto cleanup; + goto fail; } if (fseek(file, 0, SEEK_SET) != 0) { - goto cleanup; + goto fail; } /* allocate content buffer */ content = (char*)malloc((size_t)length + sizeof("")); if (content == NULL) { - goto cleanup; + goto fail; } /* read the file into memory */ read_chars = fread(content, sizeof(char), (size_t)length, file); if ((long)read_chars != length) { - free(content); - content = NULL; - goto cleanup; + goto fail; } content[read_chars] = '\0'; + *size = read_chars + sizeof(""); + + goto cleanup; + +fail: + if (size != NULL) + { + *size = 0; + } + if (content != NULL) + { + free(content); + content = NULL; + } cleanup: if (file != NULL) @@ -85,92 +102,50 @@ cleanup: int main(int argc, char** argv) { const char *filename = NULL; - cJSON *item = NULL; char *json = NULL; int status = EXIT_FAILURE; char *printed_json = NULL; - if ((argc < 2) || (argc > 3)) + if (argc != 2) { printf("Usage:\n"); - printf("%s input_file [enable_printing]\n", argv[0]); + printf("%s input_file\n", argv[0]); printf("\t input_file: file containing the test data\n"); - printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n"); goto cleanup; } filename = argv[1]; -#if __AFL_HAVE_MANUAL_CONTROL +#if defined(__AFL_HAVE_MANUAL_CONTROL) && __AFL_HAVE_MANUAL_CONTROL while (__AFL_LOOP(1000)) { -#endif - status = EXIT_SUCCESS; - - json = read_file(filename); - if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0')) +#else { - status = EXIT_FAILURE; - goto cleanup; - } - item = cJSON_Parse(json + 2); - if (item == NULL) - { - goto cleanup; - } +#endif + size_t size = 0; + status = EXIT_SUCCESS; - if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0)) - { - int do_format = 0; - if (json[1] == 'f') + json = read_file(filename, &size); + if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0')) { - do_format = 1; + status = EXIT_FAILURE; + goto cleanup; } - if (json[0] == 'b') - { - /* buffered printing */ - printed_json = cJSON_PrintBuffered(item, 1, do_format); - } - else + LLVMFuzzerTestOneInput(json, size); + + cleanup: + if (json != NULL) { - /* unbuffered printing */ - if (do_format) - { - printed_json = cJSON_Print(item); - } - else - { - printed_json = cJSON_PrintUnformatted(item); - } + free(json); + json = NULL; } - if (printed_json == NULL) + if (printed_json != NULL) { - status = EXIT_FAILURE; - goto cleanup; + free(printed_json); + printed_json = NULL; } - printf("%s\n", printed_json); - } - -cleanup: - if (item != NULL) - { - cJSON_Delete(item); - item = NULL; } - if (json != NULL) - { - free(json); - json = NULL; - } - if (printed_json != NULL) - { - free(printed_json); - printed_json = NULL; - } -#if __AFL_HAVE_MANUAL_CONTROL - } -#endif return status; } diff --git a/fuzzing/fuzz-target.c b/fuzzing/fuzz-target.c new file mode 100644 index 0000000000000000000000000000000000000000..057a296a21f2067379971009281a205e0f8e5460 --- /dev/null +++ b/fuzzing/fuzz-target.c @@ -0,0 +1,129 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include <string.h> +#include <stdio.h> + +#include "fuzz-target.h" +#include "../cJSON.h" + +static void minify(const unsigned char *data, size_t size) +{ + unsigned char *copied_data = (unsigned char*)malloc(size); + if (copied_data == NULL) + { + return; + } + + memcpy(copied_data, data, size); + + cJSON_Minify((char*)copied_data); + + free(copied_data); + + return; +} + +static void printing(cJSON *json, unsigned char format_setting, unsigned char buffered_setting) +{ + unsigned char *printed = NULL; + + if (buffered_setting == '1') + { + printed = (unsigned char*)cJSON_PrintBuffered(json, 1, (format_setting == '1')); + } + else + { + if (format_setting == '1') + { + printed = (unsigned char*)cJSON_Print(json); + } + else + { + printed = (unsigned char*)cJSON_PrintUnformatted(json); + } + } + + if (printed != NULL) + { + free(printed); + } +} + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) +{ + unsigned char minify_setting = '\0'; /* minify instead of parsing */ + unsigned char require_zero_setting = '\0'; /* zero termination required */ + unsigned char format_setting = '\0'; /* formatted printing */ + unsigned char buffered_setting = '\0'; /* buffered printing */ + const size_t data_offset = 4; + + cJSON *json = NULL; + + /* don't work with NULL or without mode selector */ + if ((data == NULL) || (size < data_offset)) + { + return 0; + } + + /* get configuration from the beginning of the test case */ + minify_setting = data[0]; + require_zero_setting = data[1]; + format_setting = data[2]; + buffered_setting = data[3]; + + /* check if settings are valid */ + if ((minify_setting != '0') && (minify_setting != '1')) + { + return 0; + } + if ((require_zero_setting != '0') && (require_zero_setting != '1')) + { + return 0; + } + if ((format_setting != '0') && (format_setting != '1')) + { + return 0; + } + if ((buffered_setting != '0') && (buffered_setting != '1')) + { + return 0; + } + + if (minify_setting == '1') + { + minify(data + data_offset, size); + return 0; + } + + json = cJSON_ParseWithOpts((const char*)data + data_offset, NULL, (require_zero_setting == '1')); + if (json == NULL) + { + return 0; + } + + printing(json, format_setting, buffered_setting); + + free(json); + + return 0; +} diff --git a/fuzzing/fuzz-target.h b/fuzzing/fuzz-target.h new file mode 100644 index 0000000000000000000000000000000000000000..8b3115d9becdf6bbad7d1149b10bed0816dc0e88 --- /dev/null +++ b/fuzzing/fuzz-target.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include <stdlib.h> + +#ifndef CJSON_FUZZ_TARGET +#define CJSON_FUZZ_TARGET + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); + +#endif /* CJSON_FUZZ_TARGET */ diff --git a/fuzzing/inputs/test1 b/fuzzing/inputs/test1 index 6a0c0d7c596034f729dc2acc2e4262b2396750c8..c88024fbfbdc143af8e16dc3a2a67788998abb01 100644 --- a/fuzzing/inputs/test1 +++ b/fuzzing/inputs/test1 @@ -1,4 +1,4 @@ -bf{ +0111{ "glossary": { "title": "example glossary", "GlossDiv": { diff --git a/fuzzing/inputs/test10 b/fuzzing/inputs/test10 index 01e9a82f67d09b2a5e5bff75bc1f00fdf8ef97ce..58526971a1f76847e89a73ad51c1e47d3c7c5a0b 100644 --- a/fuzzing/inputs/test10 +++ b/fuzzing/inputs/test10 @@ -1 +1 @@ -bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] +0111["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] diff --git a/fuzzing/inputs/test11 b/fuzzing/inputs/test11 index 818c6e0f286296dd6b2d897c0985797114717593..686d07989637febdb51db9fbfc10802583b8bbc7 100644 --- a/fuzzing/inputs/test11 +++ b/fuzzing/inputs/test11 @@ -1,4 +1,4 @@ -bf{ +0111{ "name": "Jack (\"Bee\") Nimble", "format": {"type": "rect", "width": 1920, diff --git a/fuzzing/inputs/test2 b/fuzzing/inputs/test2 index 3fdf8cb78682e4e8d967c843103ef073d04b8af2..04aa455b074465d05a6ae8ba913217d09105581d 100644 --- a/fuzzing/inputs/test2 +++ b/fuzzing/inputs/test2 @@ -1,4 +1,4 @@ -bf{"menu": { +0111{"menu": { "id": "file", "value": "File", "popup": { diff --git a/fuzzing/inputs/test3 b/fuzzing/inputs/test3 index 7143163ba2dfa8ee539b534aa84f9762da9bd887..d1e0a0786f72d3f75e0edba8410af1f593911d14 100644 --- a/fuzzing/inputs/test3 +++ b/fuzzing/inputs/test3 @@ -1,4 +1,4 @@ -bf{"widget": { +0111{"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", diff --git a/fuzzing/inputs/test3.bu b/fuzzing/inputs/test3.bu index 6fc93d3cfc788d2e299c99ee8babf589451cb84f..e58848cec07c5029b00e129dc064265da4defcbe 100644 --- a/fuzzing/inputs/test3.bu +++ b/fuzzing/inputs/test3.bu @@ -1,4 +1,4 @@ -bu{"widget": { +0101{"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", diff --git a/fuzzing/inputs/test3.uf b/fuzzing/inputs/test3.uf index d48df612644fca0a5ac7ab2b37017be35c2e3d38..6b983b782fbd509b69c27cd427826286974271a6 100644 --- a/fuzzing/inputs/test3.uf +++ b/fuzzing/inputs/test3.uf @@ -1,4 +1,4 @@ -uf{"widget": { +0110{"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", diff --git a/fuzzing/inputs/test3.uu b/fuzzing/inputs/test3.uu index ad6ae54102c43f45bd0c8e66d5c1071bd492ff5a..7e4482b1493d797fd8c1887e8fc9d58037538729 100644 --- a/fuzzing/inputs/test3.uu +++ b/fuzzing/inputs/test3.uu @@ -1,4 +1,4 @@ -uu{"widget": { +0100{"widget": { "debug": "on", "window": { "title": "Sample Konfabulator Widget", diff --git a/fuzzing/inputs/test4 b/fuzzing/inputs/test4 index e24ae9b3d704e330d5c45d65741da4e931b5c838..ca248f90e5d384fedcc383527cc21cac2b4be2cf 100644 --- a/fuzzing/inputs/test4 +++ b/fuzzing/inputs/test4 @@ -1,4 +1,4 @@ -bf{"web-app": { +0111{"web-app": { "servlet": [ { "servlet-name": "cofaxCDS", diff --git a/fuzzing/inputs/test5 b/fuzzing/inputs/test5 index f6cc84e131f9022bcc017b6cfa8ea65a64b50382..814e7e8087acf2edab56901af3c1fc4e3152f4b3 100644 --- a/fuzzing/inputs/test5 +++ b/fuzzing/inputs/test5 @@ -1,4 +1,4 @@ -bf{"menu": { +0111{"menu": { "header": "SVG Viewer", "items": [ {"id": "Open"}, diff --git a/fuzzing/inputs/test6 b/fuzzing/inputs/test6 index af279752e8c4ee6a5e172057f27fe18ca5453200..e637a4049210b1d2cbd9e9e32e13f0760f332f8c 100644 --- a/fuzzing/inputs/test6 +++ b/fuzzing/inputs/test6 @@ -1,4 +1,4 @@ -bf<!DOCTYPE html> +0111<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> diff --git a/fuzzing/inputs/test7 b/fuzzing/inputs/test7 index 4a3c0b7a1be7526f5e51fc3bcb7e0d2bcc475c31..ce7cd3cf35b780e25142fbd0edae7c0045c9b0f2 100644 --- a/fuzzing/inputs/test7 +++ b/fuzzing/inputs/test7 @@ -1,4 +1,4 @@ -bf[ +0111[ { "precision": "zip", "Latitude": 37.7668, diff --git a/fuzzing/inputs/test8 b/fuzzing/inputs/test8 index 3ffe570ce1fb30341a9c312211df58b000b0032d..aed7bd0d79424a3d3c2bd2354890a8308f5e6fe0 100644 --- a/fuzzing/inputs/test8 +++ b/fuzzing/inputs/test8 @@ -1,4 +1,4 @@ -bf{ +0111{ "Image": { "Width": 800, "Height": 600, diff --git a/fuzzing/inputs/test9 b/fuzzing/inputs/test9 index 28c9033ad64858ec33bc4695134e876c769a1f25..a99c4eb0bbc4e99b3c50c1b9641e380e20e07826 100644 --- a/fuzzing/inputs/test9 +++ b/fuzzing/inputs/test9 @@ -1,4 +1,4 @@ -bf[ +0111[ [0, -1, 0], [1, 0, 0], [0, 0, 1] diff --git a/fuzzing/libfuzzer.sh b/fuzzing/libfuzzer.sh new file mode 100755 index 0000000000000000000000000000000000000000..3c81917cc8b76f3d6e1608a408bf7331370d827c --- /dev/null +++ b/fuzzing/libfuzzer.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +mkdir -p libfuzzer-build || exit 1 +cd libfuzzer-build || exit 1 +#cleanup +rm -r -- * + +CC=clang cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off -DCMAKE_BUILD_TYPE=Debug -DENABLE_LIBFUZZER=On +make fuzz-target