diff --git a/CMakeLists.txt b/CMakeLists.txt index e7f2f7ef09bdaf1014e3c023683e47cc6b49fa52..de01b79a07fec9fc65365b975f6d7cdcfeefdb5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ set(CMAKE_LEGACY_CYGWIN_WIN32 0) cmake_minimum_required(VERSION 2.8) -subdirs(tests) +subdirs(tests fuzzing) include(GNUInstallDirs) diff --git a/fuzzing/.gitignore b/fuzzing/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c82a26201784a79292c0b29b40c31e2d9eb9fd9b --- /dev/null +++ b/fuzzing/.gitignore @@ -0,0 +1 @@ +afl-build diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fdd7126e98684ce7c88906ac5bdadaa903af15cf --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,28 @@ +option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off) +if (ENABLE_FUZZING) + find_program(AFL_FUZZ afl-fuzz) + if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND") + message(FATAL_ERROR "Couldn't find afl-fuzz.") + endif() + + add_executable(afl-main afl.c) + target_link_libraries(afl-main "${CJSON_LIB}") + + 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}" + DEPENDS afl-main) + + +endif() diff --git a/fuzzing/afl-prepare-linux.sh b/fuzzing/afl-prepare-linux.sh new file mode 100755 index 0000000000000000000000000000000000000000..41c9b899688e704ae731e945ed3b5de3e2ba43ae --- /dev/null +++ b/fuzzing/afl-prepare-linux.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -x +echo core | sudo tee /proc/sys/kernel/core_pattern +echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor diff --git a/fuzzing/afl.c b/fuzzing/afl.c new file mode 100644 index 0000000000000000000000000000000000000000..af5945d9139298fd0d53718b37574e250c550943 --- /dev/null +++ b/fuzzing/afl.c @@ -0,0 +1,175 @@ +/* + 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 +#include +#include + +#include "../cJSON.h" + +static char *read_file(const char *filename) +{ + FILE *file = NULL; + long length = 0; + char *content = NULL; + size_t read_chars = 0; + + /* open in read binary mode */ + file = fopen(filename, "rb"); + if (file == NULL) + { + goto cleanup; + } + + /* get the length */ + if (fseek(file, 0, SEEK_END) != 0) + { + goto cleanup; + } + length = ftell(file); + if (length < 0) + { + goto cleanup; + } + if (fseek(file, 0, SEEK_SET) != 0) + { + goto cleanup; + } + + /* allocate content buffer */ + content = (char*)malloc((size_t)length + sizeof('\0')); + if (content == NULL) + { + goto cleanup; + } + + /* 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; + } + content[read_chars] = '\0'; + + +cleanup: + if (file != NULL) + { + fclose(file); + } + + return content; +} + +int main(int argc, char** argv) +{ + const char *filename = NULL; + cJSON *item = NULL; + char *json = NULL; + int status; + char *printed_json = NULL; + + if ((argc < 2) || (argc > 3)) + { + printf("Usage:\n"); + printf("%s input_file [enable_printing]\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"); + } + + filename = argv[1]; + +#if __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')) + { + status = EXIT_FAILURE; + goto cleanup; + } + item = cJSON_Parse(json + 2); + if (item == NULL) + { + goto cleanup; + } + + if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0)) + { + int do_format = 0; + if (json[1] == 'f') + { + do_format = 1; + } + + if (json[0] == 'b') + { + /* buffered printing */ + printed_json = cJSON_PrintBuffered(item, 1, do_format); + } + else + { + /* unbuffered printing */ + if (do_format) + { + printed_json = cJSON_Print(item); + } + else + { + printed_json = cJSON_PrintUnformatted(item); + } + } + if (printed_json == NULL) + { + status = EXIT_FAILURE; + goto cleanup; + } + 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/afl.sh b/fuzzing/afl.sh new file mode 100755 index 0000000000000000000000000000000000000000..8f0f02fb017e0141e9eaee3405582ab0f64bdd1f --- /dev/null +++ b/fuzzing/afl.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +mkdir -p afl-build || exit 1 +cd afl-build || exit 1 +#cleanup +rm -r -- * + +CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off +make afl diff --git a/fuzzing/inputs/test1 b/fuzzing/inputs/test1 new file mode 100644 index 0000000000000000000000000000000000000000..6a0c0d7c596034f729dc2acc2e4262b2396750c8 --- /dev/null +++ b/fuzzing/inputs/test1 @@ -0,0 +1,22 @@ +bf{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/fuzzing/inputs/test10 b/fuzzing/inputs/test10 new file mode 100644 index 0000000000000000000000000000000000000000..01e9a82f67d09b2a5e5bff75bc1f00fdf8ef97ce --- /dev/null +++ b/fuzzing/inputs/test10 @@ -0,0 +1 @@ +bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] diff --git a/fuzzing/inputs/test11 b/fuzzing/inputs/test11 new file mode 100644 index 0000000000000000000000000000000000000000..818c6e0f286296dd6b2d897c0985797114717593 --- /dev/null +++ b/fuzzing/inputs/test11 @@ -0,0 +1,8 @@ +bf{ +"name": "Jack (\"Bee\") Nimble", +"format": {"type": "rect", +"width": 1920, +"height": 1080, +"interlace": false,"frame rate": 24 +} +} diff --git a/fuzzing/inputs/test2 b/fuzzing/inputs/test2 new file mode 100644 index 0000000000000000000000000000000000000000..3fdf8cb78682e4e8d967c843103ef073d04b8af2 --- /dev/null +++ b/fuzzing/inputs/test2 @@ -0,0 +1,11 @@ +bf{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/fuzzing/inputs/test3 b/fuzzing/inputs/test3 new file mode 100644 index 0000000000000000000000000000000000000000..7143163ba2dfa8ee539b534aa84f9762da9bd887 --- /dev/null +++ b/fuzzing/inputs/test3 @@ -0,0 +1,26 @@ +bf{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/fuzzing/inputs/test3.bu b/fuzzing/inputs/test3.bu new file mode 100644 index 0000000000000000000000000000000000000000..6fc93d3cfc788d2e299c99ee8babf589451cb84f --- /dev/null +++ b/fuzzing/inputs/test3.bu @@ -0,0 +1,26 @@ +bu{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/fuzzing/inputs/test3.uf b/fuzzing/inputs/test3.uf new file mode 100644 index 0000000000000000000000000000000000000000..d48df612644fca0a5ac7ab2b37017be35c2e3d38 --- /dev/null +++ b/fuzzing/inputs/test3.uf @@ -0,0 +1,26 @@ +uf{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/fuzzing/inputs/test3.uu b/fuzzing/inputs/test3.uu new file mode 100644 index 0000000000000000000000000000000000000000..ad6ae54102c43f45bd0c8e66d5c1071bd492ff5a --- /dev/null +++ b/fuzzing/inputs/test3.uu @@ -0,0 +1,26 @@ +uu{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/fuzzing/inputs/test4 b/fuzzing/inputs/test4 new file mode 100644 index 0000000000000000000000000000000000000000..e24ae9b3d704e330d5c45d65741da4e931b5c838 --- /dev/null +++ b/fuzzing/inputs/test4 @@ -0,0 +1,88 @@ +bf{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/fuzzing/inputs/test5 b/fuzzing/inputs/test5 new file mode 100644 index 0000000000000000000000000000000000000000..f6cc84e131f9022bcc017b6cfa8ea65a64b50382 --- /dev/null +++ b/fuzzing/inputs/test5 @@ -0,0 +1,27 @@ +bf{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] +}} diff --git a/fuzzing/inputs/test6 b/fuzzing/inputs/test6 new file mode 100644 index 0000000000000000000000000000000000000000..af279752e8c4ee6a5e172057f27fe18ca5453200 --- /dev/null +++ b/fuzzing/inputs/test6 @@ -0,0 +1,16 @@ +bf + + + + + Application Error + + + + + diff --git a/fuzzing/inputs/test7 b/fuzzing/inputs/test7 new file mode 100644 index 0000000000000000000000000000000000000000..4a3c0b7a1be7526f5e51fc3bcb7e0d2bcc475c31 --- /dev/null +++ b/fuzzing/inputs/test7 @@ -0,0 +1,22 @@ +bf[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] diff --git a/fuzzing/inputs/test8 b/fuzzing/inputs/test8 new file mode 100644 index 0000000000000000000000000000000000000000..3ffe570ce1fb30341a9c312211df58b000b0032d --- /dev/null +++ b/fuzzing/inputs/test8 @@ -0,0 +1,13 @@ +bf{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http:/*www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } + } diff --git a/fuzzing/inputs/test9 b/fuzzing/inputs/test9 new file mode 100644 index 0000000000000000000000000000000000000000..28c9033ad64858ec33bc4695134e876c769a1f25 --- /dev/null +++ b/fuzzing/inputs/test9 @@ -0,0 +1,5 @@ +bf[ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ] diff --git a/fuzzing/json.dict b/fuzzing/json.dict new file mode 100644 index 0000000000000000000000000000000000000000..ac9c08c822ad37d59e0a6581ab0392c18439c377 --- /dev/null +++ b/fuzzing/json.dict @@ -0,0 +1,47 @@ +# +# AFL dictionary for JSON +# ----------------------------- +# + +object_start="{" +object_end="}" +object_empty="{}" +object_one_element="{\"one\":1}" +object_two_elements="{\"1\":1,\"2\":2}" +object_separator=":" + +array_start="[" +array_end="]" +array_empty="[]" +array_one_element="[1]" +array_two_elements="[1,2]" + +separator="," + +escape_sequence_b="\\b" +escape_sequence_f="\\f" +escape_sequence_n="\\n" +escape_sequence_r="\\r" +escape_sequence_t="\\t" +escape_sequence_quote="\\\"" +escape_sequence_backslash="\\\\" +escapce_sequence_slash="\\/" +escpae_sequence_utf16_base="\\u" +escape_sequence_utf16="\\u12ab" + +number_integer="1" +number_double="1.0" +number_negative_integer="-1" +number_negative_double="-1.0" +number_engineering1="1e1" +number_engineering2="1e-1" +number_positive_integer="+1" +number_positive_double="+1.0" +number_e="e" +number_plus="+" +number_minus="-" +number_separator="." + +null="null" +true="true" +false="false"