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..d95be8548ef4b695228467820aa5f936e1941634 --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,21 @@ +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() + + 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" "@@" + DEPENDS afl-main) + + +endif() diff --git a/fuzzing/afl.c b/fuzzing/afl.c new file mode 100644 index 0000000000000000000000000000000000000000..28c7e40c8f7c5dc09ed71e579e2922477b4256f7 --- /dev/null +++ b/fuzzing/afl.c @@ -0,0 +1,117 @@ +/* + 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 "../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; + + if (argc < 2) + { + printf("Usage:\n"); + printf("%s input_file\n", argv[0]); + printf("\t input_file: file containing the test data"); + } + + filename = argv[1]; + + json = read_file(filename); + item = cJSON_Parse(json); + if (item == NULL) + { + goto cleanup; + } + +cleanup: + if (item != NULL) + { + cJSON_Delete(item); + } + if (json != NULL) + { + free(json); + } + + return EXIT_SUCCESS; +} diff --git a/fuzzing/afl.sh b/fuzzing/afl.sh new file mode 100755 index 0000000000000000000000000000000000000000..64b60a35b041571f6435e40e2ecef40cdf75993e --- /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-gcc 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..eacfbf5e605778dff836a3ae11cadfccb1c0d857 --- /dev/null +++ b/fuzzing/inputs/test1 @@ -0,0 +1,22 @@ +{ + "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..d19eb8b5d2384ef7576fb46509ddc1db78a08b45 --- /dev/null +++ b/fuzzing/inputs/test10 @@ -0,0 +1 @@ +["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] diff --git a/fuzzing/inputs/test11 b/fuzzing/inputs/test11 new file mode 100644 index 0000000000000000000000000000000000000000..eaf43e6efc271fb42b12084946b691f85f195de5 --- /dev/null +++ b/fuzzing/inputs/test11 @@ -0,0 +1,8 @@ +{ +"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..5600991a4c7a02a9365bbc8248806fd11c3d197b --- /dev/null +++ b/fuzzing/inputs/test2 @@ -0,0 +1,11 @@ +{"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..5662b3774e83cf212359c9838c2326dc7b309586 --- /dev/null +++ b/fuzzing/inputs/test3 @@ -0,0 +1,26 @@ +{"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;" + } +}} \ No newline at end of file diff --git a/fuzzing/inputs/test4 b/fuzzing/inputs/test4 new file mode 100644 index 0000000000000000000000000000000000000000..d540b57f0df6834de5d58a5ae874c3661e50e51f --- /dev/null +++ b/fuzzing/inputs/test4 @@ -0,0 +1,88 @@ +{"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"}}} \ No newline at end of file diff --git a/fuzzing/inputs/test5 b/fuzzing/inputs/test5 new file mode 100644 index 0000000000000000000000000000000000000000..49980ca25bcc4b9e11efb75946bc15febb4ec352 --- /dev/null +++ b/fuzzing/inputs/test5 @@ -0,0 +1,27 @@ +{"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..d5cb28f6703e2348628cca50f79db13ee3f13f83 --- /dev/null +++ b/fuzzing/inputs/test6 @@ -0,0 +1,16 @@ + + + + + + Application Error + + + + + \ No newline at end of file diff --git a/fuzzing/inputs/test7 b/fuzzing/inputs/test7 new file mode 100644 index 0000000000000000000000000000000000000000..330853664c43709710fdb4b7222b62823691886a --- /dev/null +++ b/fuzzing/inputs/test7 @@ -0,0 +1,22 @@ +[ + { + "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..4b1f5b97c24ddb4e52d678d80877310edefe5cae --- /dev/null +++ b/fuzzing/inputs/test8 @@ -0,0 +1,13 @@ +{ + "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..2a939b96c841ad320f4033342aa4ea4cb89a582a --- /dev/null +++ b/fuzzing/inputs/test9 @@ -0,0 +1,5 @@ +[ + [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"