diff --git a/src/cJSON.c b/src/cJSON.c index 9aec18469..33d9df04a 100644 --- a/src/cJSON.c +++ b/src/cJSON.c @@ -79,8 +79,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; @@ -111,7 +115,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) + (CJSON_VERSION_PATCH != 19) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif @@ -243,10 +247,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; @@ -290,21 +296,21 @@ static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) { double number = 0; unsigned char *after_end = NULL; - unsigned char number_c_string[64]; - unsigned char decimal_point = get_decimal_point(); - size_t i = 0; + 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)) { return false; } /* 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++) { + * 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; can_access_at_index(input_buffer, i); i++) { switch (buffer_at_offset(input_buffer)[i]) { case '0': case '1': @@ -320,11 +326,12 @@ static cJSON_bool parse_number(cJSON *const item, 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: @@ -332,10 +339,31 @@ static cJSON_bool parse_number(cJSON *const item, } } 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 */ } @@ -353,6 +381,8 @@ static cJSON_bool parse_number(cJSON *const item, 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; } @@ -370,16 +400,34 @@ 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) || + 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; } @@ -467,9 +515,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; @@ -515,6 +562,8 @@ static cJSON_bool print_number(const cJSON *const item, /* This checks for NaN and Infinity */ if (isnan(d) || isinf(d)) { 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 */ @@ -809,6 +858,7 @@ static cJSON_bool parse_string(cJSON *const item, fail: if (output != NULL) { input_buffer->hooks.deallocate(output); + output = NULL; } if (input_pointer != NULL) { @@ -1137,6 +1187,7 @@ static unsigned char *print(const cJSON *const item, /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } return printed; @@ -1144,10 +1195,12 @@ static unsigned char *print(const cJSON *const item, fail: if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } if (printed != NULL) { hooks->deallocate(printed); + printed = NULL; } return NULL; @@ -1183,6 +1236,7 @@ cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); + p.buffer = NULL; return NULL; } @@ -1527,6 +1581,10 @@ static cJSON_bool parse_object(cJSON *const item, 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); @@ -2033,7 +2091,8 @@ cJSON_AddArrayToObject(cJSON *const object, const char *const name) { 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; } @@ -2103,7 +2162,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; - if (which < 0) { + if (which < 0 || newitem == NULL) { return false; } @@ -2112,6 +2171,11 @@ cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { 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; @@ -2127,7 +2191,8 @@ 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; } @@ -2191,6 +2256,10 @@ static cJSON_bool replace_item_in_object(cJSON *object, } replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); + if (replacement->string == NULL) { + return false; + } + replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer( @@ -2356,6 +2425,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]); if (!n) { @@ -2369,7 +2439,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; } @@ -2399,7 +2472,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; } @@ -2430,7 +2506,10 @@ cJSON_CreateDoubleArray(const double *numbers, int count) { } p = n; } - a->child->prev = n; + + if (a && a->child) { + a->child->prev = n; + } return a; } @@ -2461,13 +2540,23 @@ cJSON_CreateStringArray(const char *const *strings, int count) { } 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; cJSON *next = NULL; @@ -2510,9 +2599,12 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { /* Walk the ->next chain for the child. */ 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; } @@ -2713,7 +2805,7 @@ 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)) { + ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } @@ -2831,4 +2923,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/src/cJSON.h b/src/cJSON.h index 1b5655c7b..17798b6af 100644 --- a/src/cJSON.h +++ b/src/cJSON.h @@ -89,7 +89,7 @@ CJSON_EXPORT_SYMBOLS does /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 14 +#define CJSON_VERSION_PATCH 19 #include @@ -149,6 +149,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); @@ -319,13 +325,11 @@ cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new * memory that will need to be released. With recurse!=0, it will duplicate any - * children connected to the item. - * The item->next and ->prev pointers are always zero on return from Duplicate. - */ + * children connected to the item. The item->next and ->prev pointers are always + * zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or - * invalid, they will be considered unequal. - * case_sensitive determines if object keys are treated case sensitive (1) or - * case insensitive (0) */ + * invalid, they will be considered unequal. case_sensitive determines if object + * keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, @@ -333,8 +337,8 @@ cJSON_Compare(const cJSON *const a, /* 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. */ + * such as a string constant, 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. @@ -381,6 +385,15 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 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; \