diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2020-09-21 16:51:33 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2022-03-25 17:04:37 +0100 |
commit | 460d7c87803b47b28434cc44d7d027b3a9c48dac (patch) | |
tree | 30cd4c71976bfd8a15d196a908390696d1449593 /src | |
parent | 131c7009fa4b3969bb74b17f8e2cec8ba6e710a8 (diff) |
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 64 |
1 files changed, 36 insertions, 28 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 4400c67c16..8de7637df5 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -47,6 +47,7 @@ #include "qset.h" #include "qlocale.h" +#include "private/qcalendarmath_p.h" #include "private/qdatetime_p.h" #if QT_CONFIG(datetimeparser) #include "private/qdatetimeparser_p.h" @@ -1957,6 +1958,7 @@ bool QTime::setHMS(int h, int m, int s, int ms) return false; } mds = (h * SECS_PER_HOUR + m * SECS_PER_MIN + s) * MSECS_PER_SEC + ms; + Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY); return true; } @@ -2022,15 +2024,8 @@ int QTime::secsTo(QTime t) const QTime QTime::addMSecs(int ms) const { QTime t; - if (isValid()) { - if (ms < 0) { - // %,/ 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; - } - } + if (isValid()) + t.mds = QRoundingDown::qMod(ds() + ms, MSECS_PER_DAY); return t; } @@ -2690,8 +2685,18 @@ static void msecsToTime(qint64 msecs, QDate *date, QTime *time) // Converts a date/time value into msecs static qint64 timeToMSecs(QDate date, QTime time) { - return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY) - + time.msecsSinceStartOfDay(); + qint64 days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; + 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: { 3000, Q_INT64_C(32535215999999) }, }; - // Assume we do at least reach the end of 32-bit time_t: - qint64 stop = quint64(TIME_T_MAX) * MSECS_PER_SEC - 1 + MSECS_PER_SEC; + // Assume we do at least reach the end of a signed 32-bit time_t (since + // 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: bool stopMax = true; for (const auto c : ends) { @@ -3424,26 +3431,27 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT // Get the effective data from QTimeZone DaylightStatus dst = hint ? *hint : UnknownDaylightTime; QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(dst)); - if (data.offsetFromUtc == QTimeZonePrivate::invalidSeconds()) { - if (hint) - *hint = QDateTimePrivate::UnknownDaylightTime; - if (abbreviation) - *abbreviation = QString(); + const bool badDateTime = data.offsetFromUtc == QTimeZonePrivate::invalidSeconds(); + Q_ASSERT(badDateTime + || zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc); + if (hint) { + *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) *zoneDate = QDate(); if (zoneTime) *zoneTime = QTime(); } else { - Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc); - msecsToTime(data.atMSecsSinceEpoch + data.offsetFromUtc * MSECS_PER_SEC, - zoneDate, zoneTime); - if (hint) { - *hint = data.daylightTimeOffset - ? QDateTimePrivate::DaylightTime - : QDateTimePrivate::StandardTime; - } - if (abbreviation) - *abbreviation = data.abbreviation; + msecsToTime(msecs, zoneDate, zoneTime); } return data.atMSecsSinceEpoch; } |