Avoid overflow in some more qdatetime.cpp functions
One multiplied TIME_T_MAX * MSECS_PER_SEC despite knowing this to be too close for comfort to the bounds of qint64; but the comment indicated it should have been using a 32-bit signed max in place of TIME_T_MAX. Two others simply neglected to check for overflow. Also use QRoundingDown::qMod() in one place to avoid needlessly complicating arithmetic; and assert, in another, that we were never in danger of overflowing anyway (thanks to an earlier isValid() check). Restructured QDateTimePrivate::zoneMSecsToEpochMSecs() in the process of catching its potential overflow. Change-Id: I429321d90246ba922cccf09a4f028f03a514cb6b Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
131c7009fa
commit
460d7c8780
@ -47,6 +47,7 @@
|
|||||||
#include "qset.h"
|
#include "qset.h"
|
||||||
#include "qlocale.h"
|
#include "qlocale.h"
|
||||||
|
|
||||||
|
#include "private/qcalendarmath_p.h"
|
||||||
#include "private/qdatetime_p.h"
|
#include "private/qdatetime_p.h"
|
||||||
#if QT_CONFIG(datetimeparser)
|
#if QT_CONFIG(datetimeparser)
|
||||||
#include "private/qdatetimeparser_p.h"
|
#include "private/qdatetimeparser_p.h"
|
||||||
@ -1957,6 +1958,7 @@ bool QTime::setHMS(int h, int m, int s, int ms)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mds = (h * SECS_PER_HOUR + m * SECS_PER_MIN + s) * MSECS_PER_SEC + ms;
|
mds = (h * SECS_PER_HOUR + m * SECS_PER_MIN + s) * MSECS_PER_SEC + ms;
|
||||||
|
Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2022,15 +2024,8 @@ int QTime::secsTo(QTime t) const
|
|||||||
QTime QTime::addMSecs(int ms) const
|
QTime QTime::addMSecs(int ms) const
|
||||||
{
|
{
|
||||||
QTime t;
|
QTime t;
|
||||||
if (isValid()) {
|
if (isValid())
|
||||||
if (ms < 0) {
|
t.mds = QRoundingDown::qMod(ds() + ms, MSECS_PER_DAY);
|
||||||
// %,/ not well-defined for -ve, so always work with +ve.
|
|
||||||
int negdays = (MSECS_PER_DAY - ms) / MSECS_PER_DAY;
|
|
||||||
t.mds = (ds() + ms + negdays * MSECS_PER_DAY) % MSECS_PER_DAY;
|
|
||||||
} else {
|
|
||||||
t.mds = (ds() + ms) % MSECS_PER_DAY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2690,8 +2685,18 @@ static void msecsToTime(qint64 msecs, QDate *date, QTime *time)
|
|||||||
// Converts a date/time value into msecs
|
// Converts a date/time value into msecs
|
||||||
static qint64 timeToMSecs(QDate date, QTime time)
|
static qint64 timeToMSecs(QDate date, QTime time)
|
||||||
{
|
{
|
||||||
return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY)
|
qint64 days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH;
|
||||||
+ time.msecsSinceStartOfDay();
|
qint64 msecs, dayms = time.msecsSinceStartOfDay();
|
||||||
|
if (days < 0 && dayms > 0) {
|
||||||
|
++days;
|
||||||
|
dayms -= MSECS_PER_DAY;
|
||||||
|
}
|
||||||
|
if (mul_overflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), &msecs)
|
||||||
|
|| add_overflow(msecs, dayms, &msecs)) {
|
||||||
|
using Bound = std::numeric_limits<qint64>;
|
||||||
|
return days < 0 ? Bound::min() : Bound::max();
|
||||||
|
}
|
||||||
|
return msecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -2753,8 +2758,10 @@ static auto computeSystemMillisRange()
|
|||||||
// MS's end-of-range, end of year 3000:
|
// MS's end-of-range, end of year 3000:
|
||||||
{ 3000, Q_INT64_C(32535215999999) },
|
{ 3000, Q_INT64_C(32535215999999) },
|
||||||
};
|
};
|
||||||
// Assume we do at least reach the end of 32-bit time_t:
|
// Assume we do at least reach the end of a signed 32-bit time_t (since
|
||||||
qint64 stop = quint64(TIME_T_MAX) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
|
// our actual time_t is bigger than that):
|
||||||
|
qint64 stop =
|
||||||
|
quint64(std::numeric_limits<qint32>::max()) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
|
||||||
// Cleared if first pass round loop fails:
|
// Cleared if first pass round loop fails:
|
||||||
bool stopMax = true;
|
bool stopMax = true;
|
||||||
for (const auto c : ends) {
|
for (const auto c : ends) {
|
||||||
@ -3424,26 +3431,27 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
|
|||||||
// Get the effective data from QTimeZone
|
// Get the effective data from QTimeZone
|
||||||
DaylightStatus dst = hint ? *hint : UnknownDaylightTime;
|
DaylightStatus dst = hint ? *hint : UnknownDaylightTime;
|
||||||
QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(dst));
|
QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(dst));
|
||||||
if (data.offsetFromUtc == QTimeZonePrivate::invalidSeconds()) {
|
const bool badDateTime = data.offsetFromUtc == QTimeZonePrivate::invalidSeconds();
|
||||||
if (hint)
|
Q_ASSERT(badDateTime
|
||||||
*hint = QDateTimePrivate::UnknownDaylightTime;
|
|| zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc);
|
||||||
if (abbreviation)
|
if (hint) {
|
||||||
*abbreviation = QString();
|
*hint = badDateTime
|
||||||
|
? QDateTimePrivate::UnknownDaylightTime
|
||||||
|
: (data.daylightTimeOffset
|
||||||
|
? QDateTimePrivate::DaylightTime
|
||||||
|
: QDateTimePrivate::StandardTime);
|
||||||
|
}
|
||||||
|
if (abbreviation)
|
||||||
|
*abbreviation = badDateTime ? QString() : data.abbreviation;
|
||||||
|
qint64 msecs;
|
||||||
|
if (badDateTime ||
|
||||||
|
add_overflow(data.offsetFromUtc * MSECS_PER_SEC, data.atMSecsSinceEpoch, &msecs)) {
|
||||||
if (zoneDate)
|
if (zoneDate)
|
||||||
*zoneDate = QDate();
|
*zoneDate = QDate();
|
||||||
if (zoneTime)
|
if (zoneTime)
|
||||||
*zoneTime = QTime();
|
*zoneTime = QTime();
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc);
|
msecsToTime(msecs, zoneDate, zoneTime);
|
||||||
msecsToTime(data.atMSecsSinceEpoch + data.offsetFromUtc * MSECS_PER_SEC,
|
|
||||||
zoneDate, zoneTime);
|
|
||||||
if (hint) {
|
|
||||||
*hint = data.daylightTimeOffset
|
|
||||||
? QDateTimePrivate::DaylightTime
|
|
||||||
: QDateTimePrivate::StandardTime;
|
|
||||||
}
|
|
||||||
if (abbreviation)
|
|
||||||
*abbreviation = data.abbreviation;
|
|
||||||
}
|
}
|
||||||
return data.atMSecsSinceEpoch;
|
return data.atMSecsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user