Compare commits

...

7 Commits

Author SHA1 Message Date
Max Bruckner
7aed06e364 WIP: Libfuzzer 2017-06-06 14:43:09 +02:00
Max Bruckner
33865a75a1 tests/print_number: Add test with 17 digits of precision 2017-06-06 14:35:54 +02:00
Max Bruckner
6d3a32122e tests/print_number: Use proper double literals 2017-06-06 14:35:54 +02:00
Max Bruckner
ed549df6e7 Add warning -Wswitch-enum 2017-06-06 14:35:54 +02:00
Max Bruckner
7aa668ee7a Add warning -Wused-but-marked-unused 2017-06-06 14:35:54 +02:00
Max Bruckner
7f8ee7084a Add warning -Wmissing-variable-declarations 2017-06-06 14:35:53 +02:00
Max Bruckner
5f1fdb4c6f Add warning -Wunused-macro 2017-06-06 14:35:50 +02:00
25 changed files with 261 additions and 102 deletions

View File

@ -42,6 +42,10 @@ if (ENABLE_CUSTOM_COMPILER_FLAGS)
-Wdouble-promotion
-Wparentheses
-Wformat-overflow
-Wunused-macros
-Wmissing-variable-declarations
-Wused-but-marked-unused
-Wswitch-enum
)
endif()

View File

@ -208,7 +208,6 @@ typedef struct
/* check if the given size is left to read in a given parse buffer (starting with 1) */
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
#define cannot_read(buffer, size) (!can_read(buffer, size))
/* check if the buffer can be accessed at the given index (starting with 0) */
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))

1
fuzzing/.gitignore vendored
View File

@ -1 +1,2 @@
afl-build
libfuzzer-build

View File

@ -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)

View File

@ -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))
{
#else
{
#endif
status = EXIT_SUCCESS;
size_t size = 0;
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)
json = read_file(filename, &size);
if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
{
status = EXIT_FAILURE;
goto cleanup;
}
printf("%s\n", printed_json);
}
cleanup:
if (item != NULL)
{
cJSON_Delete(item);
item = NULL;
LLVMFuzzerTestOneInput(json, size);
cleanup:
if (json != NULL)
{
free(json);
json = NULL;
}
if (printed_json != NULL)
{
free(printed_json);
printed_json = 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;
}

129
fuzzing/fuzz-target.c Normal file
View File

@ -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;
}

30
fuzzing/fuzz-target.h Normal file
View File

@ -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 */

View File

@ -1,4 +1,4 @@
bf{
0111{
"glossary": {
"title": "example glossary",
"GlossDiv": {

View File

@ -1 +1 @@
bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
0111["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

View File

@ -1,4 +1,4 @@
bf{
0111{
"name": "Jack (\"Bee\") Nimble",
"format": {"type": "rect",
"width": 1920,

View File

@ -1,4 +1,4 @@
bf{"menu": {
0111{"menu": {
"id": "file",
"value": "File",
"popup": {

View File

@ -1,4 +1,4 @@
bf{"widget": {
0111{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",

View File

@ -1,4 +1,4 @@
bu{"widget": {
0101{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",

View File

@ -1,4 +1,4 @@
uf{"widget": {
0110{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",

View File

@ -1,4 +1,4 @@
uu{"widget": {
0100{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",

View File

@ -1,4 +1,4 @@
bf{"web-app": {
0111{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",

View File

@ -1,4 +1,4 @@
bf{"menu": {
0111{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},

View File

@ -1,4 +1,4 @@
bf<!DOCTYPE html>
0111<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@ -1,4 +1,4 @@
bf[
0111[
{
"precision": "zip",
"Latitude": 37.7668,

View File

@ -1,4 +1,4 @@
bf{
0111{
"Image": {
"Width": 800,
"Height": 600,

View File

@ -1,4 +1,4 @@
bf[
0111[
[0, -1, 0],
[1, 0, 0],
[0, 0, 1]

9
fuzzing/libfuzzer.sh Executable file
View File

@ -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

View File

@ -25,6 +25,14 @@ if(ENABLE_CJSON_TEST)
target_compile_options(unity PRIVATE "-fno-sanitize=float-divide-by-zero")
endif()
endif()
# Disable -Wswitch-enum for Unity
if (FLAG_SUPPORTED_Wswitchenum)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-switch-enum")
else()
target_compile_options(unity PRIVATE "-Wno-switch-enum")
endif()
endif()
#copy test files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")

View File

@ -119,7 +119,7 @@ static cJSON_bool test_apply_patch(const cJSON * const test)
return successful;
}
static cJSON_bool test_generate_test(cJSON *test __attribute__((unused)))
static cJSON_bool test_generate_test(cJSON *test)
{
cJSON *doc = NULL;
cJSON *patch = NULL;

View File

@ -49,16 +49,16 @@ static void print_number_should_print_zero(void)
static void print_number_should_print_negative_integers(void)
{
assert_print_number("-1", -1);
assert_print_number("-32768", -32768);
assert_print_number("-1", -1.0);
assert_print_number("-32768", -32768.0);
assert_print_number("-2147483648", -2147483648.0);
}
static void print_number_should_print_positive_integers(void)
{
assert_print_number("1", 1);
assert_print_number("32767", 32767);
assert_print_number("2147483647", 2147483647);
assert_print_number("1", 1.0);
assert_print_number("32767", 32767.0);
assert_print_number("2147483647", 2147483647.0);
}
static void print_number_should_print_positive_reals(void)
@ -68,6 +68,7 @@ static void print_number_should_print_positive_reals(void)
assert_print_number("1000000000000", 10e11);
assert_print_number("1.23e+129", 123e+127);
assert_print_number("1.23e-126", 123e-128);
assert_print_number("3.1415926535897931", 3.1415926535897931);
}
static void print_number_should_print_negative_reals(void)