details: https://hg.nginx.org/njs/rev/5b1bf60c8ede branches: changeset: 1272:5b1bf60c8ede user: Dmitry Volyntsev <xei...@nginx.com> date: Tue Dec 03 14:59:26 2019 +0300 description: Fixed Number.prototype.toString(radix).
Fixed buffer-overflow in Number.prototype.toString(radix) when fraction == delta == 0. The last comparison might by true for very small numbers (denormals) around zero when fast-math mode is enabled. The issue was introduced in 5f0812d53158. diffstat: auto/clang | 13 ++++ src/njs_clang.h | 18 +++++ src/njs_number.c | 4 +- src/njs_shell.c | 16 +++++ src/test/njs_unit_test.c | 150 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 175 insertions(+), 26 deletions(-) diffs (326 lines): diff -r 01c7375c9b5c -r 5b1bf60c8ede auto/clang --- a/auto/clang Fri Nov 29 12:53:33 2019 +0300 +++ b/auto/clang Tue Dec 03 14:59:26 2019 +0300 @@ -177,3 +177,16 @@ njs_feature_test="#include <math.h> return 0; }" . auto/feature + + +njs_feature="_mm_setcsr()" +njs_feature_name=NJS_HAVE_DENORMALS_CONTROL +njs_feature_run=no +njs_feature_incs= +njs_feature_libs= +njs_feature_test="#include <xmmintrin.h> + int main(void) { + _mm_setcsr(_mm_getcsr()); + return 0; + }" +. auto/feature diff -r 01c7375c9b5c -r 5b1bf60c8ede src/njs_clang.h --- a/src/njs_clang.h Fri Nov 29 12:53:33 2019 +0300 +++ b/src/njs_clang.h Tue Dec 03 14:59:26 2019 +0300 @@ -165,6 +165,24 @@ njs_leading_zeros64(uint64_t x) #endif +#if (NJS_HAVE_DENORMALS_CONTROL) +#include <xmmintrin.h> + +/* + * 0x8000 Flush to zero + * 0x0040 Denormals are zeros + */ + +#define NJS_MM_DENORMALS_MASK 0x8040 + +#define njs_mm_denormals(on) \ + _mm_setcsr((_mm_getcsr() & ~NJS_MM_DENORMALS_MASK) | (!(on) ? 0x8040: 0x0)) +#else + +#define njs_mm_denormals(on) +#endif + + #ifndef NJS_MAX_ALIGNMENT #if (NJS_SOLARIS) diff -r 01c7375c9b5c -r 5b1bf60c8ede src/njs_number.c --- a/src/njs_number.c Fri Nov 29 12:53:33 2019 +0300 +++ b/src/njs_number.c Tue Dec 03 14:59:26 2019 +0300 @@ -565,7 +565,7 @@ njs_number_prototype_to_string(njs_vm_t number = njs_number(value); - if (radix != 10 && !isnan(number) && !isinf(number)) { + if (radix != 10 && !isnan(number) && !isinf(number) && number != 0) { return njs_number_to_string_radix(vm, &vm->retval, number, radix); } } @@ -838,7 +838,7 @@ njs_number_to_string_radix(njs_vm_t *vm, delta = 0.5 * (njs_number_next_double(n) - n); delta = njs_max(njs_number_next_double(0.0), delta); - if (fraction >= delta) { + if (fraction >= delta && delta != 0) { *p++ = '.'; do { diff -r 01c7375c9b5c -r 5b1bf60c8ede src/njs_shell.c --- a/src/njs_shell.c Fri Nov 29 12:53:33 2019 +0300 +++ b/src/njs_shell.c Tue Dec 03 14:59:26 2019 +0300 @@ -26,6 +26,7 @@ typedef struct { uint8_t disassemble; + uint8_t denormals; uint8_t interactive; uint8_t module; uint8_t quiet; @@ -232,6 +233,8 @@ main(int argc, char **argv) goto done; } + njs_mm_denormals(opts.denormals); + njs_memzero(&vm_options, sizeof(njs_vm_opt_t)); if (opts.file == NULL) { @@ -306,6 +309,7 @@ njs_get_options(njs_opts_t *opts, int ar "Options:\n" " -c specify the command to execute.\n" " -d print disassembled code.\n" + " -f disabled denormals mode.\n" " -p set path prefix for modules.\n" " -q disable interactive introduction prompt.\n" " -s sandbox mode.\n" @@ -316,6 +320,8 @@ njs_get_options(njs_opts_t *opts, int ar ret = NJS_DONE; + opts->denormals = 1; + for (i = 1; i < argc; i++) { p = argv[i]; @@ -349,6 +355,16 @@ njs_get_options(njs_opts_t *opts, int ar opts->disassemble = 1; break; + case 'f': + +#if !(NJS_HAVE_DENORMALS_CONTROL) + njs_stderror("option \"-f\" is not supported\n"); + return NJS_ERROR; +#endif + + opts->denormals = 0; + break; + case 'p': if (++i < argc) { opts->n_paths++; diff -r 01c7375c9b5c -r 5b1bf60c8ede src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Nov 29 12:53:33 2019 +0300 +++ b/src/test/njs_unit_test.c Tue Dec 03 14:59:26 2019 +0300 @@ -361,13 +361,23 @@ 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") }, + + { njs_str("(1234.567).toString(3)"), + njs_str("1200201.120022100021001021021002202") }, + + { njs_str("(1234.567).toString(5)"), + njs_str("14414.240414141414141414") }, + + { njs_str("(1234.567).toString(17)"), + njs_str("44a.9aeb6faa0da") }, + + { njs_str("(1234.567).toString(36)"), + njs_str("ya.kety9sifl") }, + + { njs_str("Number(-1.1).toString(36)"), + njs_str("-1.3llllllllm") }, { njs_str("Math.pow(-2, 1023).toString(2).length"), njs_str("1025") }, @@ -439,10 +449,6 @@ static njs_unit_test_t njs_test[] = { 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") }, @@ -450,13 +456,6 @@ static njs_unit_test_t njs_test[] = { 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") }, @@ -639,14 +638,6 @@ static njs_unit_test_t njs_test[] = { njs_str("Array(5).fill().map((n, i) => i + 1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toExponential())"), njs_str("-6.666666666666666e-1,1.3333333333333333e+0,-2e+0,2.6666666666666667e+0,-3.3333333333333337e+0") }, -#ifndef NJS_SUNC - { njs_str("4.94065645841246544176568792868e-324.toExponential()"), - njs_str("5e-324") }, - - { njs_str("4.94065645841246544176568792868e-324.toExponential(10)"), - njs_str("4.9406564584e-324") }, -#endif - { njs_str("1.7976931348623157e+308.toExponential()"), njs_str("1.7976931348623157e+308") }, @@ -15343,6 +15334,84 @@ static njs_unit_test_t njs_test[] = }; +static njs_unit_test_t njs_denormals_test[] = +{ + { njs_str("2.2250738585072014e-308"), + njs_str("2.2250738585072014e-308") }, + +#ifndef NJS_SUNC + { njs_str("2.2250738585072014E-308.toString(2) == ('0.' + '0'.repeat(1021) + '1')"), + njs_str("true") }, + + { njs_str("Number('2.2250738585072014E-323')"), + njs_str("2.5e-323") }, + + { njs_str("Number('2.2250738585072014E-323') + 0"), + njs_str("2.5e-323") }, + + /* Smallest positive double (next_double(0)). */ + { njs_str("5E-324.toString(36) === '0.' + '0'.repeat(207) + '3'"), + njs_str("true") }, + + /* Maximum fraction length. */ + { njs_str("2.2250738585072014E-323.toString(2) == ('0.' + '0'.repeat(1071) + '101')"), + njs_str("true") }, + + /* Denormals. */ + { njs_str("var zeros = count => '0'.repeat(count);" + "[" + " [1.8858070859709815e-308, `0.${zeros(1022)}1101100011110111011100000100011001111101110001010111`]," + // FIXME: " [Number.MIN_VALUE, `0.${zeros(1073)}1`]" + " [-5.06631661953108e-309, `-0.${zeros(1024)}11101001001010000001101111010101011111111011010111`]," + " [6.22574126804e-313, `0.${zeros(1037)}11101010101101100111000110100111001`]," + " [-4e-323, `-0.${zeros(1070)}1`]," + "].every(t=>t[0].toString(2) === t[1])"), + njs_str("true") }, + + { njs_str("4.94065645841246544176568792868e-324.toExponential()"), + njs_str("5e-324") }, + + { njs_str("4.94065645841246544176568792868e-324.toExponential(10)"), + njs_str("4.9406564584e-324") }, +#endif + +}; + + +static njs_unit_test_t njs_disabled_denormals_test[] = +{ + { njs_str("Number('2.2250738585072014E-323')"), + njs_str("0") }, + + { njs_str("Number('2.2250738585072014E-323') + 0"), + njs_str("0") }, + + /* Smallest positive double (next_double(0)). */ + { njs_str("5E-324.toString(36)"), + njs_str("0") }, + + { njs_str("2.2250738585072014E-323.toString(2)"), + njs_str("0") }, + + /* Smallest normal double. */ + + { njs_str("2.2250738585072014e-308"), + njs_str("2.2250738585072014e-308") }, + + { njs_str("2.2250738585072014e-308/2"), + njs_str("0") }, + + /* Denormals. */ + { njs_str("[" + "1.8858070859709815e-308," + "-5.06631661953108e-309," + "6.22574126804e-313," + "-4e-323," + "].map(v=>v.toString(2))"), + njs_str("0,0,0,0") }, +}; + + static njs_unit_test_t njs_module_test[] = { { njs_str("function f(){return 2}; var f; f()"), @@ -16816,12 +16885,45 @@ main(int argc, char **argv) opts.repeat = 1; opts.unsafe = 1; + njs_mm_denormals(1); + ret = njs_unit_test(njs_test, njs_nitems(njs_test), "script tests", &opts, &stat); if (ret != NJS_OK) { return ret; } + ret = njs_unit_test(njs_denormals_test, njs_nitems(njs_denormals_test), + "denormals tests", &opts, &stat); + if (ret != NJS_OK) { + return ret; + } + +#if (NJS_HAVE_DENORMALS_CONTROL) + + njs_mm_denormals(0); + + ret = njs_unit_test(njs_test, njs_nitems(njs_test), + "script tests (disabled denormals)", &opts, &stat); + if (ret != NJS_OK) { + return ret; + } + + ret = njs_unit_test(njs_disabled_denormals_test, + njs_nitems(njs_disabled_denormals_test), + "disabled denormals tests", &opts, &stat); + if (ret != NJS_OK) { + return ret; + } + + njs_mm_denormals(1); + +#else + + (void) njs_disabled_denormals_test; + +#endif + ret = njs_timezone_optional_test(&opts, &stat); if (ret != NJS_OK) { return ret; _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel