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
34 changed files with 307 additions and 481 deletions

View File

@@ -1,28 +1,3 @@
1.5.8
=====
* Fix `make test` in the Makefile, thanks @YanhaoMo for reporting this (#195)
1.5.7
=====
Fixes:
------
* Fix a bug where realloc failing would return a pointer to an invalid memory address. This is a security issue as it could potentially be used by an attacker to write to arbitrary memory addresses. (see #189), fixed in (954d61e5e7cb9dc6c480fc28ac1cdceca07dd5bd), big thanks @timothyjohncarney for reporting this issue
* Fix a spelling mistake in the AFL fuzzer dictionary (#185), thanks @jwilk
1.5.6
=====
Fixes:
------
* Make cJSON a lot more tolerant about passing NULL pointers to its functions, it should now fail safely instead of dereferencing the pointer. (#183) Thanks @msichal for reporting #182
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 8)
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

@@ -15,7 +15,6 @@ Contributors
* Ian Mobley
* Irwan Djadjadi
* [IvanVoid](https://github.com/npi3pak)
* [Jakub Wilk](https://github.com/jwilk)
* [Jiri Zouhar](https://github.com/loigu)
* [Jonathan Fether](https://github.com/jfether)
* [Julián Vásquez](https://github.com/juvasquezg)

View File

@@ -8,7 +8,7 @@ CJSON_TEST_SRC = cJSON.c test.c
LDLIBS = -lm
LIBVERSION = 1.5.8
LIBVERSION = 1.5.4
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
@@ -66,10 +66,11 @@ shared: $(CJSON_SHARED) $(UTILS_SHARED)
static: $(CJSON_STATIC) $(UTILS_STATIC)
tests: $(CJSON_TEST)
tests: $(CJSON_TEST) $(UTILS_TEST)
test: tests
./$(CJSON_TEST)
./$(UTILS_TEST)
.c.o:
$(CC) -c $(R_CFLAGS) $<
@@ -149,4 +150,4 @@ clean:
$(RM) $(CJSON_OBJ) $(UTILS_OBJ) #delete object files
$(RM) $(CJSON_SHARED) $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) $(CJSON_STATIC) #delete cJSON
$(RM) $(UTILS_SHARED) $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) $(UTILS_STATIC) #delete cJSON_Utils
$(RM) $(CJSON_TEST) #delete test
$(RM) $(CJSON_TEST) $(UTILS_TEST) #delete tests

114
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 != 8)
#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))
@@ -377,14 +376,6 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
{
/* reallocate with realloc if available */
newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
if (newbuffer == NULL)
{
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
}
else
{
@@ -1119,7 +1110,6 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
if (!print_value(item, &p))
{
global_hooks.deallocate(p.buffer);
return NULL;
}
@@ -1130,7 +1120,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const i
{
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
if ((len < 0) || (buf == NULL))
if (len < 0)
{
return false;
}
@@ -1661,25 +1651,17 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
/* Get Array size/item / object item. */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
{
cJSON *child = NULL;
size_t size = 0;
if (array == NULL)
cJSON *c = array->child;
size_t i = 0;
while(c)
{
return 0;
}
child = array->child;
while(child != NULL)
{
size++;
child = child->next;
i++;
c = c->next;
}
/* FIXME: Can overflow here. Cannot be fixed without breaking the API */
return (int)size;
return (int)i;
}
static cJSON* get_array_item(const cJSON *array, size_t index)
@@ -1764,23 +1746,16 @@ static void suffix_object(cJSON *prev, cJSON *item)
/* Utility for handling references. */
static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
{
cJSON *reference = NULL;
if (item == NULL)
cJSON *ref = cJSON_New_Item(hooks);
if (!ref)
{
return NULL;
}
reference = cJSON_New_Item(hooks);
if (reference == NULL)
{
return NULL;
}
memcpy(reference, item, sizeof(cJSON));
reference->string = NULL;
reference->type |= cJSON_IsReference;
reference->next = reference->prev = NULL;
return reference;
memcpy(ref, item, sizeof(cJSON));
ref->string = NULL;
ref->type |= cJSON_IsReference;
ref->next = ref->prev = NULL;
return ref;
}
/* Add item to array/object. */
@@ -1813,11 +1788,6 @@ CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
if (item == NULL)
{
return;
}
/* call cJSON_AddItemToObjectCS for code reuse */
cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item);
/* remove cJSON_StringIsConst flag */
@@ -1834,7 +1804,7 @@ CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSO
/* Add an item to an object with constant string as key */
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
if ((item == NULL) || (string == NULL))
if (!item)
{
return;
}
@@ -1852,21 +1822,11 @@ CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJ
CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
if (array == NULL)
{
return;
}
cJSON_AddItemToArray(array, create_reference(item, &global_hooks));
}
CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
if ((object == NULL) || (string == NULL))
{
return;
}
cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks));
}
@@ -1971,7 +1931,7 @@ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newit
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
{
if ((parent == NULL) || (replacement == NULL) || (item == NULL))
if ((parent == NULL) || (replacement == NULL))
{
return false;
}
@@ -2016,7 +1976,7 @@ CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newi
static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
{
if ((replacement == NULL) || (string == NULL))
if (replacement == NULL)
{
return false;
}
@@ -2179,7 +2139,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
cJSON *p = NULL;
cJSON *a = NULL;
if ((count < 0) || (numbers == NULL))
if (count < 0)
{
return NULL;
}
@@ -2214,7 +2174,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
cJSON *p = NULL;
cJSON *a = NULL;
if ((count < 0) || (numbers == NULL))
if (count < 0)
{
return NULL;
}
@@ -2250,7 +2210,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
cJSON *p = NULL;
cJSON *a = NULL;
if ((count < 0) || (numbers == NULL))
if (count < 0)
{
return NULL;
}
@@ -2286,7 +2246,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count)
cJSON *p = NULL;
cJSON *a = NULL;
if ((count < 0) || (strings == NULL))
if (count < 0)
{
return NULL;
}
@@ -2398,12 +2358,6 @@ fail:
CJSON_PUBLIC(void) cJSON_Minify(char *json)
{
unsigned char *into = (unsigned char*)json;
if (json == NULL)
{
return;
}
while (*json)
{
if (*json == ' ')
@@ -2639,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;
@@ -2666,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;
}

10
cJSON.h
View File

@@ -31,7 +31,7 @@ extern "C"
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 5
#define CJSON_VERSION_PATCH 8
#define CJSON_VERSION_PATCH 4
#include <stddef.h>
@@ -138,10 +138,6 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
@@ -232,6 +228,10 @@ The item->next and ->prev pointers are always zero on return from Duplicate. */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Macros for creating things quickly. */

View File

@@ -162,11 +162,6 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje
size_t child_index = 0;
cJSON *current_child = 0;
if ((object == NULL) || (target == NULL))
{
return NULL;
}
if (object == target)
{
/* found */
@@ -262,12 +257,6 @@ static cJSON_bool decode_array_index_from_pointer(const unsigned char * const po
static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
{
cJSON *current_element = object;
if (pointer == NULL)
{
return NULL;
}
/* follow path of the pointer */
while ((pointer[0] == '/') && (current_element != NULL))
{
@@ -290,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;
@@ -550,10 +538,6 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
{
if (object == NULL)
{
return;
}
object->child = sort_list(object->child, case_sensitive);
}
@@ -958,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;
}
@@ -1043,14 +1020,7 @@ CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, con
static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
{
cJSON *patch = NULL;
if ((patches == NULL) || (operation == NULL) || (path == NULL))
{
return;
}
patch = cJSON_CreateObject();
cJSON *patch = cJSON_CreateObject();
if (patch == NULL)
{
return;
@@ -1228,14 +1198,7 @@ static void create_patches(cJSON * const patches, const unsigned char * const pa
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
{
cJSON *patches = NULL;
if ((from == NULL) || (to == NULL))
{
return NULL;
}
patches = cJSON_CreateArray();
cJSON *patches = cJSON_CreateArray();
create_patches(patches, (const unsigned char*)"", from, to, false);
return patches;
@@ -1243,14 +1206,7 @@ CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * con
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
{
cJSON *patches = NULL;
if ((from == NULL) || (to == NULL))
{
return NULL;
}
patches = cJSON_CreateArray();
cJSON *patches = cJSON_CreateArray();
create_patches(patches, (const unsigned char*)"", from, to, true);
return patches;

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]

View File

@@ -25,8 +25,8 @@ escape_sequence_r="\\r"
escape_sequence_t="\\t"
escape_sequence_quote="\\\""
escape_sequence_backslash="\\\\"
escape_sequence_slash="\\/"
escape_sequence_utf16_base="\\u"
escapce_sequence_slash="\\/"
escpae_sequence_utf16_base="\\u"
escape_sequence_utf16="\\u12ab"
number_integer="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")
@@ -84,8 +92,7 @@ if(ENABLE_CJSON_TEST)
set (cjson_utils_tests
json_patch_tests
old_utils_tests
misc_utils_tests)
old_utils_tests)
foreach (cjson_utils_test ${cjson_utils_tests})
add_executable("${cjson_utils_test}" "${cjson_utils_test}.c")

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

@@ -324,108 +324,6 @@ static void cjson_replace_item_in_object_should_preserve_name(void)
cJSON_Delete(replacement);
}
static void cjson_functions_shouldnt_crash_with_null_pointers(void)
{
char buffer[10];
cJSON *item = cJSON_CreateString("item");
cJSON_InitHooks(NULL);
TEST_ASSERT_NULL(cJSON_Parse(NULL));
TEST_ASSERT_NULL(cJSON_ParseWithOpts(NULL, NULL, true));
TEST_ASSERT_NULL(cJSON_Print(NULL));
TEST_ASSERT_NULL(cJSON_PrintUnformatted(NULL));
TEST_ASSERT_NULL(cJSON_PrintBuffered(NULL, 10, true));
TEST_ASSERT_FALSE(cJSON_PrintPreallocated(NULL, buffer, sizeof(buffer), true));
TEST_ASSERT_FALSE(cJSON_PrintPreallocated(item, NULL, 1, true));
cJSON_Delete(NULL);
cJSON_GetArraySize(NULL);
TEST_ASSERT_NULL(cJSON_GetArrayItem(NULL, 0));
TEST_ASSERT_NULL(cJSON_GetObjectItem(NULL, "item"));
TEST_ASSERT_NULL(cJSON_GetObjectItem(item, NULL));
TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(NULL, "item"));
TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(item, NULL));
TEST_ASSERT_FALSE(cJSON_HasObjectItem(NULL, "item"));
TEST_ASSERT_FALSE(cJSON_HasObjectItem(item, NULL));
TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL));
TEST_ASSERT_FALSE(cJSON_IsFalse(NULL));
TEST_ASSERT_FALSE(cJSON_IsTrue(NULL));
TEST_ASSERT_FALSE(cJSON_IsBool(NULL));
TEST_ASSERT_FALSE(cJSON_IsNull(NULL));
TEST_ASSERT_FALSE(cJSON_IsNumber(NULL));
TEST_ASSERT_FALSE(cJSON_IsString(NULL));
TEST_ASSERT_FALSE(cJSON_IsArray(NULL));
TEST_ASSERT_FALSE(cJSON_IsObject(NULL));
TEST_ASSERT_FALSE(cJSON_IsRaw(NULL));
TEST_ASSERT_NULL(cJSON_CreateString(NULL));
TEST_ASSERT_NULL(cJSON_CreateRaw(NULL));
TEST_ASSERT_NULL(cJSON_CreateIntArray(NULL, 10));
TEST_ASSERT_NULL(cJSON_CreateFloatArray(NULL, 10));
TEST_ASSERT_NULL(cJSON_CreateDoubleArray(NULL, 10));
TEST_ASSERT_NULL(cJSON_CreateStringArray(NULL, 10));
cJSON_AddItemToArray(NULL, item);
cJSON_AddItemToArray(item, NULL);
cJSON_AddItemToObject(item, "item", NULL);
cJSON_AddItemToObject(item, NULL, item);
cJSON_AddItemToObject(NULL, "item", item);
cJSON_AddItemToObjectCS(item, "item", NULL);
cJSON_AddItemToObjectCS(item, NULL, item);
cJSON_AddItemToObjectCS(NULL, "item", item);
cJSON_AddItemReferenceToArray(NULL, item);
cJSON_AddItemReferenceToArray(item, NULL);
cJSON_AddItemReferenceToObject(item, "item", NULL);
cJSON_AddItemReferenceToObject(item, NULL, item);
cJSON_AddItemReferenceToObject(NULL, "item", item);
TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(NULL, item));
TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(item, NULL));
TEST_ASSERT_NULL(cJSON_DetachItemFromArray(NULL, 0));
cJSON_DeleteItemFromArray(NULL, 0);
TEST_ASSERT_NULL(cJSON_DetachItemFromObject(NULL, "item"));
TEST_ASSERT_NULL(cJSON_DetachItemFromObject(item, NULL));
TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(NULL, "item"));
TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(item, NULL));
cJSON_DeleteItemFromObject(NULL, "item");
cJSON_DeleteItemFromObject(item, NULL);
cJSON_DeleteItemFromObjectCaseSensitive(NULL, "item");
cJSON_DeleteItemFromObjectCaseSensitive(item, NULL);
cJSON_InsertItemInArray(NULL, 0, item);
cJSON_InsertItemInArray(item, 0, NULL);
TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(NULL, item, item));
TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, NULL, item));
TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, item, NULL));
cJSON_ReplaceItemInArray(item, 0, NULL);
cJSON_ReplaceItemInArray(NULL, 0, item);
cJSON_ReplaceItemInObject(NULL, "item", item);
cJSON_ReplaceItemInObject(item, NULL, item);
cJSON_ReplaceItemInObject(item, "item", NULL);
cJSON_ReplaceItemInObjectCaseSensitive(NULL, "item", item);
cJSON_ReplaceItemInObjectCaseSensitive(item, NULL, item);
cJSON_ReplaceItemInObjectCaseSensitive(item, "item", NULL);
TEST_ASSERT_NULL(cJSON_Duplicate(NULL, true));
TEST_ASSERT_FALSE(cJSON_Compare(item, NULL, false));
TEST_ASSERT_FALSE(cJSON_Compare(NULL, item, false));
cJSON_Minify(NULL);
/* skipped because it is only used via a macro that checks for NULL */
/* cJSON_SetNumberHelper(NULL, 0); */
cJSON_Delete(item);
}
static void *failing_realloc(void *pointer, size_t size)
{
(void)size;
(void)pointer;
return NULL;
}
static void ensure_should_fail_on_failed_realloc(void)
{
printbuffer buffer = {NULL, 10, 0, 0, false, false, {&malloc, &free, &failing_realloc}};
buffer.buffer = (unsigned char*)malloc(100);
TEST_ASSERT_NOT_NULL(buffer.buffer);
TEST_ASSERT_NULL_MESSAGE(ensure(&buffer, 200), "Ensure didn't fail with failing realloc.");
}
int main(void)
{
UNITY_BEGIN();
@@ -440,7 +338,6 @@ int main(void)
RUN_TEST(cjson_detach_item_via_pointer_should_detach_items);
RUN_TEST(cjson_replace_item_via_pointer_should_replace_items);
RUN_TEST(cjson_replace_item_in_object_should_preserve_name);
RUN_TEST(cjson_functions_shouldnt_crash_with_null_pointers);
RUN_TEST(ensure_should_fail_on_failed_realloc);
return UNITY_END();
}

View File

@@ -1,80 +0,0 @@
/*
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
#include "../cJSON_Utils.h"
static void cjson_utils_functions_shouldnt_crash_with_null_pointers(void)
{
cJSON *item = cJSON_CreateString("item");
TEST_ASSERT_NOT_NULL(item);
TEST_ASSERT_NULL(cJSONUtils_GetPointer(item, NULL));
TEST_ASSERT_NULL(cJSONUtils_GetPointer(NULL, "pointer"));
TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(NULL, "pointer"));
TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(item, NULL));
TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(item, NULL));
TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(NULL, item));
TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(item, NULL));
TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(NULL, item));
cJSONUtils_AddPatchToArray(item, "path", "add", NULL);
cJSONUtils_AddPatchToArray(item, "path", NULL, item);
cJSONUtils_AddPatchToArray(item, NULL, "add", item);
cJSONUtils_AddPatchToArray(NULL, "path", "add", item);
cJSONUtils_ApplyPatches(item, NULL);
cJSONUtils_ApplyPatches(NULL, item);
cJSONUtils_ApplyPatchesCaseSensitive(item, NULL);
cJSONUtils_ApplyPatchesCaseSensitive(NULL, item);
TEST_ASSERT_NULL(cJSONUtils_MergePatch(item, NULL));
item = cJSON_CreateString("item");
TEST_ASSERT_NULL(cJSONUtils_MergePatchCaseSensitive(item, NULL));
item = cJSON_CreateString("item");
/* these calls are actually valid */
/* cJSONUtils_MergePatch(NULL, item); */
/* cJSONUtils_MergePatchCaseSensitive(NULL, item);*/
/* cJSONUtils_GenerateMergePatch(item, NULL); */
/* cJSONUtils_GenerateMergePatch(NULL, item); */
/* cJSONUtils_GenerateMergePatchCaseSensitive(item, NULL); */
/* cJSONUtils_GenerateMergePatchCaseSensitive(NULL, item); */
TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(item, NULL));
TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(NULL, item));
cJSONUtils_SortObject(NULL);
cJSONUtils_SortObjectCaseSensitive(NULL);
cJSON_Delete(item);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(cjson_utils_functions_shouldnt_crash_with_null_pointers);
return UNITY_END();
}

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)