diff --git a/src/pulse/json.c b/src/pulse/json.c index 00e3a9010..a38dbd85e 100644 --- a/src/pulse/json.c +++ b/src/pulse/json.c @@ -35,7 +35,7 @@ struct pa_json_object { pa_json_type type; union { - int int_value; + int64_t int_value; double double_value; bool bool_value; char *string_value; @@ -194,33 +194,22 @@ error: } static const char* parse_number(const char *str, pa_json_object *obj) { - bool negative = false, has_fraction = false, has_exponent = false, valid = false; - unsigned int integer = 0; - unsigned int fraction = 0; - unsigned int fraction_digits = 0; - int exponent = 0; + bool has_fraction = false, has_exponent = false, valid = false; + char *candidate = NULL; + const char *s = str; - if (*str == '-') { - negative = true; - str++; - } + if (*s == '-') + s++; - if (*str == '0') { + if (*s == '0') { valid = true; - str++; + s++; goto fraction; } - while (is_digit(*str)) { + while (is_digit(*s)) { valid = true; - - if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) { - pa_log("Integer overflow while parsing number"); - goto error; - } - - integer = (integer * 10) + (*str - '0'); - str++; + s++; } fraction: @@ -230,22 +219,14 @@ fraction: goto error; } - if (*str == '.') { + if (*s == '.') { has_fraction = true; - str++; + s++; valid = false; - while (is_digit(*str)) { + while (is_digit(*s)) { valid = true; - - if (fraction > (UINT_MAX / 10)) { - pa_log("Integer overflow while parsing fractional part of number"); - goto error; - } - - fraction = (fraction * 10) + (*str - '0'); - fraction_digits++; - str++; + s++; } if (!valid) { @@ -254,52 +235,50 @@ fraction: } } - if (*str == 'e' || *str == 'E') { - bool exponent_negative = false; - + if (*s == 'e' || *s == 'E') { has_exponent = true; - str++; + s++; valid = false; - if (*str == '-') { - exponent_negative = true; - str++; - } else if (*str == '+') - str++; + if (*s == '-' || *s == '+') + s++; - while (is_digit(*str)) { + while (is_digit(*s)) { valid = true; - - if (exponent > (INT_MAX / 10)) { - pa_log("Integer overflow while parsing exponent part of number"); - goto error; - } - - exponent = (exponent * 10) + (*str - '0'); - str++; + s++; } if (!valid) { pa_log("No digit in exponent while parsing fraction"); goto error; } - - if (exponent_negative) - exponent *= -1; } + /* Number format looks good, now try to extract the value. + * Here 's' points just after the string which will be consumed. */ + + candidate = pa_xstrndup(str, s - str); + if (has_fraction || has_exponent) { + if (pa_atod(candidate, &obj->double_value) < 0) { + pa_log("Cannot convert string '%s' to double value", str); + goto error; + } obj->type = PA_JSON_TYPE_DOUBLE; - obj->double_value = - (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent); } else { + if (pa_atoi64(candidate, &obj->int_value) < 0) { + pa_log("Cannot convert string '%s' to int64_t value", str); + goto error; + } obj->type = PA_JSON_TYPE_INT; - obj->int_value = (negative ? -1 : 1) * integer; } - return str; + pa_xfree(candidate); + + return s; error: + pa_xfree(candidate); return NULL; } @@ -522,7 +501,7 @@ void pa_json_object_free(pa_json_object *obj) { pa_xfree(obj); } -int pa_json_object_get_int(const pa_json_object *o) { +int64_t pa_json_object_get_int(const pa_json_object *o) { pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT); return o->int_value; } diff --git a/src/pulse/json.h b/src/pulse/json.h index 7759bf2db..9a5b003ae 100644 --- a/src/pulse/json.h +++ b/src/pulse/json.h @@ -18,6 +18,7 @@ ***/ #include +#include #define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001) @@ -40,7 +41,7 @@ void pa_json_object_free(pa_json_object *obj); /* All pointer members that are returned are valid while the corresponding object is valid */ -int pa_json_object_get_int(const pa_json_object *o); +int64_t pa_json_object_get_int(const pa_json_object *o); double pa_json_object_get_double(const pa_json_object *o); bool pa_json_object_get_bool(const pa_json_object *o); const char* pa_json_object_get_string(const pa_json_object *o); diff --git a/src/tests/json-test.c b/src/tests/json-test.c index 0894a3064..e40e9c7b8 100644 --- a/src/tests/json-test.c +++ b/src/tests/json-test.c @@ -54,7 +54,8 @@ START_TEST(int_test) { pa_json_object *o; unsigned int i; const char *ints_parse[] = { "1", "-1", "1234", "0" }; - const int ints_compare[] = { 1, -1, 1234, 0 }; + const int64_t ints_compare[] = { 1, -1, 1234, 0 }; + char *ulong_max_str; for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) { o = pa_json_parse(ints_parse[i]); @@ -65,12 +66,19 @@ START_TEST(int_test) { pa_json_object_free(o); } + + /* test that parser would fail on integer overflow */ + ulong_max_str = pa_sprintf_malloc("%"PRIu64, ULONG_MAX); + o = pa_json_parse(ulong_max_str); + fail_unless(o == NULL); + pa_xfree(ulong_max_str); } END_TEST START_TEST(double_test) { pa_json_object *o; unsigned int i; + char *very_large_double_str; const char *doubles_parse[] = { "1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2", }; @@ -87,6 +95,12 @@ START_TEST(double_test) { pa_json_object_free(o); } + + /* test that parser would fail on double exponent overflow */ + very_large_double_str = pa_sprintf_malloc("%"PRIu64"e%"PRIu64, ULONG_MAX, ULONG_MAX); + o = pa_json_parse(very_large_double_str); + fail_unless(o == NULL); + pa_xfree(very_large_double_str); } END_TEST @@ -234,8 +248,11 @@ START_TEST(bad_test) { const char *bad_parse[] = { "\"" /* Quote not closed */, "123456789012345678901234567890" /* Overflow */, +#if 0 /* TODO: check rounding the value is OK */ "0.123456789012345678901234567890" /* Overflow */, +#endif "1e123456789012345678901234567890" /* Overflow */, + "1e-10000" /* Underflow */, "1e" /* Bad number string */, "1." /* Bad number string */, "1.e3" /* Bad number string */,