Title: [290282] trunk
Revision
290282
Author
[email protected]
Date
2022-02-21 18:12:13 -0800 (Mon, 21 Feb 2022)

Log Message

[JSC] Temporal.PlainDate should validate input range
https://bugs.webkit.org/show_bug.cgi?id=236936

Reviewed by Darin Adler.

JSTests:

* stress/temporal-plaindate.js:
(shouldThrow):

Source/_javascript_Core:

Implement https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits check in
PlainDate to validate input range. For example, 0x7fffffff year should be rejected since
it is larger than ECMAScript datetime representation value. This is checked via ISODateTimeWithinLimits
in the spec.

We also remove isValid assertions in ExactTime. This should not be checked in these accessors, rather,
we should call that function when we would like to check, since PlainDate can represent a bit smaller
value than ExactTime's minValue (minValue - nsPerDay).

We also extend ExactTime::fromISOPartsAndOffset to handle values via Int128 to accept int32_t range years.
By using Int128 for nanoseconds, we can even represent int32_t max / min years. And we remove
`ASSERT(y >= -999999 && y <= 999999)` check since this is not necessary.

* runtime/ISO8601.cpp:
(JSC::ISO8601::ExactTime::fromISOPartsAndOffset):
(JSC::ISO8601::isDateTimeWithinLimits):
* runtime/ISO8601.h:
(JSC::ISO8601::ExactTime::ExactTime): Deleted.
(JSC::ISO8601::ExactTime::fromEpochSeconds): Deleted.
(JSC::ISO8601::ExactTime::fromEpochMilliseconds): Deleted.
(JSC::ISO8601::ExactTime::fromEpochMicroseconds): Deleted.
(JSC::ISO8601::ExactTime::epochSeconds const): Deleted.
(JSC::ISO8601::ExactTime::epochMilliseconds const): Deleted.
(JSC::ISO8601::ExactTime::epochMicroseconds const): Deleted.
(JSC::ISO8601::ExactTime::epochNanoseconds const): Deleted.
(JSC::ISO8601::ExactTime::nanosecondsFraction const): Deleted.
(JSC::ISO8601::ExactTime::asString const): Deleted.
(JSC::ISO8601::ExactTime::isValid const): Deleted.
(JSC::ISO8601::ExactTime::operator< const): Deleted.
(JSC::ISO8601::ExactTime::operator<= const): Deleted.
(JSC::ISO8601::ExactTime::operator== const): Deleted.
(JSC::ISO8601::ExactTime::operator!= const): Deleted.
(JSC::ISO8601::ExactTime::operator>= const): Deleted.
(JSC::ISO8601::ExactTime::operator> const): Deleted.
* runtime/TemporalPlainDate.cpp:
(JSC::toPlainDate):

Source/WTF:

Add code to allow dataLog(Int128).

* wtf/Int128.cpp:
(WTF::printInternal):
* wtf/Int128.h:

Modified Paths

Diff

Modified: trunk/JSTests/ChangeLog (290281 => 290282)


--- trunk/JSTests/ChangeLog	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/JSTests/ChangeLog	2022-02-22 02:12:13 UTC (rev 290282)
@@ -1,5 +1,15 @@
 2022-02-21  Yusuke Suzuki  <[email protected]>
 
+        [JSC] Temporal.PlainDate should validate input range
+        https://bugs.webkit.org/show_bug.cgi?id=236936
+
+        Reviewed by Darin Adler.
+
+        * stress/temporal-plaindate.js:
+        (shouldThrow):
+
+2022-02-21  Yusuke Suzuki  <[email protected]>
+
         [JSC] ShadowRealm wrapArgument should check exceptions
         https://bugs.webkit.org/show_bug.cgi?id=236984
 

Modified: trunk/JSTests/stress/temporal-plaindate.js (290281 => 290282)


--- trunk/JSTests/stress/temporal-plaindate.js	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/JSTests/stress/temporal-plaindate.js	2022-02-22 02:12:13 UTC (rev 290282)
@@ -110,6 +110,14 @@
 shouldThrow(() => { new Temporal.PlainDate(2007, -Infinity, 1); }, RangeError);
 shouldThrow(() => { new Temporal.PlainDate(2007, 1, Infinity); }, RangeError);
 shouldThrow(() => { new Temporal.PlainDate(2007, 1, -Infinity); }, RangeError);
+shouldThrow(() => { new Temporal.PlainDate(0x43530, 9, 14); }, RangeError);
+shouldBe(String(new Temporal.PlainDate(0x43530, 9, 13)), `275760-09-13`);
+shouldThrow(() => { new Temporal.PlainDate(-0x425cd, 4, 19); }, RangeError);
+shouldBe(String(new Temporal.PlainDate(-0x425cd, 4, 20)), `-271821-04-20`);
+shouldThrow(() => { new Temporal.PlainDate(0x80000000, 1, 1); }, RangeError);
+shouldThrow(() => { new Temporal.PlainDate(-0x80000000, 1, 1); }, RangeError);
+shouldThrow(() => { new Temporal.PlainDate(0x7fffffff, 1, 1); }, RangeError);
+shouldThrow(() => { new Temporal.PlainDate(-0x7fffffff, 1, 1); }, RangeError);
 
 let failures = [
     "",

Modified: trunk/Source/_javascript_Core/ChangeLog (290281 => 290282)


--- trunk/Source/_javascript_Core/ChangeLog	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/_javascript_Core/ChangeLog	2022-02-22 02:12:13 UTC (rev 290282)
@@ -1 +1,45 @@
+2022-02-21  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Temporal.PlainDate should validate input range
+        https://bugs.webkit.org/show_bug.cgi?id=236936
+
+        Reviewed by Darin Adler.
+
+        Implement https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits check in
+        PlainDate to validate input range. For example, 0x7fffffff year should be rejected since
+        it is larger than ECMAScript datetime representation value. This is checked via ISODateTimeWithinLimits
+        in the spec.
+
+        We also remove isValid assertions in ExactTime. This should not be checked in these accessors, rather,
+        we should call that function when we would like to check, since PlainDate can represent a bit smaller
+        value than ExactTime's minValue (minValue - nsPerDay).
+
+        We also extend ExactTime::fromISOPartsAndOffset to handle values via Int128 to accept int32_t range years.
+        By using Int128 for nanoseconds, we can even represent int32_t max / min years. And we remove
+        `ASSERT(y >= -999999 && y <= 999999)` check since this is not necessary.
+
+        * runtime/ISO8601.cpp:
+        (JSC::ISO8601::ExactTime::fromISOPartsAndOffset):
+        (JSC::ISO8601::isDateTimeWithinLimits):
+        * runtime/ISO8601.h:
+        (JSC::ISO8601::ExactTime::ExactTime): Deleted.
+        (JSC::ISO8601::ExactTime::fromEpochSeconds): Deleted.
+        (JSC::ISO8601::ExactTime::fromEpochMilliseconds): Deleted.
+        (JSC::ISO8601::ExactTime::fromEpochMicroseconds): Deleted.
+        (JSC::ISO8601::ExactTime::epochSeconds const): Deleted.
+        (JSC::ISO8601::ExactTime::epochMilliseconds const): Deleted.
+        (JSC::ISO8601::ExactTime::epochMicroseconds const): Deleted.
+        (JSC::ISO8601::ExactTime::epochNanoseconds const): Deleted.
+        (JSC::ISO8601::ExactTime::nanosecondsFraction const): Deleted.
+        (JSC::ISO8601::ExactTime::asString const): Deleted.
+        (JSC::ISO8601::ExactTime::isValid const): Deleted.
+        (JSC::ISO8601::ExactTime::operator< const): Deleted.
+        (JSC::ISO8601::ExactTime::operator<= const): Deleted.
+        (JSC::ISO8601::ExactTime::operator== const): Deleted.
+        (JSC::ISO8601::ExactTime::operator!= const): Deleted.
+        (JSC::ISO8601::ExactTime::operator>= const): Deleted.
+        (JSC::ISO8601::ExactTime::operator> const): Deleted.
+        * runtime/TemporalPlainDate.cpp:
+        (JSC::toPlainDate):
+
 == Rolled over to ChangeLog-2022-02-22 ==

Modified: trunk/Source/_javascript_Core/runtime/ISO8601.cpp (290281 => 290282)


--- trunk/Source/_javascript_Core/runtime/ISO8601.cpp	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/_javascript_Core/runtime/ISO8601.cpp	2022-02-22 02:12:13 UTC (rev 290282)
@@ -1192,23 +1192,20 @@
     return true;
 }
 
-ExactTime ExactTime::fromISOPartsAndOffset(int32_t y, uint8_t mon, uint8_t d, unsigned h, unsigned min, unsigned s, unsigned ms, unsigned micros, unsigned ns, int64_t offset)
+ExactTime ExactTime::fromISOPartsAndOffset(int32_t year, uint8_t month, uint8_t day, unsigned hour, unsigned minute, unsigned second, unsigned millisecond, unsigned microsecond, unsigned nanosecond, int64_t offset)
 {
-    ASSERT(y >= -999999 && y <= 999999);
-    ASSERT(mon >= 1 && mon <= 12);
-    ASSERT(d >= 1 && d <= 31);
-    ASSERT(h <= 23);
-    ASSERT(min <= 59);
-    ASSERT(s <= 59);
-    ASSERT(ms <= 999);
-    ASSERT(micros <= 999);
-    ASSERT(ns <= 999);
+    ASSERT(month >= 1 && month <= 12);
+    ASSERT(day >= 1 && day <= 31);
+    ASSERT(hour <= 23);
+    ASSERT(minute <= 59);
+    ASSERT(second <= 59);
+    ASSERT(millisecond <= 999);
+    ASSERT(microsecond <= 999);
+    ASSERT(nanosecond <= 999);
 
-    double dateDays = dateToDaysFrom1970(y, mon - 1, d);
-    double timeMs = timeToMS(h, min, s, ms);
-    double utcMs = dateDays * msPerDay + timeMs;
-    Int128 utcNs = static_cast<Int128>(utcMs) * 1'000'000 + micros * 1000 + ns;
-    return ExactTime { utcNs - offset };
+    Int128 dateDays = static_cast<Int128>(dateToDaysFrom1970(year, month - 1, day));
+    Int128 utcNanoseconds = dateDays * nsPerDay + hour * nsPerHour + minute * nsPerMinute + second * nsPerSecond + millisecond * nsPerMillisecond + microsecond * nsPerMicrosecond + nanosecond;
+    return ExactTime { utcNanoseconds - offset };
 }
 
 using CheckedInt128 = Checked<Int128, RecordOverflow>;
@@ -1321,5 +1318,16 @@
     return ExactTime { WTF::currentTimeInNanoseconds() };
 }
 
+// https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits
+bool isDateTimeWithinLimits(int32_t year, uint8_t month, uint8_t day, unsigned hour, unsigned minute, unsigned second, unsigned millisecond, unsigned microsecond, unsigned nanosecond)
+{
+    Int128 nanoseconds = ExactTime::fromISOPartsAndOffset(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, 0).epochNanoseconds();
+    if (nanoseconds <= (ExactTime::minValue - ExactTime::nsPerDay))
+        return false;
+    if (nanoseconds >= (ExactTime::maxValue + ExactTime::nsPerDay))
+        return false;
+    return true;
+}
+
 } // namespace ISO8601
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/ISO8601.h (290281 => 290282)


--- trunk/Source/_javascript_Core/runtime/ISO8601.h	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/_javascript_Core/runtime/ISO8601.h	2022-02-22 02:12:13 UTC (rev 290282)
@@ -83,6 +83,16 @@
 class ExactTime {
     WTF_MAKE_FAST_ALLOCATED(ExactTime);
 public:
+    static constexpr Int128 dayRangeSeconds { 86400'00000000 }; // 1e8 days
+    static constexpr Int128 nsPerMicrosecond { 1000 };
+    static constexpr Int128 nsPerMillisecond { 1'000'000 };
+    static constexpr Int128 nsPerSecond { 1'000'000'000 };
+    static constexpr Int128 nsPerMinute = nsPerSecond * 60;
+    static constexpr Int128 nsPerHour = nsPerMinute * 60;
+    static constexpr Int128 nsPerDay = nsPerHour * 24;
+    static constexpr Int128 minValue = -dayRangeSeconds * nsPerSecond;
+    static constexpr Int128 maxValue = dayRangeSeconds * nsPerSecond;
+
     constexpr ExactTime() = default;
     constexpr ExactTime(const ExactTime&) = default;
     constexpr explicit ExactTime(Int128 epochNanoseconds) : m_epochNanoseconds(epochNanoseconds) { }
@@ -103,22 +113,18 @@
 
     int64_t epochSeconds() const
     {
-        ASSERT(isValid());
         return static_cast<int64_t>(m_epochNanoseconds / ExactTime::nsPerSecond);
     }
     int64_t epochMilliseconds() const
     {
-        ASSERT(isValid());
         return static_cast<int64_t>(m_epochNanoseconds / ExactTime::nsPerMillisecond);
     }
     int64_t epochMicroseconds() const
     {
-        ASSERT(isValid());
         return static_cast<int64_t>(m_epochNanoseconds / ExactTime::nsPerMicrosecond);
     }
     constexpr Int128 epochNanoseconds() const
     {
-        ASSERT(isValid());
         return m_epochNanoseconds;
     }
 
@@ -177,15 +183,6 @@
     static ExactTime now();
 
 private:
-    static constexpr Int128 dayRangeSeconds { 86400'00000000 }; // 1e8 days
-    static constexpr Int128 nsPerMicrosecond { 1000 };
-    static constexpr Int128 nsPerMillisecond { 1'000'000 };
-    static constexpr Int128 nsPerSecond { 1'000'000'000 };
-    static constexpr Int128 nsPerMinute = nsPerSecond * 60;
-    static constexpr Int128 nsPerHour = nsPerMinute * 60;
-    static constexpr Int128 minValue = -dayRangeSeconds * nsPerSecond;
-    static constexpr Int128 maxValue = dayRangeSeconds * nsPerSecond;
-
     static void asStringImpl(StringBuilder& builder, Int128 value)
     {
         if (value > 9)
@@ -308,5 +305,7 @@
 
 std::optional<ExactTime> parseInstant(StringView);
 
+bool isDateTimeWithinLimits(int32_t year, uint8_t month, uint8_t day, unsigned hour, unsigned minute, unsigned second, unsigned millisecond, unsigned microsecond, unsigned nanosecond);
+
 } // namespace ISO8601
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/TemporalPlainDate.cpp (290281 => 290282)


--- trunk/Source/_javascript_Core/runtime/TemporalPlainDate.cpp	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/_javascript_Core/runtime/TemporalPlainDate.cpp	2022-02-22 02:12:13 UTC (rev 290282)
@@ -89,22 +89,38 @@
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    double year = duration.years();
-    double month = duration.months();
-    double day = duration.days();
-    if (!(month >= 1 && month <= 12)) {
+    double yearDouble = duration.years();
+    double monthDouble = duration.months();
+    double dayDouble = duration.days();
+
+    if (!isInBounds<int32_t>(yearDouble)) {
+        throwRangeError(globalObject, scope, "year is out of range"_s);
+        return { };
+    }
+    int32_t year = static_cast<int32_t>(yearDouble);
+
+    if (!(monthDouble >= 1 && monthDouble <= 12)) {
         throwRangeError(globalObject, scope, "month is out of range"_s);
         return { };
     }
-    double daysInMonth = ISO8601::daysInMonth(static_cast<int32_t>(year), static_cast<unsigned>(month));
-    if (!(day >= 1 && day <= daysInMonth)) {
+    unsigned month = static_cast<unsigned>(monthDouble);
+
+    double daysInMonth = ISO8601::daysInMonth(year, month);
+    if (!(dayDouble >= 1 && dayDouble <= daysInMonth)) {
         throwRangeError(globalObject, scope, "day is out of range"_s);
         return { };
     }
+    unsigned day = static_cast<unsigned>(dayDouble);
+
+    if (!ISO8601::isDateTimeWithinLimits(year, month, day, 0, 0, 0, 0, 0, 0)) {
+        throwRangeError(globalObject, scope, "date time is out of range of ECMAScript representation"_s);
+        return { };
+    }
+
     return ISO8601::PlainDate {
-        static_cast<int32_t>(year),
-        static_cast<unsigned>(month),
-        static_cast<unsigned>(day)
+        year,
+        month,
+        day
     };
 }
 

Modified: trunk/Source/WTF/ChangeLog (290281 => 290282)


--- trunk/Source/WTF/ChangeLog	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/WTF/ChangeLog	2022-02-22 02:12:13 UTC (rev 290282)
@@ -1,3 +1,16 @@
+2022-02-21  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Temporal.PlainDate should validate input range
+        https://bugs.webkit.org/show_bug.cgi?id=236936
+
+        Reviewed by Darin Adler.
+
+        Add code to allow dataLog(Int128).
+
+        * wtf/Int128.cpp:
+        (WTF::printInternal):
+        * wtf/Int128.h:
+
 2022-02-18  Devin Rousso  <[email protected]>
 
         [iOS] Safari can sometimes hang while printing due to sync IPC

Modified: trunk/Source/WTF/wtf/Int128.cpp (290281 => 290282)


--- trunk/Source/WTF/wtf/Int128.cpp	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/WTF/wtf/Int128.cpp	2022-02-22 02:12:13 UTC (rev 290282)
@@ -23,6 +23,9 @@
 #include <string>
 #include <type_traits>
 #include <wtf/MathExtras.h>
+#include <wtf/PrintStream.h>
+#include <wtf/Vector.h>
+#include <wtf/text/IntegerToStringConversion.h>
 
 namespace WTF {
 
@@ -318,6 +321,29 @@
   return os << rep;
 }
 
+void printInternal(PrintStream& out, UInt128 value)
+{
+    auto vector = numberToStringUnsigned<Vector<LChar, 50>>(value);
+    vector.append('\0');
+    out.printf("%s", bitwise_cast<const char*>(vector.data()));
+}
+
+void printInternal(PrintStream& out, Int128 value)
+{
+    if (value >= 0) {
+        printInternal(out, static_cast<UInt128>(value));
+        return;
+    }
+    UInt128 positive;
+    if (value == std::numeric_limits<Int128>::min())
+        positive = static_cast<UInt128>(0x8000'0000'0000'0000ULL) << 64;
+    else
+        positive = -value;
+    auto vector = numberToStringUnsigned<Vector<LChar, 50>>(positive);
+    vector.append('\0');
+    out.printf("-%s", bitwise_cast<const char*>(vector.data()));
+}
+
 }  // namespace WTF
 
 namespace std {

Modified: trunk/Source/WTF/wtf/Int128.h (290281 => 290282)


--- trunk/Source/WTF/wtf/Int128.h	2022-02-22 02:01:09 UTC (rev 290281)
+++ trunk/Source/WTF/wtf/Int128.h	2022-02-22 02:12:13 UTC (rev 290282)
@@ -55,6 +55,7 @@
 namespace WTF {
 
 class Int128Impl;
+class PrintStream;
 
 // UInt128Impl
 //
@@ -1263,6 +1264,9 @@
 using Int128 = Int128Impl;
 #endif
 
+WTF_EXPORT_PRIVATE void printInternal(PrintStream&, UInt128);
+WTF_EXPORT_PRIVATE void printInternal(PrintStream&, Int128);
+
 }  // namespace WTF
 
 using WTF::Int128;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to