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
30 changed files with 272 additions and 164 deletions

View File

@@ -1,11 +1,3 @@
1.5.5
=====
Fixes:
------
* Fix pointers to nested arrays in cJSON_Utils (9abe75e072050f34732a7169740989a082b65134)
* Fix an error with case sensitivity handling in cJSON_Utils (b9cc911831b0b3e1bb72f142389428e59f882b38)
* Fix cJSON_Compare for arrays that are prefixes of the other and objects that are a subset of the other (03ba72faec115160d1f3aea5582d9b6af5d3e473) See #180, thanks @zhengqb for reporting
1.5.4
=====
Fixes:

View File

@@ -7,7 +7,7 @@ project(cJSON C)
set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 5)
set(PROJECT_VERSION_PATCH 5)
set(PROJECT_VERSION_PATCH 4)
set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
@@ -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

@@ -8,7 +8,7 @@ CJSON_TEST_SRC = cJSON.c test.c
LDLIBS = -lm
LIBVERSION = 1.5.5
LIBVERSION = 1.5.4
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1

27
cJSON.c
View File

@@ -58,7 +58,7 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
}
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 5)
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 4)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
@@ -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))
@@ -2594,22 +2593,16 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
b_element = b_element->next;
}
/* one of the arrays is longer than the other */
if (a_element != b_element) {
return false;
}
return true;
}
case cJSON_Object:
{
cJSON *a_element = NULL;
cJSON *b_element = NULL;
cJSON_ArrayForEach(a_element, a)
{
/* TODO This has O(n^2) runtime, which is horrible! */
b_element = get_object_item(b, a_element->string, case_sensitive);
cJSON *b_element = get_object_item(b, a_element->string, case_sensitive);
if (b_element == NULL)
{
return false;
@@ -2621,22 +2614,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
}
}
/* doing this twice, once on a and b to prevent true comparison if a subset of b
* TODO: Do this the proper way, this is just a fix for now */
cJSON_ArrayForEach(b_element, b)
{
a_element = get_object_item(a, b_element->string, case_sensitive);
if (a_element == NULL)
{
return false;
}
if (!cJSON_Compare(b_element, a_element, case_sensitive))
{
return false;
}
}
return true;
}

View File

@@ -31,7 +31,7 @@ extern "C"
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 5
#define CJSON_VERSION_PATCH 5
#define CJSON_VERSION_PATCH 4
#include <stddef.h>

View File

@@ -279,17 +279,16 @@ static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer,
{
current_element = current_element->next;
}
/* skip to the next path token or end of string */
while ((pointer[0] != '\0') && (pointer[0] != '/'))
{
pointer++;
}
}
else
{
return NULL;
}
/* skip to the next path token or end of string */
while ((pointer[0] != '\0') && (pointer[0] != '/'))
{
pointer++;
}
}
return current_element;
@@ -943,14 +942,7 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_
}
else if (cJSON_IsObject(parent))
{
if (case_sensitive)
{
cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
}
else
{
cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
}
cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
cJSON_AddItemToObject(parent, (char*)child_pointer, value);
value = NULL;
}

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

@@ -148,10 +148,6 @@ static void cjson_compare_should_compare_arrays(void)
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
/* Arrays that are a prefix of another array */
TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", true));
TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", false));
}
static void cjson_compare_should_compare_objects(void)
@@ -175,15 +171,6 @@ static void cjson_compare_should_compare_objects(void)
"{\"Flse\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
false));
/* test objects that are a subset of each other */
TEST_ASSERT_FALSE(compare_from_string(
"{\"one\": 1, \"two\": 2}",
"{\"one\": 1, \"two\": 2, \"three\": 3}",
true))
TEST_ASSERT_FALSE(compare_from_string(
"{\"one\": 1, \"two\": 2}",
"{\"one\": 1, \"two\": 2, \"three\": 3}",
false))
}
int main(void)

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)