Skip to content

Commit 0c35883

Browse files
committed
Set error codes for truncating int conversions
json_object_get_int/int64/uint64() now sets errno to ERANGE when the source value can't be represented in the target type.
1 parent 8c13801 commit 0c35883

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

json_object.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -757,17 +757,29 @@ int32_t json_object_get_int(const struct json_object *jso)
757757
{
758758
case json_type_int:
759759
/* Make sure we return the correct values for out of range numbers. */
760-
if (cint64 <= INT32_MIN)
760+
if (cint64 < INT32_MIN)
761+
{
762+
errno = ERANGE;
761763
return INT32_MIN;
762-
if (cint64 >= INT32_MAX)
764+
}
765+
if (cint64 > INT32_MAX)
766+
{
767+
errno = ERANGE;
763768
return INT32_MAX;
769+
}
764770
return (int32_t)cint64;
765771
case json_type_double:
766772
cdouble = JC_DOUBLE_C(jso)->c_double;
767-
if (cdouble <= INT32_MIN)
773+
if (cdouble < INT32_MIN)
774+
{
775+
errno = ERANGE;
768776
return INT32_MIN;
769-
if (cdouble >= INT32_MAX)
777+
}
778+
if (cdouble > INT32_MAX)
779+
{
780+
errno = ERANGE;
770781
return INT32_MAX;
782+
}
771783
if (isnan(cdouble))
772784
{
773785
errno = EINVAL;
@@ -820,19 +832,28 @@ int64_t json_object_get_int64(const struct json_object *jso)
820832
{
821833
case json_object_int_type_int64: return jsoint->cint.c_int64;
822834
case json_object_int_type_uint64:
823-
if (jsoint->cint.c_uint64 >= INT64_MAX)
835+
if (jsoint->cint.c_uint64 > INT64_MAX)
836+
{
837+
errno = ERANGE;
824838
return INT64_MAX;
839+
}
825840
return (int64_t)jsoint->cint.c_uint64;
826841
default: json_abort("invalid cint_type");
827842
}
828843
}
829844
case json_type_double:
830845
// INT64_MAX can't be exactly represented as a double
831846
// so cast to tell the compiler it's ok to round up.
832-
if (JC_DOUBLE_C(jso)->c_double >= (double)INT64_MAX)
847+
if (JC_DOUBLE_C(jso)->c_double > (double)INT64_MAX)
848+
{
849+
errno = ERANGE;
833850
return INT64_MAX;
834-
if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN)
851+
}
852+
if (JC_DOUBLE_C(jso)->c_double < (double)INT64_MIN)
853+
{
854+
errno = ERANGE;
835855
return INT64_MIN;
856+
}
836857
if (isnan(JC_DOUBLE_C(jso)->c_double))
837858
{
838859
errno = EINVAL;
@@ -864,7 +885,10 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
864885
{
865886
case json_object_int_type_int64:
866887
if (jsoint->cint.c_int64 < 0)
888+
{
889+
errno = ERANGE;
867890
return 0;
891+
}
868892
return (uint64_t)jsoint->cint.c_int64;
869893
case json_object_int_type_uint64: return jsoint->cint.c_uint64;
870894
default: json_abort("invalid cint_type");
@@ -873,10 +897,16 @@ uint64_t json_object_get_uint64(const struct json_object *jso)
873897
case json_type_double:
874898
// UINT64_MAX can't be exactly represented as a double
875899
// so cast to tell the compiler it's ok to round up.
876-
if (JC_DOUBLE_C(jso)->c_double >= (double)UINT64_MAX)
900+
if (JC_DOUBLE_C(jso)->c_double > (double)UINT64_MAX)
901+
{
902+
errno = ERANGE;
877903
return UINT64_MAX;
904+
}
878905
if (JC_DOUBLE_C(jso)->c_double < 0)
906+
{
907+
errno = ERANGE;
879908
return 0;
909+
}
880910
if (isnan(JC_DOUBLE_C(jso)->c_double))
881911
{
882912
errno = EINVAL;

json_object.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,8 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i);
743743
* which return INT32_MIN and the errno is set to EINVAL.
744744
* Strings will be parsed as an integer. If no conversion exists then 0 is
745745
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
746-
* set)
746+
* set).
747+
* Sets errno to ERANGE if the value exceeds the range of int.
747748
*
748749
* Note that integers are stored internally as 64-bit values.
749750
* If the value of too big or too small to fit into 32-bit, INT32_MAX or
@@ -789,7 +790,8 @@ JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val);
789790
* which return INT64_MIN and the errno is set to EINVAL.
790791
* Strings will be parsed as an int64. If no conversion exists then 0 is
791792
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
792-
* set)
793+
* set).
794+
* Sets errno to ERANGE if the value exceeds the range of int64.
793795
*
794796
* NOTE: Set errno to 0 directly before a call to this function to determine
795797
* whether or not conversion was successful (it does not clear the value for
@@ -807,7 +809,8 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj);
807809
* which return 0 and the errno is set to EINVAL.
808810
* Strings will be parsed as an uint64. If no conversion exists then 0 is
809811
* returned and errno is set to EINVAL. null is equivalent to 0 (no error values
810-
* set)
812+
* set).
813+
* Sets errno to ERANGE if the value exceeds the range of uint64.
811814
*
812815
* NOTE: Set errno to 0 directly before a call to this function to determine
813816
* whether or not conversion was successful (it does not clear the value for

tests/test_int_get.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include "json.h"
1010

11+
#define I32_MAX_S "2147483647"
12+
#define I32_MIN_S "-2147483648"
1113
#define I64_MAX_S "9223372036854775807"
1214
#define I64_OVER "9223372036854775808"
1315
#define I64_MIN_S "-9223372036854775808"
@@ -43,12 +45,15 @@ int main(int argc, char **argv)
4345

4446
CHECK_GET_INT(N_I64(INT32_MAX), INT32_MAX && errno == 0);
4547
CHECK_GET_INT(N_I64(INT32_MIN), INT32_MIN && errno == 0);
46-
CHECK_GET_INT(N_I64(INT64_MAX), INT32_MAX && errno == 0);
47-
CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == 0);
48-
CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == 0);
49-
CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == 0);
48+
CHECK_GET_INT(N_I64(INT64_MAX), INT32_MAX && errno == ERANGE);
49+
CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == ERANGE);
50+
CHECK_GET_INT(N_STR(I32_MAX_S), INT32_MAX && errno == 0);
51+
CHECK_GET_INT(N_STR(I32_MIN_S), INT32_MIN && errno == 0);
52+
CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == ERANGE);
53+
CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == ERANGE);
54+
CHECK_GET_INT(N_DBL(INFINITY), INT32_MAX && errno == ERANGE);
55+
CHECK_GET_INT(N_DBL(-INFINITY), INT32_MIN && errno == ERANGE);
5056
CHECK_GET_INT(N_DBL(NAN), INT32_MIN && errno == EINVAL);
51-
5257
printf("INT GET PASSED\n");
5358

5459
CHECK_GET_INT64(N_I64(INT64_MAX), INT64_MAX && errno == 0);
@@ -57,12 +62,16 @@ int main(int argc, char **argv)
5762
CHECK_GET_INT64(N_STR(I64_MIN_S), INT64_MIN && errno == 0);
5863
CHECK_GET_INT64(N_STR(I64_OVER), INT64_MAX && errno == ERANGE);
5964
CHECK_GET_INT64(N_STR(I64_UNDER), INT64_MIN && errno == ERANGE);
65+
CHECK_GET_INT64(N_DBL(INFINITY), INT64_MAX && errno == ERANGE);
66+
CHECK_GET_INT64(N_DBL(-INFINITY), INT64_MIN && errno == ERANGE);
6067
CHECK_GET_INT64(N_DBL(NAN), INT64_MIN && errno == EINVAL);
6168
printf("INT64 GET PASSED\n");
6269

6370
CHECK_GET_UINT64(N_U64(UINT64_MAX), UINT64_MAX && errno == 0);
6471
CHECK_GET_UINT64(N_U64(-1), UINT64_MAX && errno == 0);
6572
CHECK_GET_UINT64(N_STR(U64_OUT_S), UINT64_MAX && errno == ERANGE);
73+
CHECK_GET_UINT64(N_DBL(INFINITY), UINT64_MAX && errno == ERANGE);
74+
CHECK_GET_UINT64(N_DBL(-INFINITY), 0 && errno == ERANGE);
6675
CHECK_GET_UINT64(N_DBL(NAN), 0 && errno == EINVAL);
6776
printf("UINT64 GET PASSED\n");
6877

0 commit comments

Comments
 (0)