diff options
author | Konstantin Shegunov <kshegunov@gmail.com> | 2018-08-01 00:50:14 +0300 |
---|---|---|
committer | Konstantin Shegunov <kshegunov@gmail.com> | 2019-05-08 17:19:44 +0000 |
commit | c6bee8e4b2c48775247c67ef8e7569f623655c61 (patch) | |
tree | a3bfc28da2b37935129ebf16425de58336e3a0e6 /tests/auto | |
parent | 8adcfa8b240876236f08ad826e6d77ff1f5e9a96 (diff) |
Fix integer overflows in QDeadlineTimer
If the deadline is far in the future, the conversions to nanoseconds
or internal arithmetic may overflow and give an invalid object, thus
the deadline may end up in the past. Added a test to the testlib
selftest for sleep.
[ChangeLog][QtCore][QDeadlineTimer] Fixed integer overflows
leading to immediate timeouts.
Task-number: QTBUG-69750
Change-Id: I9814eccdf9f9b3add9ca66ec3e27e10cd5ad54a8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'tests/auto')
8 files changed, 117 insertions, 6 deletions
diff --git a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp index 6ab24d2480..4ca68550b9 100644 --- a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp +++ b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp @@ -29,6 +29,7 @@ #include <QtCore/QString> #include <QtCore/QTime> #include <QtCore/QDeadlineTimer> +#include <QtCore/QElapsedTimer> #include <QtTest/QtTest> #if QT_HAS_INCLUDE(<chrono>) @@ -50,6 +51,7 @@ private Q_SLOTS: void current(); void deadlines(); void setDeadline(); + void overflow(); void expire(); void stdchrono(); }; @@ -417,6 +419,83 @@ void tst_QDeadlineTimer::setDeadline() QCOMPARE(deadline.deadlineNSecs(), nsec); } +void tst_QDeadlineTimer::overflow() +{ + QFETCH_GLOBAL(Qt::TimerType, timerType); + // Check the constructor for overflows (should also cover saturating the result of the deadline() method if overflowing) + QDeadlineTimer now = QDeadlineTimer::current(timerType), deadline(std::numeric_limits<qint64>::max() - 1, timerType); + QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline()); + + // Check the setDeadline with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max) + deadline.setDeadline(std::numeric_limits<qint64>::max() - 1, timerType); + QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline()); + + // Check the setRemainingTime with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max) + deadline.setRemainingTime(std::numeric_limits<qint64>::max() - 1, timerType); + QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline()); + + // Check that the deadline gets saturated when the arguments of setPreciseDeadline are large + deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType); + QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max()); + QVERIFY(deadline.isForever()); + + // Check that remainingTime gets saturated if we overflow + deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType); + QCOMPARE(deadline.remainingTime(), qint64(-1)); + QVERIFY(deadline.isForever()); + + // Check that we saturate the getter for nanoseconds + deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, 0, timerType); + QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max()); + + // Check that adding nanoseconds and overflowing is consistent and saturates the timer + deadline = QDeadlineTimer::addNSecs(deadline, std::numeric_limits<qint64>::max() - 1); + QVERIFY(deadline.isForever()); + + // Make sure forever is forever, regardless of us subtracting time from it + deadline = QDeadlineTimer(QDeadlineTimer::Forever, timerType); + deadline = QDeadlineTimer::addNSecs(deadline, -10000); + QVERIFY(deadline.isForever()); + + // Make sure we get the correct result when moving the deadline back and forth in time + QDeadlineTimer current = QDeadlineTimer::current(timerType); + QDeadlineTimer takenNSecs = QDeadlineTimer::addNSecs(current, -1000); + QVERIFY(takenNSecs.deadlineNSecs() - current.deadlineNSecs() == -1000); + QDeadlineTimer addedNSecs = QDeadlineTimer::addNSecs(current, 1000); + QVERIFY(addedNSecs.deadlineNSecs() - current.deadlineNSecs() == 1000); + + // Make sure the calculation goes as expected when we need to subtract nanoseconds + // We make use of an additional timer to be certain that + // even when the environment is under load we can track the + // time needed to do the calls + static constexpr qint64 nsExpected = 1000 * 1000 * 1000 - 1000; // 1s - 1000ns, what we pass to setPreciseRemainingTime() later + + QElapsedTimer callTimer; + callTimer.start(); + + deadline = QDeadlineTimer::current(timerType); + qint64 nsDeadline = deadline.deadlineNSecs(); + // We adjust in relation to current() here, so we expect the difference to be a tad over the exact number. + // However we are tracking the elapsed time, so it shouldn't be a problem. + deadline.setPreciseRemainingTime(1, -1000, timerType); + qint64 difference = (deadline.deadlineNSecs() - nsDeadline) - nsExpected; + QVERIFY(difference >= 0); // Should always be true, but just in case + QVERIFY(difference <= callTimer.nsecsElapsed()); // Ideally difference should be 0 exactly + + // Make sure setRemainingTime underflows gracefully + deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::min() / 10, 0, timerType); + QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever + QVERIFY(deadline.remainingTime() == 0); + // If the timer is saturated we don't want to get a valid number of milliseconds + QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min()); + + // Check that the conversion to milliseconds and nanoseconds underflows gracefully + deadline.setPreciseDeadline(std::numeric_limits<qint64>::min() / 10, 0, timerType); + QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever + QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min()); + QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min()); +} + void tst_QDeadlineTimer::expire() { QFETCH_GLOBAL(Qt::TimerType, timerType); diff --git a/tests/auto/testlib/selftests/expected_sleep.lightxml b/tests/auto/testlib/selftests/expected_sleep.lightxml index 97b65d1259..78e1c44cf2 100644 --- a/tests/auto/testlib/selftests/expected_sleep.lightxml +++ b/tests/auto/testlib/selftests/expected_sleep.lightxml @@ -11,6 +11,10 @@ <Incident type="pass" file="" line="0" /> <Duration msecs="0"/> </TestFunction> +<TestFunction name="wait"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> <TestFunction name="cleanupTestCase"> <Incident type="pass" file="" line="0" /> <Duration msecs="0"/> diff --git a/tests/auto/testlib/selftests/expected_sleep.tap b/tests/auto/testlib/selftests/expected_sleep.tap index 7caef214d2..65edefb9ba 100644 --- a/tests/auto/testlib/selftests/expected_sleep.tap +++ b/tests/auto/testlib/selftests/expected_sleep.tap @@ -2,8 +2,9 @@ TAP version 13 # tst_Sleep ok 1 - initTestCase() ok 2 - sleep() -ok 3 - cleanupTestCase() -1..3 -# tests 3 -# pass 3 +ok 3 - wait() +ok 4 - cleanupTestCase() +1..4 +# tests 4 +# pass 4 # fail 0 diff --git a/tests/auto/testlib/selftests/expected_sleep.teamcity b/tests/auto/testlib/selftests/expected_sleep.teamcity index 36b2445ccc..45a503bb54 100644 --- a/tests/auto/testlib/selftests/expected_sleep.teamcity +++ b/tests/auto/testlib/selftests/expected_sleep.teamcity @@ -3,6 +3,8 @@ ##teamcity[testFinished name='initTestCase()' flowId='tst_Sleep'] ##teamcity[testStarted name='sleep()' flowId='tst_Sleep'] ##teamcity[testFinished name='sleep()' flowId='tst_Sleep'] +##teamcity[testStarted name='wait()' flowId='tst_Sleep'] +##teamcity[testFinished name='wait()' flowId='tst_Sleep'] ##teamcity[testStarted name='cleanupTestCase()' flowId='tst_Sleep'] ##teamcity[testFinished name='cleanupTestCase()' flowId='tst_Sleep'] ##teamcity[testSuiteFinished name='tst_Sleep' flowId='tst_Sleep'] diff --git a/tests/auto/testlib/selftests/expected_sleep.txt b/tests/auto/testlib/selftests/expected_sleep.txt index d18c3212bb..d1701d2bed 100644 --- a/tests/auto/testlib/selftests/expected_sleep.txt +++ b/tests/auto/testlib/selftests/expected_sleep.txt @@ -2,6 +2,7 @@ Config: Using QtTest library PASS : tst_Sleep::initTestCase() PASS : tst_Sleep::sleep() +PASS : tst_Sleep::wait() PASS : tst_Sleep::cleanupTestCase() -Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms +Totals: 4 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms ********* Finished testing of tst_Sleep ********* diff --git a/tests/auto/testlib/selftests/expected_sleep.xml b/tests/auto/testlib/selftests/expected_sleep.xml index 5729c0ac9d..94bb25ba8d 100644 --- a/tests/auto/testlib/selftests/expected_sleep.xml +++ b/tests/auto/testlib/selftests/expected_sleep.xml @@ -13,6 +13,10 @@ <Incident type="pass" file="" line="0" /> <Duration msecs="0"/> </TestFunction> +<TestFunction name="wait"> +<Incident type="pass" file="" line="0" /> + <Duration msecs="0"/> +</TestFunction> <TestFunction name="cleanupTestCase"> <Incident type="pass" file="" line="0" /> <Duration msecs="0"/> diff --git a/tests/auto/testlib/selftests/expected_sleep.xunitxml b/tests/auto/testlib/selftests/expected_sleep.xunitxml index 138486fc02..e4ed66bcb8 100644 --- a/tests/auto/testlib/selftests/expected_sleep.xunitxml +++ b/tests/auto/testlib/selftests/expected_sleep.xunitxml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<testsuite errors="0" failures="0" tests="3" name="tst_Sleep"> +<testsuite errors="0" failures="0" tests="4" name="tst_Sleep"> <properties> <property value="@INSERT_QT_VERSION_HERE@" name="QTestVersion"/> <property value="@INSERT_QT_VERSION_HERE@" name="QtVersion"/> @@ -7,6 +7,7 @@ </properties> <testcase result="pass" name="initTestCase"/> <testcase result="pass" name="sleep"/> + <testcase result="pass" name="wait"/> <testcase result="pass" name="cleanupTestCase"/> <system-err/> </testsuite> diff --git a/tests/auto/testlib/selftests/sleep/tst_sleep.cpp b/tests/auto/testlib/selftests/sleep/tst_sleep.cpp index afe6e22120..b7b141afd0 100644 --- a/tests/auto/testlib/selftests/sleep/tst_sleep.cpp +++ b/tests/auto/testlib/selftests/sleep/tst_sleep.cpp @@ -36,6 +36,7 @@ class tst_Sleep: public QObject private slots: void sleep(); + void wait(); }; void tst_Sleep::sleep() @@ -53,6 +54,24 @@ void tst_Sleep::sleep() QVERIFY(t.elapsed() > 1000 * 10); } +void tst_Sleep::wait() +{ + QElapsedTimer t; + t.start(); + + QTest::qWait(1); + QVERIFY(t.elapsed() >= 1); + + QTest::qWait(10); + QVERIFY(t.elapsed() >= 11); + + QTest::qWait(100); + QVERIFY(t.elapsed() >= 111); + + QTest::qWait(1000); + QVERIFY(t.elapsed() >= 1111); +} + QTEST_MAIN(tst_Sleep) #include "tst_sleep.moc" |