From 6f9d1f7dd65cef5738632d210d3c0f225c15a65b Mon Sep 17 00:00:00 2001 From: He Xian Date: Sat, 16 Nov 2024 15:12:31 +0800 Subject: [PATCH] "\u0000" is pretty valid in JSON string. Add valuestringlen to handle them in value. Signed-off-by: He Xian --- cJSON.c | 104 ++++++++++++++++++++++++++++++++++--------- cJSON.h | 5 +++ tests/misc_tests.c | 11 +++-- tests/print_string.c | 2 +- 4 files changed, 94 insertions(+), 28 deletions(-) diff --git a/cJSON.c b/cJSON.c index d7c72363..9a93ccaa 100644 --- a/cJSON.c +++ b/cJSON.c @@ -106,6 +106,20 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } +CJSON_PUBLIC(char *) cJSON_GetStringValueWithLength(const cJSON * const item, size_t *length) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + if(length != NULL) + { + *length = item->valuestringlen; + } + return item->valuestring; +} + CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) @@ -206,6 +220,26 @@ static unsigned char* cJSON_strdup(const unsigned char* string, const internal_h return copy; } +static unsigned char* cJSON_strndup(const unsigned char* string, size_t length, const internal_hooks * const hooks) +{ + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length += sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { if (hooks == NULL) @@ -401,6 +435,15 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double 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) +{ + if (valuestring == NULL) + { + return NULL; + } + return cJSON_SetValuestringWithLength(object, valuestring, strlen(valuestring)); +} + +CJSON_PUBLIC(char*) cJSON_SetValuestringWithLength(cJSON *object, const char *valuestring, size_t valuestringlen) { char *copy = NULL; size_t v1_len; @@ -416,20 +459,18 @@ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) return NULL; } - v1_len = strlen(valuestring); - v2_len = strlen(object->valuestring); + v1_len = valuestringlen; + v2_len = object->valuestringlen; 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); + /* memmove does handle overlapping string */ + memmove(object->valuestring, valuestring, valuestringlen); + object->valuestring[valuestringlen] = '\0'; + object->valuestringlen = valuestringlen; return object->valuestring; } - copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + copy = (char*) cJSON_strndup((const unsigned char*)valuestring, valuestringlen, &global_hooks); if (copy == NULL) { return NULL; @@ -439,6 +480,7 @@ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) cJSON_free(object->valuestring); } object->valuestring = copy; + object->valuestringlen = valuestringlen; return copy; } @@ -897,6 +939,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu item->type = cJSON_String; item->valuestring = (char*)output; + item->valuestringlen = (size_t) (output_pointer - output); input_buffer->offset = (size_t) (input_end - input_buffer->content); input_buffer->offset++; @@ -918,10 +961,10 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu return false; } -/* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +static cJSON_bool print_string_len(const unsigned char * const input, const size_t input_length, printbuffer * const output_buffer) { const unsigned char *input_pointer = NULL; + const unsigned char * const input_end = input + input_length; unsigned char *output = NULL; unsigned char *output_pointer = NULL; size_t output_length = 0; @@ -947,7 +990,7 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe } /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) + for (input_pointer = input; input_pointer < input_end; input_pointer++) { switch (*input_pointer) { @@ -992,7 +1035,7 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe output[0] = '\"'; output_pointer = output + 1; /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + for (input_pointer = input; input_pointer < input_end; (void)input_pointer++, output_pointer++) { if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { @@ -1040,10 +1083,16 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe return true; } -/* Invoke print_string_ptr (which is useful) on an item. */ +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const char * const input, printbuffer * const output_buffer) +{ + return print_string_len((const unsigned char*)input, input ? strlen(input) : 0, output_buffer); +} + +/* Invoke print_string_len (which is useful) on an item. */ static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) { - return print_string_ptr((unsigned char*)item->valuestring, p); + return print_string_len((unsigned char*)item->valuestring, item->valuestringlen, p); } /* Predeclare these prototypes. */ @@ -1437,7 +1486,7 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp return false; } - raw_length = strlen(item->valuestring) + sizeof(""); + raw_length = item->valuestringlen + sizeof(""); output = ensure(output_buffer, raw_length); if (output == NULL) { @@ -1785,7 +1834,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out } /* print key */ - if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + if (!print_string_ptr(current_item->string, output_buffer)) { return false; } @@ -2487,12 +2536,18 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) } CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + return cJSON_CreateStringWithLength(string, string ? strlen(string) : 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringWithLength(const char *string, size_t length) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + item->valuestringlen = length; + item->valuestring = (char*)cJSON_strndup((const unsigned char*)string, length, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); @@ -2510,6 +2565,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { item->type = cJSON_String | cJSON_IsReference; item->valuestring = (char*)cast_away_const(string); + item->valuestringlen = strlen(string); } return item; @@ -2542,7 +2598,8 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) if(item) { item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + item->valuestringlen = raw ? strlen(raw) : 0; + item->valuestring = (char*)cJSON_strndup((const unsigned char*)raw, item->valuestringlen, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); @@ -2768,7 +2825,8 @@ cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) newitem->valuedouble = item->valuedouble; if (item->valuestring) { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + newitem->valuestringlen = item->valuestringlen; + newitem->valuestring = (char*)cJSON_strndup((unsigned char*)item->valuestring, item->valuestringlen, &global_hooks); if (!newitem->valuestring) { goto fail; @@ -3078,7 +3136,11 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons { return false; } - if (strcmp(a->valuestring, b->valuestring) == 0) + if (a->valuestringlen != b->valuestringlen) + { + return false; + } + if (memcmp(a->valuestring, b->valuestring, a->valuestringlen) == 0) { return true; } diff --git a/cJSON.h b/cJSON.h index 37520bbc..6198e0a2 100644 --- a/cJSON.h +++ b/cJSON.h @@ -113,6 +113,8 @@ typedef struct cJSON /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; + /* Length of the item's string, useful for handling "\u0000" in string */ + size_t valuestringlen; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ @@ -183,6 +185,7 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(char *) cJSON_GetStringValueWithLength(const cJSON * const item, size_t *length); CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ @@ -204,6 +207,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringWithLength(const char *string, size_t length); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); @@ -284,6 +288,7 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (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); +CJSON_PUBLIC(char*) cJSON_SetValuestringWithLength(cJSON *object, const char *valuestring, size_t valuestringlen); /* 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) ( \ diff --git a/tests/misc_tests.c b/tests/misc_tests.c index a96c2fdc..59bd361d 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -238,7 +238,7 @@ static void cjson_should_not_follow_too_deep_circular_references(void) static void cjson_set_number_value_should_set_numbers(void) { - cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}}; + cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, 0, NULL}}; cJSON_SetNumberValue(number, 1.5); TEST_ASSERT_EQUAL(1, number->valueint); @@ -360,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, 0, NULL}}; cJSON *child = NULL; cJSON *replacement = NULL; cJSON_bool flag = false; @@ -498,11 +498,10 @@ static void cjson_set_valuestring_should_return_null_if_strings_overlap(void) str = cJSON_SetValuestring(obj, "abcde"); str += 1; - /* The string passed to strcpy overlap which is not allowed.*/ + /* The string passed to strcpy overlap which is correctly copied.*/ 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); + /* If it overlaps, the string will not be messed up.*/ + TEST_ASSERT_TRUE(strncmp(str2, "bcde", 4) == 0); cJSON_Delete(obj); } diff --git a/tests/print_string.c b/tests/print_string.c index e6f5b92d..348898f4 100644 --- a/tests/print_string.c +++ b/tests/print_string.c @@ -34,7 +34,7 @@ static void assert_print_string(const char *expected, const char *input) buffer.noalloc = true; buffer.hooks = global_hooks; - TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer), "Failed to print string."); + TEST_ASSERT_TRUE_MESSAGE(print_string_ptr(input, &buffer), "Failed to print string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected."); }