mirror of
https://github.com/json-c/json-c.git
synced 2026-03-20 21:49:07 +08:00
Explicitly handle NaN values when converting to int
Json objects of type double with the value NaN could cause undefined behavior when casting double to int in `json_object_get_int`.
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user