Bug 1158399 - Ensure/assert that DateObject::setUTCTime never stores a non-TimeClip'd value in the reserved slot. r=evilpie

This commit is contained in:
Jeff Walden 2015-04-30 20:03:30 -07:00
parent c1dad444dd
commit d88c2a427a
7 changed files with 112 additions and 90 deletions

View File

@ -6,10 +6,46 @@
#ifndef js_Date_h
#define js_Date_h
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"
#include "jstypes.h"
#include "js/Conversions.h"
#include "js/Value.h"
namespace JS {
class ClippedTime
{
double t;
/* ES5 15.9.1.14. */
double timeClip(double time) {
/* Steps 1-2. */
const double MaxTimeMagnitude = 8.64e15;
if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
return JS::GenericNaN();
/* Step 3. */
return JS::ToInteger(time) + (+0.0);
}
public:
ClippedTime() : t(JS::GenericNaN()) {}
explicit ClippedTime(double time) : t(timeClip(time)) {}
static ClippedTime NaN() { return ClippedTime(); }
double value() const { return t; }
};
inline ClippedTime
TimeClip(double d)
{
return ClippedTime(d);
}
// Year is a year, month is 0-11, day is 1-based. The return value is
// a number of milliseconds since the epoch. Can return NaN.
JS_PUBLIC_API(double)

View File

@ -61,6 +61,7 @@
#include "jit/JitCommon.h"
#include "js/CharacterEncoding.h"
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/Proxy.h"
#include "js/SliceBudget.h"
#include "js/StructuredClone.h"
@ -5237,7 +5238,7 @@ JS_NewDateObjectMsec(JSContext* cx, double msec)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return NewDateObjectMsec(cx, msec);
return NewDateObjectMsec(cx, JS::TimeClip(msec));
}
JS_PUBLIC_API(bool)

View File

@ -50,9 +50,12 @@ using namespace js;
using mozilla::ArrayLength;
using mozilla::IsFinite;
using mozilla::IsNaN;
using mozilla::NumbersAreIdentical;
using JS::AutoCheckCannotGC;
using JS::ClippedTime;
using JS::GenericNaN;
using JS::TimeClip;
using JS::ToInteger;
/*
@ -350,7 +353,7 @@ MakeDate(double day, double time)
JS_PUBLIC_API(double)
JS::MakeDate(double year, unsigned month, unsigned day)
{
return TimeClip(::MakeDate(MakeDay(year, month, day), 0));
return TimeClip(::MakeDate(MakeDay(year, month, day), 0)).value();
}
JS_PUBLIC_API(double)
@ -566,14 +569,6 @@ RegionMatches(const char* s1, int s1off, const CharT* s2, int s2off, int count)
return count == 0;
}
/* find UTC time from given date... no 1900 correction! */
static double
date_msecFromDate(double year, double mon, double mday, double hour,
double min, double sec, double msec)
{
return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
}
/* ES6 20.3.3.4. */
static bool
date_UTC(JSContext* cx, unsigned argc, Value* vp)
@ -644,7 +639,8 @@ date_UTC(JSContext* cx, unsigned argc, Value* vp)
}
// Step 16.
args.rval().setDouble(TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))));
ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
args.rval().setDouble(time.value());
return true;
}
@ -777,7 +773,7 @@ DaysInMonth(int year, int month)
*/
template <typename CharT>
static bool
ParseISODate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo)
ParseISODate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtInfo)
{
size_t i = 0;
int tzMul = 1;
@ -873,19 +869,16 @@ ParseISODate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo
month -= 1; /* convert month to 0-based */
double msec = date_msecFromDate(dateMul * double(year), month, day,
hour, min, sec, frac * 1000.0);
double msec = MakeDate(MakeDay(dateMul * double(year), month, day),
MakeTime(hour, min, sec, frac * 1000.0));
if (isLocalTime)
msec = UTC(msec, dtInfo);
else
msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
if (msec < -8.64e15 || msec > 8.64e15)
return false;
*result = msec;
return true;
*result = TimeClip(msec);
return NumbersAreIdentical(msec, result->value());
#undef PEEK
#undef NEED
@ -895,7 +888,7 @@ ParseISODate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo
template <typename CharT>
static bool
ParseDate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo)
ParseDate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtInfo)
{
if (ParseISODate(s, length, result, dtInfo))
return true;
@ -1157,19 +1150,19 @@ ParseDate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo)
if (hour < 0)
hour = 0;
double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
double msec = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0));
if (tzOffset == -1) /* no time zone specified, have to use local */
msec = UTC(msec, dtInfo);
else
msec += tzOffset * msPerMinute;
*result = msec;
*result = TimeClip(msec);
return true;
}
static bool
ParseDate(JSLinearString* s, double* result, DateTimeInfo* dtInfo)
ParseDate(JSLinearString* s, ClippedTime* result, DateTimeInfo* dtInfo)
{
AutoCheckCannotGC nogc;
return s->hasLatin1Chars()
@ -1194,45 +1187,44 @@ date_parse(JSContext* cx, unsigned argc, Value* vp)
if (!linearStr)
return false;
double result;
ClippedTime result;
if (!ParseDate(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
args.rval().setNaN();
return true;
}
result = TimeClip(result);
args.rval().setNumber(result);
args.rval().setDouble(result.value());
return true;
}
static inline double
static ClippedTime
NowAsMillis()
{
return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
return ClippedTime(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC);
}
bool
js::date_now(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setDouble(NowAsMillis());
args.rval().setDouble(NowAsMillis().value());
return true;
}
void
DateObject::setUTCTime(double t)
DateObject::setUTCTime(ClippedTime t)
{
for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
setReservedSlot(ind, UndefinedValue());
setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
setFixedSlot(UTC_TIME_SLOT, DoubleValue(t.value()));
}
void
DateObject::setUTCTime(double t, MutableHandleValue vp)
DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp)
{
setUTCTime(t);
vp.setDouble(t);
vp.setDouble(t.value());
}
void
@ -1695,7 +1687,7 @@ date_setTime_impl(JSContext* cx, CallArgs args)
{
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
if (args.length() == 0) {
dateObj->setUTCTime(GenericNaN(), args.rval());
dateObj->setUTCTime(ClippedTime::NaN(), args.rval());
return true;
}
@ -1760,7 +1752,7 @@ date_setMilliseconds_impl(JSContext* cx, CallArgs args)
double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
/* Step 3. */
double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
/* Steps 4-5. */
dateObj->setUTCTime(u, args.rval());
@ -1790,7 +1782,7 @@ date_setUTCMilliseconds_impl(JSContext* cx, CallArgs args)
double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
/* Step 3. */
double v = TimeClip(MakeDate(Day(t), time));
ClippedTime v = TimeClip(MakeDate(Day(t), time));
/* Steps 4-5. */
dateObj->setUTCTime(v, args.rval());
@ -1827,7 +1819,7 @@ date_setSeconds_impl(JSContext* cx, CallArgs args)
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
/* Step 5. */
double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
/* Steps 6-7. */
dateObj->setUTCTime(u, args.rval());
@ -1864,7 +1856,7 @@ date_setUTCSeconds_impl(JSContext* cx, CallArgs args)
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
/* Step 5. */
double v = TimeClip(date);
ClippedTime v = TimeClip(date);
/* Steps 6-7. */
dateObj->setUTCTime(v, args.rval());
@ -1906,7 +1898,7 @@ date_setMinutes_impl(JSContext* cx, CallArgs args)
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
/* Step 6. */
double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
/* Steps 7-8. */
dateObj->setUTCTime(u, args.rval());
@ -1948,7 +1940,7 @@ date_setUTCMinutes_impl(JSContext* cx, CallArgs args)
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
/* Step 6. */
double v = TimeClip(date);
ClippedTime v = TimeClip(date);
/* Steps 7-8. */
dateObj->setUTCTime(v, args.rval());
@ -1995,7 +1987,7 @@ date_setHours_impl(JSContext* cx, CallArgs args)
double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
/* Step 6. */
double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
/* Steps 7-8. */
dateObj->setUTCTime(u, args.rval());
@ -2042,7 +2034,7 @@ date_setUTCHours_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
/* Step 7. */
double v = TimeClip(newDate);
ClippedTime v = TimeClip(newDate);
/* Steps 8-9. */
dateObj->setUTCTime(v, args.rval());
@ -2074,7 +2066,7 @@ date_setDate_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
/* Step 4. */
double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
/* Steps 5-6. */
dateObj->setUTCTime(u, args.rval());
@ -2106,7 +2098,7 @@ date_setUTCDate_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
/* Step 4. */
double v = TimeClip(newDate);
ClippedTime v = TimeClip(newDate);
/* Steps 5-6. */
dateObj->setUTCTime(v, args.rval());
@ -2163,7 +2155,7 @@ date_setMonth_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
/* Step 5. */
double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
/* Steps 6-7. */
dateObj->setUTCTime(u, args.rval());
@ -2200,7 +2192,7 @@ date_setUTCMonth_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
/* Step 5. */
double v = TimeClip(newDate);
ClippedTime v = TimeClip(newDate);
/* Steps 6-7. */
dateObj->setUTCTime(v, args.rval());
@ -2258,7 +2250,7 @@ date_setFullYear_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
/* Step 6. */
double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
/* Steps 7-8. */
dateObj->setUTCTime(u, args.rval());
@ -2300,7 +2292,7 @@ date_setUTCFullYear_impl(JSContext* cx, CallArgs args)
double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
/* Step 6. */
double v = TimeClip(newDate);
ClippedTime v = TimeClip(newDate);
/* Steps 7-8. */
dateObj->setUTCTime(v, args.rval());
@ -2330,7 +2322,7 @@ date_setYear_impl(JSContext* cx, CallArgs args)
/* Step 3. */
if (IsNaN(y)) {
dateObj->setUTCTime(GenericNaN(), args.rval());
dateObj->setUTCTime(ClippedTime::NaN(), args.rval());
return true;
}
@ -2374,7 +2366,7 @@ static const char * const months[] =
static void
print_gmt_string(char* buf, size_t size, double utctime)
{
MOZ_ASSERT(TimeClip(utctime) == utctime);
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime));
JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
days[int(WeekDay(utctime))],
int(DateFromTime(utctime)),
@ -2388,7 +2380,7 @@ print_gmt_string(char* buf, size_t size, double utctime)
static void
print_iso_string(char* buf, size_t size, double utctime)
{
MOZ_ASSERT(TimeClip(utctime) == utctime);
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime));
JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
int(YearFromTime(utctime)),
int(MonthFromTime(utctime)) + 1,
@ -2402,7 +2394,7 @@ print_iso_string(char* buf, size_t size, double utctime)
static void
print_iso_extended_string(char* buf, size_t size, double utctime)
{
MOZ_ASSERT(TimeClip(utctime) == utctime);
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime));
JS_snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
int(YearFromTime(utctime)),
int(MonthFromTime(utctime)) + 1,
@ -2558,7 +2550,7 @@ date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rv
if (!IsFinite(date)) {
JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else {
MOZ_ASSERT(TimeClip(date) == date);
MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).value(), date));
double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
@ -2973,9 +2965,9 @@ static const JSFunctionSpec date_methods[] = {
};
static bool
NewDateObject(JSContext* cx, const CallArgs& args, double d)
NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
{
JSObject* obj = NewDateObjectMsec(cx, d);
JSObject* obj = NewDateObjectMsec(cx, t);
if (!obj)
return false;
@ -2984,9 +2976,9 @@ NewDateObject(JSContext* cx, const CallArgs& args, double d)
}
static bool
ToDateString(JSContext* cx, const CallArgs& args, double d)
ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t)
{
return date_format(cx, d, FORMATSPEC_FULL, args.rval());
return date_format(cx, t.value(), FORMATSPEC_FULL, args.rval());
}
static bool
@ -2994,7 +2986,7 @@ DateNoArguments(JSContext* cx, const CallArgs& args)
{
MOZ_ASSERT(args.length() == 0);
double now = NowAsMillis();
ClippedTime now = NowAsMillis();
if (args.isConstructing())
return NewDateObject(cx, args, now);
@ -3008,7 +3000,7 @@ DateOneArgument(JSContext* cx, const CallArgs& args)
MOZ_ASSERT(args.length() == 1);
if (args.isConstructing()) {
double d;
ClippedTime t;
if (!ToPrimitive(cx, args[0]))
return false;
@ -3018,17 +3010,16 @@ DateOneArgument(JSContext* cx, const CallArgs& args)
if (!linearStr)
return false;
if (!ParseDate(linearStr, &d, &cx->runtime()->dateTimeInfo))
d = GenericNaN();
else
d = TimeClip(d);
if (!ParseDate(linearStr, &t, &cx->runtime()->dateTimeInfo))
t = ClippedTime::NaN();
} else {
double d;
if (!ToNumber(cx, args[0], &d))
return false;
d = TimeClip(d);
t = TimeClip(d);
}
return NewDateObject(cx, args, d);
return NewDateObject(cx, args, t);
}
return ToDateString(cx, args, NowAsMillis());
@ -3131,7 +3122,7 @@ js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
static bool
FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto)
{
proto->as<DateObject>().setUTCTime(GenericNaN());
proto->as<DateObject>().setUTCTime(ClippedTime::NaN());
/*
* Date.prototype.toGMTString has the same initial value as
@ -3173,13 +3164,13 @@ const Class DateObject::class_ = {
}
};
JS_FRIEND_API(JSObject*)
js::NewDateObjectMsec(JSContext* cx, double msec_time)
JSObject*
js::NewDateObjectMsec(JSContext* cx, ClippedTime t)
{
JSObject* obj = NewBuiltinClassInstance(cx, &DateObject::class_);
if (!obj)
return nullptr;
obj->as<DateObject>().setUTCTime(msec_time);
obj->as<DateObject>().setUTCTime(t);
return obj;
}
@ -3188,8 +3179,8 @@ js::NewDateObject(JSContext* cx, int year, int mon, int mday,
int hour, int min, int sec)
{
MOZ_ASSERT(mon < 12);
double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
return NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo));
double msec_time = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
return NewDateObjectMsec(cx, TimeClip(UTC(msec_time, &cx->runtime()->dateTimeInfo)));
}
JS_FRIEND_API(bool)

View File

@ -13,9 +13,12 @@
#include "jstypes.h"
#include "js/Date.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "vm/DateTime.h"
namespace js {
/*
@ -26,8 +29,8 @@ namespace js {
* Construct a new Date Object from a time value given in milliseconds UTC
* since the epoch.
*/
extern JS_FRIEND_API(JSObject*)
NewDateObjectMsec(JSContext* cx, double msec_time);
extern JSObject*
NewDateObjectMsec(JSContext* cx, JS::ClippedTime t);
/*
* Construct a new Date Object from an exploded local time value.

View File

@ -9,6 +9,7 @@
#include "jsobj.h"
#include "js/Date.h"
#include "js/Value.h"
namespace js {
@ -46,8 +47,8 @@ class DateObject : public NativeObject
}
// Set UTC time to a given time and invalidate cached local time.
void setUTCTime(double t);
void setUTCTime(double t, MutableHandleValue vp);
void setUTCTime(JS::ClippedTime t);
void setUTCTime(JS::ClippedTime t, MutableHandleValue vp);
inline double cachedLocalTime(DateTimeInfo* dtInfo);

View File

@ -39,19 +39,6 @@ const unsigned SecondsPerDay = SecondsPerHour * 24;
const double StartOfTime = -8.64e15;
const double EndOfTime = 8.64e15;
const double MaxTimeMagnitude = 8.64e15;
/* ES5 15.9.1.14. */
inline double
TimeClip(double time)
{
/* Steps 1-2. */
if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
return JS::GenericNaN();
/* Step 3. */
return JS::ToInteger(time + (+0.0));
}
/*
* Stores date/time information, particularly concerning the current local

View File

@ -40,6 +40,7 @@
#include "jswrapper.h"
#include "builtin/MapObject.h"
#include "js/Date.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayObject.h"
#include "vm/WrapperObject.h"
@ -53,6 +54,7 @@ using mozilla::BitwiseCast;
using mozilla::IsNaN;
using mozilla::LittleEndian;
using mozilla::NativeEndian;
using mozilla::NumbersAreIdentical;
using JS::CanonicalizeNaN;
// When you make updates here, make sure you consider whether you need to bump the
@ -1591,12 +1593,13 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
double d;
if (!in.readDouble(&d) || !checkDouble(d))
return false;
if (!IsNaN(d) && d != TimeClip(d)) {
JS::ClippedTime t = JS::TimeClip(d);
if (!NumbersAreIdentical(d, t.value())) {
JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "date");
return false;
}
JSObject* obj = NewDateObjectMsec(context(), d);
JSObject* obj = NewDateObjectMsec(context(), t);
if (!obj)
return false;
vp.setObject(*obj);