From 032fe51a941d11fb0176bb8b1d8bbc50cff6849f Mon Sep 17 00:00:00 2001 From: Nick Vatamaniuc Date: Mon, 10 Mar 2025 22:09:36 -0400 Subject: [PATCH] fixed date parsing in case there is more than nine initial digits Backport from upstream: https://github.com/bellard/quickjs/commit/030333cff616043858b32dc32405f9776372a0e6 Most engines like v8, and current versions of spidermonkey versions (v128 at least) return NaN while QuickJS parses up to 9 digits at a time, then tries to parse the rest. Trying to parse extra digits can sometimes produce random garbage. To fix it, when parsing the initial integer parse as many digits as we can (max = 0) instead of just 9. Add a few tests, including uncommenting some previous ones, and ensure they pass on v8 version 11 (upstream didn't include the extra tests). --- quickjs.c | 9 ++++++--- tests/test_builtin.js | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/quickjs.c b/quickjs.c index cb87b50e9..d95f27bb8 100644 --- a/quickjs.c +++ b/quickjs.c @@ -50670,6 +50670,9 @@ static bool string_get_digits(const uint8_t *sp, int *pp, int *pval, p_start = p; while ((c = sp[p]) >= '0' && c <= '9') { + /* arbitrary limit to 9 digits */ + if (v >= 100000000) + return false; v = v * 10 + c - '0'; p++; if (p - p_start == max_digits) @@ -50713,7 +50716,7 @@ static bool string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, bool stric sgn = sp[p++]; if (sgn == '+' || sgn == '-') { int n = p; - if (!string_get_digits(sp, &p, &hh, 1, 9)) + if (!string_get_digits(sp, &p, &hh, 1, 0)) return false; n = p - n; if (strict && n != 2 && n != 4) @@ -50907,7 +50910,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, *is_local = false; } else { p++; - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (c == '-') { if (val == 0) return false; @@ -50918,7 +50921,7 @@ static bool js_date_parse_otherstring(const uint8_t *sp, } } } else - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (string_skip_char(sp, &p, ':')) { /* time part */ fields[3] = val; diff --git a/tests/test_builtin.js b/tests/test_builtin.js index c6ea5bb82..3c37f5930 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -608,11 +608,26 @@ function test_date() // Hence the fractional part after . should have 3 digits and how // a different number of digits is handled is implementation defined. assert(Date.parse(""), NaN); + assert(Date.parse("13"), NaN); + assert(Date.parse("31"), NaN); + assert(Date.parse("1000"), -30610224000000); + assert(Date.parse("1969"), -31536000000); + assert(Date.parse("1970"), 0); assert(Date.parse("2000"), 946684800000); + assert(Date.parse("9999"), 253370764800000); + assert(Date.parse("275761"), NaN); + assert(Date.parse("999999"), NaN); + assert(Date.parse("1000000000"), NaN); + assert(Date.parse("-271821"), NaN); + assert(Date.parse("-271820"), -8639977881600000); + assert(Date.parse("-100000"), -3217862419200000); + assert(Date.parse("+100000"), 3093527980800000); + assert(Date.parse("+275760"), 8639977881600000); + assert(Date.parse("+275761"), NaN); assert(Date.parse("2000-01"), 946684800000); assert(Date.parse("2000-01-01"), 946684800000); - //assert(Date.parse("2000-01-01T"), NaN); - //assert(Date.parse("2000-01-01T00Z"), NaN); + assert(Date.parse("2000-01-01T"), NaN); + assert(Date.parse("2000-01-01T00Z"), NaN); assert(Date.parse("2000-01-01T00:00Z"), 946684800000); assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000); assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100);