diff --git a/.gitattributes b/.gitattributes index 7491fcc7..883895fb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,6 @@ .github export-ignore .editorconfig export-ignore .travis.yml export-ignore + +# Linguist incorrectly identified the headers as C++, manually override this. +*.h linguist-language=C diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc9d17c6..b06ab11d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,7 +31,7 @@ jobs: - name: install build dependencies run: | sudo apt-get update - sudo apt-get install clang-8 valgrind + sudo apt-get install clang-14 valgrind - name: build and test shell: bash run: | diff --git a/.github/workflows/ci-fuzz.yml b/.github/workflows/ci-fuzz.yml new file mode 100644 index 00000000..9e8be0bc --- /dev/null +++ b/.github/workflows/ci-fuzz.yml @@ -0,0 +1,23 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'cjson' + dry-run: false + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'cjson' + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash + uses: actions/upload-artifact@v4 + if: failure() + with: + name: artifacts + path: ./out/artifacts diff --git a/.gitignore b/.gitignore index 58edf92c..65fa89b6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ test *.swp *.patch tags -*.dylib +*.dylib* build/ cJSON_test cJSON_test_utils @@ -16,3 +16,5 @@ libcjson_utils.so.* .vscode .idea cmake-build-debug +*.lst +*.lss diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0f5b2d..2f046e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,61 @@ +1.7.19 (Sep 9, 2025) +====== +Fixes: +------ +* Fix indentation (should use spaces), see #814 +* Fix spelling errors found by CodeSpell, see #841 +* Check for NULL in cJSON_DetachItemViaPointer, fixes #882, see #886 +* Fix #881, check overlap before calling strcpy in cJSON_SetValuestring, see #885 +* Fix #880 Max recursion depth for cJSON_Duplicate to prevent stack exhaustion, see #888 +* Allocate memory for the temporary buffer when paring numbers, see #939 +* fix the incorrect check in decode_array_index_from_pointer, see #957 + +1.7.18 (May 13, 2024) +====== +Fixes: +------ +* Add NULL check to cJSON_SetValuestring()(CVE-2024-31755), see #839 and #840 +* Remove non-functional list handling of compiler flags, see #851 +* Fix heap buffer overflow, see #852 +* remove misused optimization flag -01, see #854 +* Set free'd pointers to NULL whenever they are not reassigned immediately after, see #855 and #833 + +1.7.17 (Dec 26, 2023) +====== +Fixes: +------ +* Fix null reference in cJSON_SetValuestring(CVE-2023-50472), see #809 +* Fix null reference in cJSON_InsertItemInArray(CVE-2023-50471), see #809 and #810 + +1.7.16 (Jul 5, 2023) +====== +Features: +------ +* Add an option for ENABLE_CJSON_VERSION_SO in CMakeLists.txt, see #534 +* Add cmake_policy to CMakeLists.txt, see #163 +* Add cJSON_SetBoolValue, see #639 +* Add meson documentation, see #761 + +Fixes: +------ +* Fix memory leak in merge_patch, see #611 +* Fix conflicting target names 'uninstall', see #617 +* Bump cmake version to 3.0 and use new version syntax, see #587 +* Print int without decimal places, see #630 +* Fix 'cjson_utils-static' target not exist, see #625 +* Add allocate check for replace_item_in_object, see #675 +* Fix a null pointer crash in cJSON_ReplaceItemViaPointer, see #726 + +1.7.15 (Aug 25, 2021) +====== +Fixes: +------ +* Fix potential core dumped for strrchr, see [#546](https://github.com/DaveGamble/cJSON/pull/546) +* Fix null pointer crash in cJSON_CreateXxArray, see [#538](https://github.com/DaveGamble/cJSON/pull/538) +* Fix several null pointer problems on allocation failure, see [#526](https://github.com/DaveGamble/cJSON/pull/526) +* Fix a possible dereference of null pointer, see [#519](https://github.com/DaveGamble/cJSON/pull/519) +* Fix windows build failure about defining nan, see [#518](https://github.com/DaveGamble/cJSON/pull/518) + 1.7.14 (Sep 3, 2020) ====== Fixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index e8c96342..c7ca27f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,16 @@ set(CMAKE_LEGACY_CYGWIN_WIN32 0) -cmake_minimum_required(VERSION 2.8.5) +cmake_minimum_required(VERSION 3.0) -project(cJSON C) +project(cJSON + VERSION 1.7.19 + LANGUAGES C) + +cmake_policy(SET CMP0054 NEW) # set CMP0054 policy include(GNUInstallDirs) -set(PROJECT_VERSION_MAJOR 1) -set(PROJECT_VERSION_MINOR 7) -set(PROJECT_VERSION_PATCH 14) set(CJSON_VERSION_SO 1) set(CJSON_UTILS_VERSION_SO 1) -set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") - set(custom_compiler_flags) @@ -71,7 +70,6 @@ if (ENABLE_SANITIZERS) -fsanitize=float-cast-overflow -fsanitize-address-use-after-scope -fsanitize=integer - -01 -fno-sanitize-recover ) endif() @@ -103,13 +101,10 @@ foreach(compiler_flag ${custom_compiler_flags}) CHECK_C_COMPILER_FLAG(${compiler_flag} "FLAG_SUPPORTED_${current_variable}") if (FLAG_SUPPORTED_${current_variable}) - list(APPEND supported_compiler_flags) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${compiler_flag}") endif() endforeach() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${supported_compiler_flags}") - option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(ENABLE_TARGET_EXPORT "Enable exporting of CMake targets. Disable when it causes problems!" ON) @@ -122,6 +117,7 @@ set(SOURCES cJSON.c) option(BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static libraries" Off) option(CJSON_OVERRIDE_BUILD_SHARED_LIBS "Override BUILD_SHARED_LIBS with CJSON_BUILD_SHARED_LIBS" OFF) option(CJSON_BUILD_SHARED_LIBS "Overrides BUILD_SHARED_LIBS if CJSON_OVERRIDE_BUILD_SHARED_LIBS is enabled" ON) +option(ENABLE_CJSON_VERSION_SO "Enables cJSON so version" ON) if ((CJSON_OVERRIDE_BUILD_SHARED_LIBS AND CJSON_BUILD_SHARED_LIBS) OR ((NOT CJSON_OVERRIDE_BUILD_SHARED_LIBS) AND BUILD_SHARED_LIBS)) set(CJSON_LIBRARY_TYPE SHARED) @@ -156,17 +152,23 @@ install(TARGETS "${CJSON_LIB}" INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" ) if (BUILD_SHARED_AND_STATIC_LIBS) - install(TARGETS "${CJSON_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}") + install(TARGETS "${CJSON_LIB}-static" + EXPORT "${CJSON_LIB}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" +) endif() if(ENABLE_TARGET_EXPORT) # export library information for CMake projects install(EXPORT "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON") endif() -set_target_properties("${CJSON_LIB}" - PROPERTIES - SOVERSION "${CJSON_VERSION_SO}" - VERSION "${PROJECT_VERSION}") +if(ENABLE_CJSON_VERSION_SO) + set_target_properties("${CJSON_LIB}" + PROPERTIES + SOVERSION "${CJSON_VERSION_SO}" + VERSION "${PROJECT_VERSION}") +endif() #cJSON_Utils option(ENABLE_CJSON_UTILS "Enable building the cJSON_Utils library." OFF) @@ -199,7 +201,11 @@ if(ENABLE_CJSON_UTILS) INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" ) if (BUILD_SHARED_AND_STATIC_LIBS) - install(TARGETS "${CJSON_UTILS_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}") + install(TARGETS "${CJSON_UTILS_LIB}-static" + EXPORT "${CJSON_UTILS_LIB}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" + ) endif() install(FILES cJSON_Utils.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson") install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig") @@ -208,10 +214,12 @@ if(ENABLE_CJSON_UTILS) install(EXPORT "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON") endif() - set_target_properties("${CJSON_UTILS_LIB}" - PROPERTIES - SOVERSION "${CJSON_UTILS_VERSION_SO}" - VERSION "${PROJECT_VERSION}") + if(ENABLE_CJSON_VERSION_SO) + set_target_properties("${CJSON_UTILS_LIB}" + PROPERTIES + SOVERSION "${CJSON_UTILS_VERSION_SO}" + VERSION "${PROJECT_VERSION}") + endif() endif() # create the other package config files @@ -255,7 +263,11 @@ if(ENABLE_CJSON_TEST) endif() #Create the uninstall target -add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_SOURCE_DIR}/library_config/uninstall.cmake") +option(ENABLE_CJSON_UNINSTALL "Enable creating uninstall target" ON) +if(ENABLE_CJSON_UNINSTALL) + add_custom_target(uninstall "${CMAKE_COMMAND}" -P + "${PROJECT_SOURCE_DIR}/library_config/uninstall.cmake") +endif() # Enable the use of locales option(ENABLE_LOCALES "Enable the use of locales" ON) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f113ab22..b9db178a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -10,8 +10,10 @@ Current Maintainer: Contributors: * [Ajay Bhargav](https://github.com/ajaybhargav) +* [AlexanderVasiljev](https://github.com/AlexanderVasiljev) * [Alper Akcan](https://github.com/alperakcan) * [Andrew Tang](https://github.com/singku) +* [Andy](https://github.com/mlh0101) * [Anton Sergeev](https://github.com/anton-sergeev) * [Benbuck Nason](https://github.com/bnason-nf) * [Bernt Johan Damslora](https://github.com/bjda) @@ -22,6 +24,7 @@ Contributors: * [Debora Grosse](https://github.com/DeboraG) * [dieyushi](https://github.com/dieyushi) * [Dōngwén Huáng (黄东文)](https://github.com/DongwenHuang) +* [Dominik](https://github.com/DL6ER) * [Donough Liu](https://github.com/ldm0) * [Erez Oxman](https://github.com/erez-o) * Eswar Yaganti @@ -29,25 +32,33 @@ Contributors: * [Fabrice Fontaine](https://github.com/ffontaine) * Ian Mobley * Irwan Djadjadi +* [hopper-vul](https://github.com/hopper-vul) * [HuKeping](https://github.com/HuKeping) * [IvanVoid](https://github.com/npi3pak) * [Jakub Wilk](https://github.com/jwilk) * [Jiri Zouhar](https://github.com/loigu) * [Jonathan Fether](https://github.com/jfether) +* [Joshua Arulsamy](https://github.com/jarulsamy) * [Julian Ste](https://github.com/julian-st) * [Julián Vásquez](https://github.com/juvasquezg) +* [Junbo Zheng](https://github.com/Junbo-Zheng) * [Kevin Branigan](https://github.com/kbranigan) * [Kevin Sapper](https://github.com/sappo) * [Kyle Chisholm](https://github.com/ChisholmKyle) * [Linus Wallgren](https://github.com/ecksun) +* [Luo Jin](https://github.com/Up-wind) +* [Max](https://github.com/maebex) +* [MaxBrandtner](https://github.com/MaxBrandtner) * [Mateusz Szafoni](https://github.com/raiden00pl) * Mike Pontillo * [miaoerduo](https://github.com/miaoerduo) +* [mohawk2](https://github.com/mohawk2) * [Mike Jerris](https://github.com/mjerris) * [Mike Robinson](https://github.com/mhrobinson) * [Moorthy](https://github.com/moorthy-bs) * [myd7349](https://github.com/myd7349) * [NancyLi1013](https://github.com/NancyLi1013) +* [Orri](https://github.com/sbvoxel) * Paulo Antonio Alvarez * [Paweł Malowany](https://github.com/PawelMalowany) * [Pawel Winogrodzki](https://github.com/PawelWMS) @@ -61,11 +72,17 @@ Contributors: * [Romain Porte](https://github.com/MicroJoe) * [SANJEEV BA](https://github.com/basanjeev) * [Sang-Heon Jeon](https://github.com/lntuition) +* [Sayan Bandyopadhyay](https://github.com/saynb) * [Simon Sobisch](https://github.com/GitMensch) * [Simon Ricaldone](https://github.com/simon-p-r) +* [Stoian Ivanov](https://github.com/sdrsdr) +* [SuperH-0630](https://github.com/SuperH-0630) * [Square789](https://github.com/Square789) * [Stephan Gatzka](https://github.com/gatzka) +* [Tony Langhammer](https://github.com/BigBrainAFK) * [Vemake](https://github.com/vemakereporter) +* [vwvw](https://github.com/vwvw) +* [warmsocks](https://github.com/warmsocks) * [Wei Tan](https://github.com/tan-wei) * [Weston Schmidt](https://github.com/schmidtw) * [xiaomianhehe](https://github.com/xiaomianhehe) @@ -73,6 +90,7 @@ Contributors: * [yuta-oxo](https://github.com/yuta-oxo) * [Zach Hindes](https://github.com/zhindes) * [Zhao Zhixu](https://github.com/zhaozhixu) +* [10km](https://github.com/10km) And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0) diff --git a/Makefile b/Makefile index b1432305..e2676d2f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CJSON_TEST_SRC = cJSON.c test.c LDLIBS = -lm -LIBVERSION = 1.7.14 +LIBVERSION = 1.7.19 CJSON_SOVERSION = 1 UTILS_SOVERSION = 1 diff --git a/README.md b/README.md index cd18c7c5..99147afd 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Ultralightweight JSON parser in ANSI C. * [Copying the source](#copying-the-source) * [CMake](#cmake) * [Makefile](#makefile) + * [Meson](#meson) * [Vcpkg](#Vcpkg) * [Including cJSON](#including-cjson) * [Data Structure](#data-structure) @@ -118,6 +119,7 @@ You can change the build process with a list of different options that you can p * `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation. * `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default ) * `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`. +* `-DENABLE_CJSON_VERSION_SO`: Enable cJSON so version. ( on by default ) If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example: ``` @@ -144,6 +146,23 @@ make all If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. And uninstall them with: `make PREFIX=/usr DESTDIR=temp uninstall`. +#### Meson + +To make cjson work in a project using meson, the libcjson dependency has to be included: + +```meson +project('c-json-example', 'c') + +cjson = dependency('libcjson') + +example = executable( + 'example', + 'example.c', + dependencies: [cjson], +) +``` + + #### Vcpkg You can download and install cJSON using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: @@ -406,7 +425,7 @@ end: } ``` -Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lifes a little easier: +Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lives a little easier: ```c //NOTE: Returns a heap allocated string, you are required to free it after use. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..33d99a2e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported Versions + +Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to cjson team privately, to minimize attacks against current users of cjson before they are fixed. Vulnerabilities will be investigated and patched on the next patch (or minor) release as soon as possible. This information could be kept entirely internal to the project. + +## Reporting a Vulnerability + +If you know of a publicly disclosed security vulnerability for cjson, please IMMEDIATELY contact wp_scut@163.com and peterlee@apache.org to inform the cjson Team. + +IMPORTANT: Do not file public issues on GitHub for security vulnerabilities. diff --git a/cJSON.c b/cJSON.c index 4c6a308e..6e4fb0dd 100644 --- a/cJSON.c +++ b/cJSON.c @@ -78,8 +78,12 @@ #endif #ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else #define NAN 0.0/0.0 #endif +#endif typedef struct { const unsigned char *json; @@ -92,9 +96,9 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) { - if (!cJSON_IsString(item)) + if (!cJSON_IsString(item)) { return NULL; } @@ -102,9 +106,9 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) { - if (!cJSON_IsNumber(item)) + if (!cJSON_IsNumber(item)) { return (double) NAN; } @@ -113,7 +117,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 19) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif @@ -259,10 +263,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); + item->string = NULL; } global_hooks.deallocate(item); item = next; @@ -302,9 +308,11 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu { double number = 0; unsigned char *after_end = NULL; - unsigned char number_c_string[64]; + unsigned char *number_c_string; unsigned char decimal_point = get_decimal_point(); size_t i = 0; + size_t number_string_length = 0; + cJSON_bool has_decimal_point = false; if ((input_buffer == NULL) || (input_buffer->content == NULL)) { @@ -314,7 +322,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu /* copy the number into a temporary buffer and replace '.' with the decimal point * of the current locale (for strtod) * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + for (i = 0; can_access_at_index(input_buffer, i); i++) { switch (buffer_at_offset(input_buffer)[i]) { @@ -332,11 +340,12 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu case '-': case 'e': case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; + number_string_length++; break; case '.': - number_c_string[i] = decimal_point; + number_string_length++; + has_decimal_point = true; break; default: @@ -344,11 +353,33 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu } } loop_end: - number_c_string[i] = '\0'; + /* malloc for temporary buffer, add 1 for '\0' */ + number_c_string = (unsigned char *) input_buffer->hooks.allocate(number_string_length + 1); + if (number_c_string == NULL) + { + return false; /* allocation failure */ + } + + memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length); + number_c_string[number_string_length] = '\0'; + + if (has_decimal_point) + { + for (i = 0; i < number_string_length; i++) + { + if (number_c_string[i] == '.') + { + /* replace '.' with the decimal point of the current locale (for strtod) */ + number_c_string[i] = decimal_point; + } + } + } number = strtod((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return false; /* parse_error */ } @@ -371,6 +402,8 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return true; } @@ -393,16 +426,33 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) { char *copy = NULL; + size_t v1_len; + size_t v2_len; /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { return NULL; } - if (strlen(valuestring) <= strlen(object->valuestring)) + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) { + return NULL; + } + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) + { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { + return NULL; + } strcpy(object->valuestring, valuestring); return object->valuestring; } @@ -507,10 +557,8 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) return NULL; } - if (newbuffer) - { - memcpy(newbuffer, p->buffer, p->offset + 1); - } + + memcpy(newbuffer, p->buffer, p->offset + 1); p->hooks.deallocate(p->buffer); } p->length = newsize; @@ -560,6 +608,10 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ @@ -882,6 +934,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu if (output != NULL) { input_buffer->hooks.deallocate(output); + output = NULL; } if (input_pointer != NULL) @@ -1101,7 +1154,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer } buffer.content = (const unsigned char*)value; - buffer.length = buffer_length; + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -1224,6 +1277,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } return printed; @@ -1232,11 +1286,13 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } if (printed != NULL) { hooks->deallocate(printed); + printed = NULL; } return NULL; @@ -1277,6 +1333,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); + p.buffer = NULL; return NULL; } @@ -1648,6 +1705,11 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu current_item = new_item; } + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); @@ -2180,7 +2242,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * c CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) { - if ((parent == NULL) || (item == NULL)) + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) { return NULL; } @@ -2258,7 +2320,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON { cJSON *after_inserted = NULL; - if (which < 0) + if (which < 0 || newitem == NULL) { return false; } @@ -2269,6 +2331,11 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON return add_item_to_array(array, newitem); } + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + newitem->next = after_inserted; newitem->prev = after_inserted->prev; after_inserted->prev = newitem; @@ -2285,7 +2352,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { - if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { return false; } @@ -2355,6 +2422,11 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO cJSON_free(replacement->string); } replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); @@ -2544,6 +2616,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) } a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); @@ -2562,7 +2635,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) } p = n; } - a->child->prev = n; + + if (a && a->child) { + a->child->prev = n; + } return a; } @@ -2599,7 +2675,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) } p = n; } - a->child->prev = n; + + if (a && a->child) { + a->child->prev = n; + } return a; } @@ -2618,7 +2697,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) a = cJSON_CreateArray(); - for(i = 0;a && (i < (size_t)count); i++) + for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if(!n) @@ -2636,7 +2715,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) } p = n; } - a->child->prev = n; + + if (a && a->child) { + a->child->prev = n; + } return a; } @@ -2673,13 +2755,23 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co } p = n; } - a->child->prev = n; + + if (a && a->child) { + a->child->prev = n; + } return a; } /* Duplication */ +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + return cJSON_Duplicate_rec(item, 0, recurse ); +} + +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) { cJSON *newitem = NULL; cJSON *child = NULL; @@ -2726,7 +2818,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) child = item->child; while (child != NULL) { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(depth >= CJSON_CIRCULAR_LIMIT) { + goto fail; + } + newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) { goto fail; @@ -2961,7 +3056,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) { - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } @@ -3092,4 +3187,5 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size) CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); + object = NULL; } diff --git a/cJSON.h b/cJSON.h index e97e5f4c..cab5feb4 100644 --- a/cJSON.h +++ b/cJSON.h @@ -81,7 +81,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 14 +#define CJSON_VERSION_PATCH 19 #include @@ -137,6 +137,12 @@ typedef int cJSON_bool; #define CJSON_NESTING_LIMIT 1000 #endif +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); @@ -256,7 +262,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. * The input pointer json cannot point to a read-only address area, such as a string constant, - * but should point to a readable and writable adress area. */ + * but should point to a readable and writable address area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. @@ -279,6 +285,13 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) diff --git a/cJSON_Utils.c b/cJSON_Utils.c index f4ad32a5..8fa24f8e 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -282,7 +282,7 @@ static cJSON_bool decode_array_index_from_pointer(const unsigned char * const po return 0; } - for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + for (position = 0; (pointer[position] >= '0') && (pointer[position] <= '9'); position++) { parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); @@ -960,7 +960,9 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_ /* split pointer in parent and child */ parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); - child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (parent_pointer) { + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + } if (child_pointer != NULL) { child_pointer[0] = '\0'; @@ -1365,6 +1367,7 @@ static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_ replacement = merge_patch(replace_me, patch_child, case_sensitive); if (replacement == NULL) { + cJSON_Delete(target); return NULL; } @@ -1406,6 +1409,10 @@ static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const c from_child = from->child; to_child = to->child; patch = cJSON_CreateObject(); + if (patch == NULL) + { + return NULL; + } while (from_child || to_child) { int diff; diff --git a/tests/cjson_add.c b/tests/cjson_add.c index 00ffc349..ac96ce75 100644 --- a/tests/cjson_add.c +++ b/tests/cjson_add.c @@ -34,7 +34,7 @@ static void * CJSON_CDECL failing_malloc(size_t size) return NULL; } -/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ static void CJSON_CDECL normal_free(void *pointer) { free(pointer); @@ -117,6 +117,50 @@ static void cjson_add_true_should_fail_on_allocation_failure(void) cJSON_Delete(root); } +static void cjson_create_int_array_should_fail_on_allocation_failure(void) +{ + int numbers[] = {1, 2, 3}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateIntArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_float_array_should_fail_on_allocation_failure(void) +{ + float numbers[] = {1.0f, 2.0f, 3.0f}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateFloatArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_double_array_should_fail_on_allocation_failure(void) +{ + double numbers[] = {1.0, 2.0, 3.0}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateDoubleArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_string_array_should_fail_on_allocation_failure(void) +{ + const char* strings[] = {"1", "2", "3"}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateStringArray(strings, 3)); + + cJSON_InitHooks(NULL); +} + static void cjson_add_false_should_add_false(void) { cJSON *root = cJSON_CreateObject(); @@ -390,6 +434,11 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_add_true_should_fail_with_null_pointers); RUN_TEST(cjson_add_true_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_int_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_float_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_double_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_string_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_add_false_should_add_false); RUN_TEST(cjson_add_false_should_fail_with_null_pointers); RUN_TEST(cjson_add_false_should_fail_on_allocation_failure); diff --git a/tests/json_patch_tests.c b/tests/json_patch_tests.c index c2c88a4f..7c3d6aed 100644 --- a/tests/json_patch_tests.c +++ b/tests/json_patch_tests.c @@ -66,7 +66,7 @@ static cJSON_bool test_apply_patch(const cJSON * const test) } else { - printf("Testing unkown\n"); + printf("Testing unknown\n"); } disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled"); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 3bf0a1cc..7c616479 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -28,7 +28,6 @@ #include "unity/src/unity.h" #include "common.h" - static void cjson_array_foreach_should_loop_over_arrays(void) { cJSON array[1]; @@ -77,7 +76,6 @@ static void cjson_get_object_item_should_get_object_items(void) found = cJSON_GetObjectItem(item, NULL); TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string."); - found = cJSON_GetObjectItem(item, "one"); TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1); @@ -127,7 +125,8 @@ static void cjson_get_object_item_case_sensitive_should_get_object_items(void) cJSON_Delete(item); } -static void cjson_get_object_item_should_not_crash_with_array(void) { +static void cjson_get_object_item_should_not_crash_with_array(void) +{ cJSON *array = NULL; cJSON *found = NULL; array = cJSON_Parse("[1]"); @@ -138,7 +137,8 @@ static void cjson_get_object_item_should_not_crash_with_array(void) { cJSON_Delete(array); } -static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void) { +static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void) +{ cJSON *array = NULL; cJSON *found = NULL; array = cJSON_Parse("[1]"); @@ -219,6 +219,23 @@ static void cjson_should_not_parse_to_deeply_nested_jsons(void) TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed."); } +static void cjson_should_not_follow_too_deep_circular_references(void) +{ + cJSON *o = cJSON_CreateArray(); + cJSON *a = cJSON_CreateArray(); + cJSON *b = cJSON_CreateArray(); + cJSON *x; + + cJSON_AddItemToArray(o, a); + cJSON_AddItemToArray(a, b); + cJSON_AddItemToArray(b, o); + + x = cJSON_Duplicate(o, 1); + TEST_ASSERT_NULL(x); + cJSON_DetachItemFromArray(b, 0); + cJSON_Delete(o); +} + static void cjson_set_number_value_should_set_numbers(void) { cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}}; @@ -280,6 +297,21 @@ static void cjson_detach_item_via_pointer_should_detach_items(void) TEST_ASSERT_NULL_MESSAGE(parent->child, "Child of the parent wasn't set to NULL."); } +static void cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null(void) +{ + cJSON list[2]; + cJSON parent[1]; + + memset(list, '\0', sizeof(list)); + + /* link the list */ + list[0].next = &(list[1]); + + parent->child = &list[0]; + TEST_ASSERT_NULL_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[1])), "Failed to detach in the middle."); + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[0])) == &(list[0]), "Failed to detach in the middle."); +} + static void cjson_replace_item_via_pointer_should_replace_items(void) { cJSON replacements[3]; @@ -302,7 +334,6 @@ static void cjson_replace_item_via_pointer_should_replace_items(void) cJSON_AddItemToArray(array, middle); cJSON_AddItemToArray(array, end); - memset(replacements, '\0', sizeof(replacements)); /* replace beginning */ @@ -329,7 +360,7 @@ static void cjson_replace_item_via_pointer_should_replace_items(void) static void cjson_replace_item_in_object_should_preserve_name(void) { - cJSON root[1] = {{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }}; + cJSON root[1] = {{NULL, NULL, NULL, 0, NULL, 0, 0, NULL}}; cJSON *child = NULL; cJSON *replacement = NULL; cJSON_bool flag = false; @@ -339,7 +370,7 @@ static void cjson_replace_item_in_object_should_preserve_name(void) replacement = cJSON_CreateNumber(2); TEST_ASSERT_NOT_NULL(replacement); - flag = cJSON_AddItemToObject(root, "child", child); + flag = cJSON_AddItemToObject(root, "child", child); TEST_ASSERT_TRUE_MESSAGE(flag, "add item to object failed"); cJSON_ReplaceItemInObject(root, "child", replacement); @@ -353,6 +384,19 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) { char buffer[10]; cJSON *item = cJSON_CreateString("item"); + cJSON *array = cJSON_CreateArray(); + cJSON *item1 = cJSON_CreateString("item1"); + cJSON *item2 = cJSON_CreateString("corrupted array item3"); + cJSON *corruptedString = cJSON_CreateString("corrupted"); + struct cJSON *originalPrev; + + add_item_to_array(array, item1); + add_item_to_array(array, item2); + + originalPrev = item2->prev; + item2->prev = NULL; + free(corruptedString->valuestring); + corruptedString->valuestring = NULL; cJSON_InitHooks(NULL); TEST_ASSERT_NULL(cJSON_Parse(NULL)); @@ -412,6 +456,8 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) cJSON_DeleteItemFromObject(item, NULL); cJSON_DeleteItemFromObjectCaseSensitive(NULL, "item"); cJSON_DeleteItemFromObjectCaseSensitive(item, NULL); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(array, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(array, 1, item)); TEST_ASSERT_FALSE(cJSON_InsertItemInArray(NULL, 0, item)); TEST_ASSERT_FALSE(cJSON_InsertItemInArray(item, 0, NULL)); TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(NULL, item, item)); @@ -428,14 +474,39 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) TEST_ASSERT_NULL(cJSON_Duplicate(NULL, true)); TEST_ASSERT_FALSE(cJSON_Compare(item, NULL, false)); TEST_ASSERT_FALSE(cJSON_Compare(NULL, item, false)); + TEST_ASSERT_NULL(cJSON_SetValuestring(NULL, "test")); + TEST_ASSERT_NULL(cJSON_SetValuestring(corruptedString, "test")); + TEST_ASSERT_NULL(cJSON_SetValuestring(item, NULL)); cJSON_Minify(NULL); /* skipped because it is only used via a macro that checks for NULL */ /* cJSON_SetNumberHelper(NULL, 0); */ + /* restore corrupted item2 to delete it */ + item2->prev = originalPrev; + cJSON_Delete(corruptedString); + cJSON_Delete(array); cJSON_Delete(item); } -static void * CJSON_CDECL failing_realloc(void *pointer, size_t size) +static void cjson_set_valuestring_should_return_null_if_strings_overlap(void) +{ + cJSON *obj; + char* str; + char* str2; + + obj = cJSON_Parse("\"foo0z\""); + + str = cJSON_SetValuestring(obj, "abcde"); + str += 1; + /* The string passed to strcpy overlap which is not allowed.*/ + str2 = cJSON_SetValuestring(obj, str); + /* If it overlaps, the string will be messed up.*/ + TEST_ASSERT_TRUE(strcmp(str, "bcde") == 0); + TEST_ASSERT_NULL(str2); + cJSON_Delete(obj); +} + +static void *CJSON_CDECL failing_realloc(void *pointer, size_t size) { (void)size; (void)pointer; @@ -445,7 +516,7 @@ static void * CJSON_CDECL failing_realloc(void *pointer, size_t size) 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); + 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."); @@ -454,7 +525,7 @@ static void ensure_should_fail_on_failed_realloc(void) static void skip_utf8_bom_should_skip_bom(void) { const unsigned char string[] = "\xEF\xBB\xBF{}"; - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; buffer.content = string; buffer.length = sizeof(string); buffer.hooks = global_hooks; @@ -466,7 +537,7 @@ static void skip_utf8_bom_should_skip_bom(void) static void skip_utf8_bom_should_not_skip_bom_if_not_at_beginning(void) { const unsigned char string[] = " \xEF\xBB\xBF{}"; - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; buffer.content = string; buffer.length = sizeof(string); buffer.hooks = global_hooks; @@ -496,12 +567,13 @@ static void cjson_get_number_value_should_get_a_number(void) TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble); TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string)); TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL)); - + cJSON_Delete(number); cJSON_Delete(string); } -static void cjson_create_string_reference_should_create_a_string_reference(void) { +static void cjson_create_string_reference_should_create_a_string_reference(void) +{ const char *string = "I am a string!"; cJSON *string_reference = cJSON_CreateStringReference(string); @@ -511,7 +583,8 @@ static void cjson_create_string_reference_should_create_a_string_reference(void) cJSON_Delete(string_reference); } -static void cjson_create_object_reference_should_create_an_object_reference(void) { +static void cjson_create_object_reference_should_create_an_object_reference(void) +{ cJSON *number_reference = NULL; cJSON *number_object = cJSON_CreateObject(); cJSON *number = cJSON_CreateNumber(42); @@ -529,7 +602,8 @@ static void cjson_create_object_reference_should_create_an_object_reference(void cJSON_Delete(number_reference); } -static void cjson_create_array_reference_should_create_an_array_reference(void) { +static void cjson_create_array_reference_should_create_an_array_reference(void) +{ cJSON *number_reference = NULL; cJSON *number_array = cJSON_CreateArray(); cJSON *number = cJSON_CreateNumber(42); @@ -566,7 +640,7 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al { cJSON *object = cJSON_CreateObject(); cJSON *number = cJSON_CreateNumber(42); - char *name = (char*)cJSON_strdup((const unsigned char*)"number", &global_hooks); + char *name = (char *)cJSON_strdup((const unsigned char *)"number", &global_hooks); TEST_ASSERT_NOT_NULL(object); TEST_ASSERT_NOT_NULL(number); @@ -626,7 +700,7 @@ static void cjson_set_valuestring_to_object_should_not_leak_memory(void) cJSON *item2 = cJSON_CreateStringReference(reference_valuestring); char *ptr1 = NULL; char *return_value = NULL; - + cJSON_AddItemToObject(root, "one", item1); cJSON_AddItemToObject(root, "two", item2); @@ -650,6 +724,80 @@ static void cjson_set_valuestring_to_object_should_not_leak_memory(void) cJSON_Delete(root); } +static void cjson_set_bool_value_must_not_break_objects(void) +{ + cJSON *bobj, *sobj, *oobj, *refobj = NULL; + + TEST_ASSERT_TRUE((cJSON_SetBoolValue(refobj, 1) == cJSON_Invalid)); + + bobj = cJSON_CreateFalse(); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 1) == cJSON_True)); + TEST_ASSERT_TRUE(cJSON_IsTrue(bobj)); + cJSON_SetBoolValue(bobj, 1); + TEST_ASSERT_TRUE(cJSON_IsTrue(bobj)); + TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 0) == cJSON_False)); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + cJSON_SetBoolValue(bobj, 0); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + + sobj = cJSON_CreateString("test"); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + cJSON_SetBoolValue(sobj, 1); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + cJSON_SetBoolValue(sobj, 0); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + + oobj = cJSON_CreateObject(); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + cJSON_SetBoolValue(oobj, 1); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + cJSON_SetBoolValue(oobj, 0); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + + refobj = cJSON_CreateStringReference("conststring"); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 1); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 0); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_Delete(refobj); + + refobj = cJSON_CreateObjectReference(oobj); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 1); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 0); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_Delete(refobj); + + cJSON_Delete(oobj); + cJSON_Delete(bobj); + cJSON_Delete(sobj); +} + +static void cjson_parse_big_numbers_should_not_report_error(void) +{ + cJSON *valid_big_number_json_object1 = cJSON_Parse("{\"a\": true, \"b\": [ null,9999999999999999999999999999999999999999999999912345678901234567]}"); + cJSON *valid_big_number_json_object2 = cJSON_Parse("{\"a\": true, \"b\": [ null,999999999999999999999999999999999999999999999991234567890.1234567E3]}"); + const char *invalid_big_number_json1 = "{\"a\": true, \"b\": [ null,99999999999999999999999999999999999999999999999.1234567890.1234567]}"; + const char *invalid_big_number_json2 = "{\"a\": true, \"b\": [ null,99999999999999999999999999999999999999999999999E1234567890e1234567]}"; + + TEST_ASSERT_NOT_NULL(valid_big_number_json_object1); + TEST_ASSERT_NOT_NULL(valid_big_number_json_object2); + TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(invalid_big_number_json1), "Invalid big number JSONs should not be parsed."); + TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(invalid_big_number_json2), "Invalid big number JSONs should not be parsed."); + + cJSON_Delete(valid_big_number_json_object1); + cJSON_Delete(valid_big_number_json_object2); +} + int CJSON_CDECL main(void) { UNITY_BEGIN(); @@ -662,11 +810,14 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_get_object_item_case_sensitive_should_not_crash_with_array); RUN_TEST(typecheck_functions_should_check_type); RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons); + RUN_TEST(cjson_should_not_follow_too_deep_circular_references); RUN_TEST(cjson_set_number_value_should_set_numbers); RUN_TEST(cjson_detach_item_via_pointer_should_detach_items); + RUN_TEST(cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null); RUN_TEST(cjson_replace_item_via_pointer_should_replace_items); RUN_TEST(cjson_replace_item_in_object_should_preserve_name); RUN_TEST(cjson_functions_should_not_crash_with_null_pointers); + RUN_TEST(cjson_set_valuestring_should_return_null_if_strings_overlap); RUN_TEST(ensure_should_fail_on_failed_realloc); RUN_TEST(skip_utf8_bom_should_skip_bom); RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning); @@ -679,6 +830,8 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased); RUN_TEST(cjson_delete_item_from_array_should_not_broken_list_structure); RUN_TEST(cjson_set_valuestring_to_object_should_not_leak_memory); + RUN_TEST(cjson_set_bool_value_must_not_break_objects); + RUN_TEST(cjson_parse_big_numbers_should_not_report_error); return UNITY_END(); } diff --git a/tests/parse_examples.c b/tests/parse_examples.c index 95a09590..d35d6cfb 100644 --- a/tests/parse_examples.c +++ b/tests/parse_examples.c @@ -250,6 +250,33 @@ static void test14_should_not_be_parsed(void) } } +/* Address Sanitizer */ +static void test15_should_not_heap_buffer_overflow(void) +{ + const char *strings[] = { + "{\"1\":1,", + "{\"1\":1, ", + }; + + size_t i; + + for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i+=1) + { + const char *json_string = strings[i]; + size_t len = strlen(json_string); + cJSON *json = NULL; + + char *exact_size_heap = (char*)malloc(len); + TEST_ASSERT_NOT_NULL(exact_size_heap); + + memcpy(exact_size_heap, json_string, len); + json = cJSON_ParseWithLength(exact_size_heap, len); + + cJSON_Delete(json); + free(exact_size_heap); + } +} + int CJSON_CDECL main(void) { UNITY_BEGIN(); @@ -267,5 +294,6 @@ int CJSON_CDECL main(void) RUN_TEST(test12_should_not_be_parsed); RUN_TEST(test13_should_be_parsed_without_null_termination); RUN_TEST(test14_should_not_be_parsed); + RUN_TEST(test15_should_not_heap_buffer_overflow); return UNITY_END(); } diff --git a/tests/parse_number.c b/tests/parse_number.c index 4cb72ec2..defda4ad 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -48,6 +48,7 @@ static void assert_parse_number(const char *string, int integer, double real) parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; buffer.content = (const unsigned char*)string; buffer.length = strlen(string) + sizeof(""); + buffer.hooks = global_hooks; TEST_ASSERT_TRUE(parse_number(item, &buffer)); assert_is_number(item); @@ -55,6 +56,17 @@ static void assert_parse_number(const char *string, int integer, double real) TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); } +static void assert_parse_big_number(const char *string) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)string; + buffer.length = strlen(string) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(parse_number(item, &buffer)); + assert_is_number(item); +} + static void parse_number_should_parse_zero(void) { assert_parse_number("0", 0, 0); @@ -96,6 +108,13 @@ static void parse_number_should_parse_negative_reals(void) assert_parse_number("-123e-128", 0, -123e-128); } +static void parse_number_should_parse_big_numbers(void) +{ + assert_parse_big_number("9999999999999999999999999999999999999999999999912345678901234567"); + assert_parse_big_number("9999999999999999999999999999999999999999999999912345678901234567E10"); + assert_parse_big_number("999999999999999999999999999999999999999999999991234567890.1234567"); +} + int CJSON_CDECL main(void) { /* initialize cJSON item */ @@ -106,5 +125,6 @@ int CJSON_CDECL main(void) RUN_TEST(parse_number_should_parse_positive_integers); RUN_TEST(parse_number_should_parse_positive_reals); RUN_TEST(parse_number_should_parse_negative_reals); + RUN_TEST(parse_number_should_parse_big_numbers); return UNITY_END(); } diff --git a/tests/print_object.c b/tests/print_object.c index 3ed0bfed..507d9759 100644 --- a/tests/print_object.c +++ b/tests/print_object.c @@ -63,7 +63,7 @@ static void assert_print_object(const char * const expected, const char * const formatted_buffer.format = true; TEST_ASSERT_TRUE_MESSAGE(print_object(item, &formatted_buffer), "Failed to print formatted string."); - TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted object is not correct."); reset(item); } diff --git a/tests/readme_examples.c b/tests/readme_examples.c index 80ea8aa1..09850cd4 100644 --- a/tests/readme_examples.c +++ b/tests/readme_examples.c @@ -69,7 +69,7 @@ static char* create_monitor(void) goto end; } /* after creation was successful, immediately add it to the monitor, - * thereby transfering ownership of the pointer to it */ + * thereby transferring ownership of the pointer to it */ cJSON_AddItemToObject(monitor, "name", name); resolutions = cJSON_CreateArray(); diff --git a/tests/unity/auto/parse_output.rb b/tests/unity/auto/parse_output.rb index f16cdb03..8e88fff0 100644 --- a/tests/unity/auto/parse_output.rb +++ b/tests/unity/auto/parse_output.rb @@ -78,7 +78,7 @@ def test_passed_unity_fixture(array) @array_list.push ' ' end - # Test was flagged as being ingored so format the output + # Test was flagged as being ignored so format the output def test_ignored(array) last_item = array.length - 1 test_name = array[last_item - 2] diff --git a/tests/unity/docs/UnityGettingStartedGuide.md b/tests/unity/docs/UnityGettingStartedGuide.md index 50fc91c7..08888a12 100644 --- a/tests/unity/docs/UnityGettingStartedGuide.md +++ b/tests/unity/docs/UnityGettingStartedGuide.md @@ -72,7 +72,7 @@ header files. These three files _are_ Unity. into this folder already. This is where all the handy documentation can be found. - `examples` - This contains a few examples of using Unity. -- `extras` - These are optional add ons to Unity that are not part of the core +- `extras` - These are optional addons to Unity that are not part of the core project. If you've reached us through James Grenning's book, you're going to want to look here. - `test` - This is how Unity and its scripts are all tested. If you're just using diff --git a/tests/unity/extras/eclipse/error_parsers.txt b/tests/unity/extras/eclipse/error_parsers.txt index 94e34ff3..f7ce8bd0 100644 --- a/tests/unity/extras/eclipse/error_parsers.txt +++ b/tests/unity/extras/eclipse/error_parsers.txt @@ -2,7 +2,7 @@ Eclipse error parsers ===================== These are a godsend for extracting & quickly navigating to -warnings & error messages from console output. Unforunately +warnings & error messages from console output. Unfortunately I don't know how to write an Eclipse plugin so you'll have to add them manually. diff --git a/tests/unity/src/unity.c b/tests/unity/src/unity.c index d02610a7..eee6f965 100644 --- a/tests/unity/src/unity.c +++ b/tests/unity/src/unity.c @@ -8,7 +8,7 @@ #include "unity.h" #include -/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +/* If omitted from header, declare overridable prototypes here so they're ready for use */ #ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION void UNITY_OUTPUT_CHAR(int); #endif diff --git a/tests/unity/test/rakefile b/tests/unity/test/rakefile index 4a2f3d2c..f05fb7a9 100644 --- a/tests/unity/test/rakefile +++ b/tests/unity/test/rakefile @@ -26,7 +26,7 @@ task :prepare_for_tests => TEMP_DIRS include RakefileHelpers -# Load proper GCC as defult configuration +# Load proper GCC as default configuration DEFAULT_CONFIG_FILE = 'gcc_auto_stdint.yml' configure_toolchain(DEFAULT_CONFIG_FILE)