Check for overflow in QDateTime::setMSecsSinceEpoch()
When adding an offset from UTC, arithmetic may overflow. Likewise when combining a date and time (that have been offset for UTC). Also check the return from epochMSecsToLocalTime(), as it can fail; and pay attention to the status stored by setDateTime(), to notice when it hits an overflow. Fixed some tests that only passed because we neglected these checks. Extended a test to check we detect overflow in a couple of cases close to the extremes. Change-Id: I127a670302f94a07bb9b087b1b9c608b7c08785c Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
fc6629b6bb
commit
451500f8f8
@ -4014,7 +4014,7 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
|
|||||||
status |= QDateTimePrivate::ValidWhenMask;
|
status |= QDateTimePrivate::ValidWhenMask;
|
||||||
break;
|
break;
|
||||||
case Qt::OffsetFromUTC:
|
case Qt::OffsetFromUTC:
|
||||||
msecs += d->m_offsetFromUtc * MSECS_PER_SEC;
|
if (!add_overflow(msecs, d->m_offsetFromUtc * MSECS_PER_SEC, &msecs))
|
||||||
status |= QDateTimePrivate::ValidWhenMask;
|
status |= QDateTimePrivate::ValidWhenMask;
|
||||||
break;
|
break;
|
||||||
case Qt::TimeZone:
|
case Qt::TimeZone:
|
||||||
@ -4038,11 +4038,15 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
|
|||||||
QDate dt;
|
QDate dt;
|
||||||
QTime tm;
|
QTime tm;
|
||||||
QDateTimePrivate::DaylightStatus dstStatus;
|
QDateTimePrivate::DaylightStatus dstStatus;
|
||||||
QDateTimePrivate::epochMSecsToLocalTime(msecs, &dt, &tm, &dstStatus);
|
if (QDateTimePrivate::epochMSecsToLocalTime(msecs, &dt, &tm, &dstStatus)) {
|
||||||
setDateTime(d, dt, tm);
|
setDateTime(d, dt, tm);
|
||||||
|
status = getStatus(d);
|
||||||
|
}
|
||||||
|
if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime)) {
|
||||||
refreshZonedDateTime(d, spec); // FIXME: we do this again, below
|
refreshZonedDateTime(d, spec); // FIXME: we do this again, below
|
||||||
msecs = getMSecs(d);
|
msecs = getMSecs(d);
|
||||||
status = mergeDaylightStatus(getStatus(d), dstStatus);
|
status = mergeDaylightStatus(getStatus(d), dstStatus);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,6 +573,7 @@ void tst_QDateTime::setSecsSinceEpoch()
|
|||||||
QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
|
QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
|
||||||
|
|
||||||
// Only testing UTC; see fromSecsSinceEpoch() for fuller test.
|
// Only testing UTC; see fromSecsSinceEpoch() for fuller test.
|
||||||
|
dt1.setTimeSpec(Qt::UTC);
|
||||||
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
||||||
dt1.setSecsSinceEpoch(maxSeconds);
|
dt1.setSecsSinceEpoch(maxSeconds);
|
||||||
QVERIFY(dt1.isValid());
|
QVERIFY(dt1.isValid());
|
||||||
@ -727,6 +728,17 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
|||||||
QDateTime reference(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC);
|
QDateTime reference(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC);
|
||||||
QCOMPARE(dt, reference.addMSecs(msecs));
|
QCOMPARE(dt, reference.addMSecs(msecs));
|
||||||
|
|
||||||
|
// Tests that we correctly recognize when we fall off the extremities:
|
||||||
|
if (msecs == std::numeric_limits<qint64>::max()) {
|
||||||
|
QDateTime off(QDate(1970, 1, 1).startOfDay(Qt::OffsetFromUTC, 1));
|
||||||
|
off.setMSecsSinceEpoch(msecs);
|
||||||
|
QVERIFY(!off.isValid());
|
||||||
|
} else if (msecs == std::numeric_limits<qint64>::min()) {
|
||||||
|
QDateTime off(QDate(1970, 1, 1).startOfDay(Qt::OffsetFromUTC, -1));
|
||||||
|
off.setMSecsSinceEpoch(msecs);
|
||||||
|
QVERIFY(!off.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
if ((localTimeType == LocalTimeAheadOfUtc && msecs == std::numeric_limits<qint64>::max())
|
if ((localTimeType == LocalTimeAheadOfUtc && msecs == std::numeric_limits<qint64>::max())
|
||||||
|| (localTimeType == LocalTimeBehindUtc && msecs == std::numeric_limits<qint64>::min())) {
|
|| (localTimeType == LocalTimeBehindUtc && msecs == std::numeric_limits<qint64>::min())) {
|
||||||
QDateTime curt = QDate(1970, 1, 1).startOfDay(); // initially in short-form
|
QDateTime curt = QDate(1970, 1, 1).startOfDay(); // initially in short-form
|
||||||
@ -766,31 +778,38 @@ void tst_QDateTime::fromMSecsSinceEpoch()
|
|||||||
QCOMPARE(dtUtc.date(), utc.date());
|
QCOMPARE(dtUtc.date(), utc.date());
|
||||||
QCOMPARE(dtUtc.time(), utc.time());
|
QCOMPARE(dtUtc.time(), utc.time());
|
||||||
|
|
||||||
|
if (msecs == Bound::max()) { // Offset is positive, so overflows max
|
||||||
|
QVERIFY(!dtOffset.isValid());
|
||||||
|
} else {
|
||||||
QCOMPARE(dtOffset, utc);
|
QCOMPARE(dtOffset, utc);
|
||||||
QCOMPARE(dtOffset.offsetFromUtc(), 60*60);
|
QCOMPARE(dtOffset.offsetFromUtc(), 60*60);
|
||||||
if (msecs != Bound::max()) // Offset is positive, so overflows max
|
|
||||||
QCOMPARE(dtOffset.time(), utc.time().addMSecs(60*60*1000));
|
QCOMPARE(dtOffset.time(), utc.time().addMSecs(60*60*1000));
|
||||||
|
}
|
||||||
|
|
||||||
if (zoneIsCET) {
|
if (zoneIsCET) {
|
||||||
QCOMPARE(dtLocal.toLocalTime(), cet);
|
QCOMPARE(dtLocal.toLocalTime(), cet);
|
||||||
QCOMPARE(dtUtc.toLocalTime(), cet);
|
QCOMPARE(dtUtc.toLocalTime(), cet);
|
||||||
|
if (msecs != Bound::max())
|
||||||
QCOMPARE(dtOffset.toLocalTime(), cet);
|
QCOMPARE(dtOffset.toLocalTime(), cet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!localOverflow)
|
if (!localOverflow)
|
||||||
QCOMPARE(dtLocal.toMSecsSinceEpoch(), msecs);
|
QCOMPARE(dtLocal.toMSecsSinceEpoch(), msecs);
|
||||||
QCOMPARE(dtUtc.toMSecsSinceEpoch(), msecs);
|
QCOMPARE(dtUtc.toMSecsSinceEpoch(), msecs);
|
||||||
|
if (msecs != Bound::max())
|
||||||
QCOMPARE(dtOffset.toMSecsSinceEpoch(), msecs);
|
QCOMPARE(dtOffset.toMSecsSinceEpoch(), msecs);
|
||||||
|
|
||||||
if (!localOverflow)
|
if (!localOverflow)
|
||||||
QCOMPARE(qint64(dtLocal.toSecsSinceEpoch()), msecs / 1000);
|
QCOMPARE(qint64(dtLocal.toSecsSinceEpoch()), msecs / 1000);
|
||||||
QCOMPARE(qint64(dtUtc.toSecsSinceEpoch()), msecs / 1000);
|
QCOMPARE(qint64(dtUtc.toSecsSinceEpoch()), msecs / 1000);
|
||||||
|
if (msecs != Bound::max())
|
||||||
QCOMPARE(qint64(dtOffset.toSecsSinceEpoch()), msecs / 1000);
|
QCOMPARE(qint64(dtOffset.toSecsSinceEpoch()), msecs / 1000);
|
||||||
|
|
||||||
QDateTime reference(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC);
|
QDateTime reference(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC);
|
||||||
if (!localOverflow)
|
if (!localOverflow)
|
||||||
QCOMPARE(dtLocal, reference.addMSecs(msecs));
|
QCOMPARE(dtLocal, reference.addMSecs(msecs));
|
||||||
QCOMPARE(dtUtc, reference.addMSecs(msecs));
|
QCOMPARE(dtUtc, reference.addMSecs(msecs));
|
||||||
|
if (msecs != Bound::max())
|
||||||
QCOMPARE(dtOffset, reference.addMSecs(msecs));
|
QCOMPARE(dtOffset, reference.addMSecs(msecs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,10 +834,10 @@ void tst_QDateTime::fromSecsSinceEpoch()
|
|||||||
QVERIFY(!QDateTime::fromSecsSinceEpoch(first - 1).isValid());
|
QVERIFY(!QDateTime::fromSecsSinceEpoch(first - 1).isValid());
|
||||||
|
|
||||||
// Use an offset for which .toUTC()'s return would flip the validity:
|
// Use an offset for which .toUTC()'s return would flip the validity:
|
||||||
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::OffsetFromUTC, 7200).isValid());
|
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds - 7200, Qt::OffsetFromUTC, 7200).isValid());
|
||||||
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds + 1, Qt::OffsetFromUTC, -7200).isValid());
|
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds - 7199, Qt::OffsetFromUTC, 7200).isValid());
|
||||||
QVERIFY(QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::OffsetFromUTC, -7200).isValid());
|
QVERIFY(QDateTime::fromSecsSinceEpoch(7200 - maxSeconds, Qt::OffsetFromUTC, -7200).isValid());
|
||||||
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1, Qt::OffsetFromUTC, 7200).isValid());
|
QVERIFY(!QDateTime::fromSecsSinceEpoch(7199 - maxSeconds, Qt::OffsetFromUTC, -7200).isValid());
|
||||||
|
|
||||||
#if QT_CONFIG(timezone)
|
#if QT_CONFIG(timezone)
|
||||||
// As for offset, use zones each side of UTC:
|
// As for offset, use zones each side of UTC:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user