Compare commits

...

15 Commits

Author SHA1 Message Date
Alan Wang c859b25da0 Release 1.7.19 (#958) 2025-09-09 21:56:10 +08:00
Lee 74e1ff4994 fix the incorrect check in decode_array_index_from_pointer (#957)
this fixes CVE-2025-57052
2025-09-05 14:53:20 +08:00
PeterAlfredLee 8f2beb57dd bump version of actions/upload-artifact 2025-05-07 11:31:28 +08:00
PeterAlfredLee a328d65ad4 allocate memory for the temporary buffer
Allocate memory for the temporary buffer when paring numbers.
This fixes CVE-2023-26819
2025-05-07 11:31:28 +08:00
Nicolas Badoux 12c4bf1986 Wrong counter increment 2024-09-23 19:08:58 +08:00
Nicolas Badoux 9d1b229086 Added max recusrion depth for cJSONDuplicate to prevent stack exhaustion in case of circular reference 2024-09-23 19:08:58 +08:00
Nicolas Badoux 078c4e6c53 Free mem in cjson_set_valuestring_should_return_null_if_strings_overlap 2024-08-30 11:29:28 +08:00
Nicolas Badoux 4f4d7f70c2 CJSON_SetValuestring: better test for overlapping string 2024-08-30 11:29:28 +08:00
Nicolas Badoux b47edc4750 CJSON_SetValuestring: add test for overlapping string 2024-08-30 11:29:28 +08:00
Nicolas Badoux d6d5449e1f fix #881, check overlap before calling strcpy in cJSON_SetValuestring 2024-08-30 11:29:28 +08:00
Nicolas Badoux a78d975537 cJSON_DetachItemViaPointer: added test and fix for check for null in item->prev 2024-08-30 11:29:05 +08:00
Nicolas Badoux f28a468e3b Check for NULL in cJSON_DetachItemViaPointer 2024-08-30 11:29:05 +08:00
Alanscut 424ce4ce96 Revert "feat: add tests for #842" to fix test failures
This reverts commit 5b502cdbfb.

Related to #860
2024-06-19 10:58:01 +08:00
Shaun Case 324973008c Fix spelling errors found by CodeSpell. See https://github.com/codespell-project/codespell 2024-05-14 09:43:59 +08:00
DL6ER 8a334b0140 Fix indentation (should use spaces)
Signed-off-by: DL6ER <dl6er@dl6er.de>
2024-05-14 09:42:46 +08:00
18 changed files with 179 additions and 39 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
if: failure()
with:
name: artifacts
+12
View File
@@ -1,3 +1,15 @@
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:
+1 -1
View File
@@ -2,7 +2,7 @@ set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 3.0)
project(cJSON
VERSION 1.7.18
VERSION 1.7.19
LANGUAGES C)
cmake_policy(SET CMP0054 NEW) # set CMP0054 policy
+3
View File
@@ -24,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
@@ -80,6 +81,8 @@ Contributors:
* [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)
+1 -1
View File
@@ -8,7 +8,7 @@ CJSON_TEST_SRC = cJSON.c test.c
LDLIBS = -lm
LIBVERSION = 1.7.18
LIBVERSION = 1.7.19
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
+61 -13
View File
@@ -117,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 != 18)
#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
@@ -308,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))
{
@@ -320,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])
{
@@ -338,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:
@@ -350,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 */
}
@@ -377,6 +402,8 @@ loop_end:
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;
}
@@ -403,6 +430,8 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
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 == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
{
@@ -413,8 +442,17 @@ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
{
return NULL;
}
if (strlen(valuestring) <= strlen(object->valuestring))
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;
}
@@ -570,10 +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 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 */
@@ -2204,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;
}
@@ -2726,7 +2764,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co
}
/* 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;
@@ -2773,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;
+7 -1
View File
@@ -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 18
#define CJSON_VERSION_PATCH 19
#include <stddef.h>
@@ -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);
+1 -1
View File
@@ -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');
-1
View File
@@ -62,7 +62,6 @@ if(ENABLE_CJSON_TEST)
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")
if (ENABLE_VALGRIND)
add_compile_definitions(ENABLE_VALGRIND)
find_program(MEMORYCHECK_COMMAND valgrind)
if ("${MEMORYCHECK_COMMAND}" MATCHES "MEMORYCHECK_COMMAND-NOTFOUND")
message(WARNING "Valgrind couldn't be found.")
+1 -1
View File
@@ -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);
+65 -13
View File
@@ -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];
@@ -456,6 +488,24 @@ static void cjson_functions_should_not_crash_with_null_pointers(void)
cJSON_Delete(item);
}
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;
@@ -732,21 +782,20 @@ static void cjson_set_bool_value_must_not_break_objects(void)
cJSON_Delete(sobj);
}
static void deallocated_pointers_should_be_set_to_null(void)
static void cjson_parse_big_numbers_should_not_report_error(void)
{
/* deallocated pointers should be set to null */
/* however, valgrind on linux reports when attempting to access a freed memory, we have to skip it */
#ifndef ENABLE_VALGRIND
cJSON *string = cJSON_CreateString("item");
cJSON *root = cJSON_CreateObject();
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]}";
cJSON_Delete(string);
free(string->valuestring);
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_AddObjectToObject(root, "object");
cJSON_Delete(root->child);
free(root->child->string);
#endif
cJSON_Delete(valid_big_number_json_object1);
cJSON_Delete(valid_big_number_json_object2);
}
int CJSON_CDECL main(void)
@@ -761,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);
@@ -779,7 +831,7 @@ int CJSON_CDECL main(void)
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(deallocated_pointers_should_be_set_to_null);
RUN_TEST(cjson_parse_big_numbers_should_not_report_error);
return UNITY_END();
}
+20
View File
@@ -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();
}
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -78,7 +78,7 @@ class ParseOutput
@array_list.push ' <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
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]
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -8,7 +8,7 @@
#include "unity.h"
#include <stddef.h>
/* 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
+1 -1
View File
@@ -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)