details: https://hg.nginx.org/njs/rev/5f0812d53158 branches: changeset: 1167:5f0812d53158 user: Alexander Borisov <alexander.bori...@nginx.com> date: Thu Oct 03 15:41:30 2019 +0300 description: Fixed buffer overflow in Number.prototype.toString(radix).
diffstat: src/njs_number.c | 121 ++++++++++++++++++++++++++++++++++------------ src/test/njs_unit_test.c | 37 ++++++++++++++ 2 files changed, 126 insertions(+), 32 deletions(-) diffs (222 lines): diff -r 5c22e2f006fe -r 5f0812d53158 src/njs_number.c --- a/src/njs_number.c Fri Sep 27 22:21:55 2019 +0300 +++ b/src/njs_number.c Thu Oct 03 15:41:30 2019 +0300 @@ -6,6 +6,7 @@ #include <njs_main.h> +#include <njs_diyfp.h> /* @@ -637,30 +638,52 @@ njs_number_prototype_to_fixed(njs_vm_t * /* - * The radix equal to 2 produces the longest intergral value of a number - * and the maximum value consists of 1024 digits and minus sign. + * The radix equal to 2 produces the longest value for a number. */ #define NJS_STRING_RADIX_INTERGRAL_LEN (1 + 1024) -#define NJS_STRING_RADIX_FRACTION_LEN (1 + 54) +#define NJS_STRING_RADIX_FRACTION_LEN (1 + 1075) #define NJS_STRING_RADIX_LEN \ (NJS_STRING_RADIX_INTERGRAL_LEN + NJS_STRING_RADIX_FRACTION_LEN) +njs_inline double +njs_number_next_double(double n) +{ + njs_diyfp_t v; + + v = njs_d2diyfp(n); + + if (signbit(n)) { + if (v.significand == 0) { + return 0.0; + } + + v.significand--; + + } else { + v.significand++; + } + + return njs_diyfp2d(v); +} + + static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string, double number, uint32_t radix) { - u_char *p, *f, *end; - double n, next; - size_t size; - uint8_t reminder; - u_char buf[NJS_STRING_RADIX_LEN]; + int digit; + char ch; + double n, remainder, integer, fraction, delta; + u_char *p, *end; + uint32_t size; + u_char buf[NJS_STRING_RADIX_LEN]; static const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; - end = buf + NJS_STRING_RADIX_LEN; p = buf + NJS_STRING_RADIX_INTERGRAL_LEN; + end = p; n = number; @@ -668,35 +691,69 @@ njs_number_to_string_radix(njs_vm_t *vm, n = -n; } + integer = floor(n); + fraction = n - integer; + + delta = 0.5 * (njs_number_next_double(n) - n); + delta = njs_max(njs_number_next_double(0.0), delta); + + if (fraction >= delta) { + *p++ = '.'; + + do { + fraction *= radix; + delta *= radix; + + digit = (int) fraction; + *p++ = digits[digit]; + + fraction -= digit; + + if ((fraction > 0.5 || (fraction == 0.5 && (digit & 1))) + && (fraction + delta > 1)) + { + while (p-- != buf) { + if (p == buf + NJS_STRING_RADIX_INTERGRAL_LEN) { + integer += 1; + break; + } + + ch = *p; + digit = (ch > '9') ? ch - 'a' + 10 : ch - '0'; + + if ((uint32_t) (digit + 1) < radix) { + *p++ = digits[digit + 1]; + break; + } + } + + break; + } + + } while (fraction >= delta); + + end = p; + } + + p = buf + NJS_STRING_RADIX_INTERGRAL_LEN; + + while (njs_d2diyfp(integer / radix).exp > 0) { + integer /= radix; + *(--p) = '0'; + } + do { - next = trunc(n / radix); - reminder = n - next * radix; - *(--p) = digits[reminder]; - n = next; - } while (n != 0); + remainder = fmod(integer, radix); + *(--p) = digits[(int) remainder]; + integer = (integer - remainder) / radix; - n = number; + } while (integer > 0); - if (n < 0) { + if (number < 0) { *(--p) = '-'; } - f = buf + NJS_STRING_RADIX_INTERGRAL_LEN; - - n = n - trunc(n); - - if (n != 0) { - *f++ = '.'; - - do { - n = n * radix; - reminder = trunc(n); - *f++ = digits[reminder]; - n = n - reminder; - } while (n != 0 && f < end); - } - - size = f - p; + size = (uint32_t) (end - p); return njs_string_new(vm, string, p, size, size); } diff -r 5c22e2f006fe -r 5f0812d53158 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Sep 27 22:21:55 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Oct 03 15:41:30 2019 +0300 @@ -361,8 +361,10 @@ static njs_unit_test_t njs_test[] = /* Number.toString(radix) method. */ +#ifndef NJS_SUNC { njs_str("0..toString(2)"), njs_str("0") }, +#endif { njs_str("240..toString(2)"), njs_str("11110000") }, @@ -427,6 +429,41 @@ static njs_unit_test_t njs_test[] = { njs_str("NaN.toString(NaN)"), njs_str("RangeError") }, + { njs_str("1.2312313132.toString(14)"), + njs_str("1.3346da6d5d455c") }, + + { njs_str("7.799999999999999.toString(14)"), + njs_str("7.b2b2b2b2b2b2a5") }, + +#ifndef NJS_SUNC + { njs_str("1e20.toString(14)"), + njs_str("33cb3bb449c2a92000") }, + + /* Smallest positive double (next_double(0)). */ + { njs_str("4.94065645841246544176568792868E-324.toString(36) == ('0.' + '0'.repeat(207) +'3')"), + njs_str("true") }, + + { njs_str("1.7976931348623157E+308.toString(36) == ('1a1e4vngaiqo' + '0'.repeat(187))"), + njs_str("true") }, + + /* Largest positive double (prev_double(INFINITY)). */ + { njs_str("1.7976931348623157E+308.toString(2) == ('1'.repeat(53) + '0'.repeat(971))"), + njs_str("true") }, + + /* Maximum fraction length. */ + { njs_str("2.2250738585072014E-323.toString(2) == ('0.' + '0'.repeat(1071) + '101')"), + njs_str("true") }, + + { njs_str("2.2250738585072014E-308.toString(2) == ('0.' + '0'.repeat(1021) + '1')"), + njs_str("true") }, + + { njs_str("Array(5).fill().map((n, i) => i + 10).map((v)=>(1.2312313132).toString(v))"), + njs_str("1.2312313132,1.25a850416057383,1.293699002749414,1.3010274cab0288,1.3346da6d5d455c") }, + + { njs_str("Array(5).fill().map((n, i) => 36 - i).map((v)=>(1e23).toString(v))"), + njs_str("ga894a06abs0000,o5hlsorok4y0000,128fpsprqld20000,1m1s0ajv6cmo0000,2kmg5hv19br00000") }, +#endif + /* Number.prototype.toFixed(frac) method. */ { njs_str("(900.1).toFixed(1)"), _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel