Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 32 additions & 5 deletions cJSON.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand All @@ -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])
{
Expand All @@ -338,23 +340,46 @@ 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:
goto loop_end;
}
}
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 */
}

Expand All @@ -377,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;
}

Expand Down
17 changes: 17 additions & 0 deletions tests/misc_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,22 @@ static void cjson_set_bool_value_must_not_break_objects(void)
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();
Expand Down Expand Up @@ -815,6 +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(cjson_parse_big_numbers_should_not_report_error);

return UNITY_END();
}
20 changes: 20 additions & 0 deletions tests/parse_number.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,25 @@ 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);
TEST_ASSERT_EQUAL_INT(integer, item->valueint);
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);
Expand Down Expand Up @@ -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 */
Expand All @@ -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();
}