Json objects of type double with the value NaN could cause undefined behavior when casting double to int in `json_object_get_int`.pull/882/head
| @@ -721,6 +721,7 @@ int32_t json_object_get_int(const struct json_object *jso) | |||
| int64_t cint64 = 0; | |||
| double cdouble; | |||
| enum json_type o_type; | |||
| errno = 0; | |||
| if (!jso) | |||
| return 0; | |||
| @@ -767,6 +768,11 @@ int32_t json_object_get_int(const struct json_object *jso) | |||
| return INT32_MIN; | |||
| if (cdouble >= INT32_MAX) | |||
| return INT32_MAX; | |||
| if (isnan(cdouble)) | |||
| { | |||
| errno = EINVAL; | |||
| return INT32_MIN; | |||
| } | |||
| return (int32_t)cdouble; | |||
| case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | |||
| default: return 0; | |||
| @@ -801,6 +807,7 @@ struct json_object *json_object_new_uint64(uint64_t i) | |||
| int64_t json_object_get_int64(const struct json_object *jso) | |||
| { | |||
| int64_t cint; | |||
| errno = 0; | |||
| if (!jso) | |||
| return 0; | |||
| @@ -826,6 +833,11 @@ int64_t json_object_get_int64(const struct json_object *jso) | |||
| return INT64_MAX; | |||
| if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN) | |||
| return INT64_MIN; | |||
| if (isnan(JC_DOUBLE_C(jso)->c_double)) | |||
| { | |||
| errno = EINVAL; | |||
| return INT64_MIN; | |||
| } | |||
| return (int64_t)JC_DOUBLE_C(jso)->c_double; | |||
| case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | |||
| case json_type_string: | |||
| @@ -839,6 +851,7 @@ int64_t json_object_get_int64(const struct json_object *jso) | |||
| uint64_t json_object_get_uint64(const struct json_object *jso) | |||
| { | |||
| uint64_t cuint; | |||
| errno = 0; | |||
| if (!jso) | |||
| return 0; | |||
| @@ -864,6 +877,11 @@ uint64_t json_object_get_uint64(const struct json_object *jso) | |||
| return UINT64_MAX; | |||
| if (JC_DOUBLE_C(jso)->c_double < 0) | |||
| return 0; | |||
| if (isnan(JC_DOUBLE_C(jso)->c_double)) | |||
| { | |||
| errno = EINVAL; | |||
| return 0; | |||
| } | |||
| return (uint64_t)JC_DOUBLE_C(jso)->c_double; | |||
| case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | |||
| case json_type_string: | |||
| @@ -693,7 +693,7 @@ JSON_EXPORT struct json_object *json_object_new_boolean(json_bool b); | |||
| * The type is coerced to a json_bool if the passed object is not a json_bool. | |||
| * integer and double objects will return 0 if there value is zero | |||
| * or 1 otherwise. If the passed object is a string it will return | |||
| * 1 if it has a non zero length. | |||
| * 1 if it has a non zero length. | |||
| * If any other object type is passed 0 will be returned, even non-empty | |||
| * json_type_array and json_type_object objects. | |||
| * | |||
| @@ -739,9 +739,11 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i); | |||
| /** Get the int value of a json_object | |||
| * | |||
| * The type is coerced to a int if the passed object is not a int. | |||
| * double objects will return their integer conversion. Strings will be | |||
| * parsed as an integer. If no conversion exists then 0 is returned | |||
| * and errno is set to EINVAL. null is equivalent to 0 (no error values set) | |||
| * double objects will return their integer conversion except for NaN values | |||
| * which return INT32_MIN and the errno is set to EINVAL. | |||
| * Strings will be parsed as an integer. If no conversion exists then 0 is | |||
| * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | |||
| * set) | |||
| * | |||
| * Note that integers are stored internally as 64-bit values. | |||
| * If the value of too big or too small to fit into 32-bit, INT32_MAX or | |||
| @@ -783,8 +785,11 @@ JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); | |||
| /** Get the int value of a json_object | |||
| * | |||
| * The type is coerced to a int64 if the passed object is not a int64. | |||
| * double objects will return their int64 conversion. Strings will be | |||
| * parsed as an int64. If no conversion exists then 0 is returned. | |||
| * double objects will return their int64 conversion except for NaN values | |||
| * which return INT64_MIN and the errno is set to EINVAL. | |||
| * Strings will be parsed as an int64. If no conversion exists then 0 is | |||
| * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | |||
| * set) | |||
| * | |||
| * NOTE: Set errno to 0 directly before a call to this function to determine | |||
| * whether or not conversion was successful (it does not clear the value for | |||
| @@ -798,8 +803,11 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); | |||
| /** Get the uint value of a json_object | |||
| * | |||
| * The type is coerced to a uint64 if the passed object is not a uint64. | |||
| * double objects will return their uint64 conversion. Strings will be | |||
| * parsed as an uint64. If no conversion exists then 0 is returned. | |||
| * double objects will return their uint64 conversion except for NaN values | |||
| * which return 0 and the errno is set to EINVAL. | |||
| * Strings will be parsed as an uint64. If no conversion exists then 0 is | |||
| * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | |||
| * set) | |||
| * | |||
| * NOTE: Set errno to 0 directly before a call to this function to determine | |||
| * whether or not conversion was successful (it does not clear the value for | |||
| @@ -36,6 +36,7 @@ int main(int argc, char **argv) | |||
| \"array_with_zero\": [ 0 ],\n\ | |||
| \"empty_object\": {},\n\ | |||
| \"nonempty_object\": { \"a\": 123 },\n\ | |||
| \"nan\": NaN,\n\ | |||
| }"; | |||
| /* Note: 2147483649 = INT_MAX + 2 */ | |||
| /* Note: 9223372036854775809 = INT64_MAX + 2 */ | |||
| @@ -62,6 +63,7 @@ int main(int argc, char **argv) | |||
| getit(new_obj, "array_with_zero"); | |||
| getit(new_obj, "empty_object"); | |||
| getit(new_obj, "nonempty_object"); | |||
| getit(new_obj, "nan"); | |||
| // Now check the behaviour of the json_object_is_type() function. | |||
| printf("\n================================\n"); | |||
| @@ -75,6 +77,7 @@ int main(int argc, char **argv) | |||
| checktype(new_obj, "int64_number"); | |||
| checktype(new_obj, "negative_number"); | |||
| checktype(new_obj, "a_null"); | |||
| checktype(new_obj, "nan"); | |||
| json_object_put(new_obj); | |||
| @@ -12,6 +12,7 @@ Parsed input: { | |||
| "array_with_zero": [ 0 ], | |||
| "empty_object": {}, | |||
| "nonempty_object": { "a": 123 }, | |||
| "nan": NaN, | |||
| } | |||
| Result is not NULL | |||
| new_obj.string_of_digits json_object_get_type()=string | |||
| @@ -92,6 +93,12 @@ new_obj.nonempty_object json_object_get_int64()=0 | |||
| new_obj.nonempty_object json_object_get_uint64()=0 | |||
| new_obj.nonempty_object json_object_get_boolean()=0 | |||
| new_obj.nonempty_object json_object_get_double()=0.000000 | |||
| new_obj.nan json_object_get_type()=double | |||
| new_obj.nan json_object_get_int()=-2147483648 | |||
| new_obj.nan json_object_get_int64()=-9223372036854775808 | |||
| new_obj.nan json_object_get_uint64()=0 | |||
| new_obj.nan json_object_get_boolean()=1 | |||
| new_obj.nan json_object_get_double()=nan | |||
| ================================ | |||
| json_object_is_type: null,boolean,double,int,object,array,string | |||
| @@ -104,3 +111,4 @@ new_obj.boolean_false : 0,1,0,0,0,0,0 | |||
| new_obj.int64_number : 0,0,0,1,0,0,0 | |||
| new_obj.negative_number : 0,0,0,1,0,0,0 | |||
| new_obj.a_null : 1,0,0,0,0,0,0 | |||
| new_obj.nan : 0,0,1,0,0,0,0 | |||
| @@ -4,6 +4,7 @@ | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <math.h> | |||
| #include "json.h" | |||
| @@ -25,6 +26,7 @@ | |||
| #define N_I64 json_object_new_int64 | |||
| #define N_U64 json_object_new_uint64 | |||
| #define N_STR json_object_new_string | |||
| #define N_DBL json_object_new_double | |||
| int main(int argc, char **argv) | |||
| { | |||
| @@ -45,6 +47,8 @@ int main(int argc, char **argv) | |||
| CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == 0); | |||
| CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == 0); | |||
| CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == 0); | |||
| CHECK_GET_INT(N_DBL(NAN), INT32_MIN && errno == EINVAL); | |||
| printf("INT GET PASSED\n"); | |||
| CHECK_GET_INT64(N_I64(INT64_MAX), INT64_MAX && errno == 0); | |||
| @@ -53,11 +57,13 @@ int main(int argc, char **argv) | |||
| CHECK_GET_INT64(N_STR(I64_MIN_S), INT64_MIN && errno == 0); | |||
| CHECK_GET_INT64(N_STR(I64_OVER), INT64_MAX && errno == ERANGE); | |||
| CHECK_GET_INT64(N_STR(I64_UNDER), INT64_MIN && errno == ERANGE); | |||
| CHECK_GET_INT64(N_DBL(NAN), INT64_MIN && errno == EINVAL); | |||
| printf("INT64 GET PASSED\n"); | |||
| CHECK_GET_UINT64(N_U64(UINT64_MAX), UINT64_MAX && errno == 0); | |||
| CHECK_GET_UINT64(N_U64(-1), UINT64_MAX && errno == 0); | |||
| CHECK_GET_UINT64(N_STR(U64_OUT_S), UINT64_MAX && errno == ERANGE); | |||
| CHECK_GET_UINT64(N_DBL(NAN), 0 && errno == EINVAL); | |||
| printf("UINT64 GET PASSED\n"); | |||
| printf("PASSED\n"); | |||