Don't call elapsed() on an invalid QElapsedTimer

It's UB, and it returns garbage.

In QWaylandWindow::timerEvent the value was unused
if the timer was invalid (but that was tested only later).

In QWaylandInputDevice / QWaylandTabletToolV2 however the
garbage value was passed on to wl_cursor_frame_and_duration.

Testcase: running tests/auto/client/client/tst_client
and tests/auto/client/tabletv2/tst_tabletv2 in a UBSAN build.

Pick-to: 6.9 6.8 6.5
Change-Id: Ie0f8610bbca49913942b2247d3e968197e12f47e
Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
This commit is contained in:
David Faure 2025-03-19 00:39:20 +01:00
parent 6580ad906a
commit f197456481
3 changed files with 8 additions and 5 deletions

View File

@ -245,7 +245,8 @@ void QWaylandInputDevice::Pointer::updateCursor()
return; return;
// Set from shape using theme // Set from shape using theme
uint time = seat()->mCursor.animationTimer.elapsed(); const QElapsedTimer &timer = seat()->mCursor.animationTimer;
const uint time = timer.isValid() ? timer.elapsed() : 0;
if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) { if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
uint duration = 0; uint duration = 0;

View File

@ -113,7 +113,8 @@ void QWaylandTabletToolV2::updateCursor()
return; return;
// Set from shape using theme // Set from shape using theme
uint time = m_tabletSeat->seat()->mCursor.animationTimer.elapsed(); QElapsedTimer &timer = m_tabletSeat->seat()->mCursor.animationTimer;
const uint time = timer.isValid() ? timer.elapsed() : 0;
if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) { if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
uint duration = 0; uint duration = 0;

View File

@ -1682,12 +1682,13 @@ void QWaylandWindow::timerEvent(QTimerEvent *event)
{ {
QMutexLocker lock(&mFrameSyncMutex); QMutexLocker lock(&mFrameSyncMutex);
bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); const bool callbackTimerValid = mFrameCallbackElapsedTimer.isValid();
if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { const bool callbackTimerExpired = callbackTimerValid && mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout);
if (!callbackTimerValid || callbackTimerExpired) {
killTimer(mFrameCallbackCheckIntervalTimerId); killTimer(mFrameCallbackCheckIntervalTimerId);
mFrameCallbackCheckIntervalTimerId = -1; mFrameCallbackCheckIntervalTimerId = -1;
} }
if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) { if (!callbackTimerValid || !callbackTimerExpired) {
return; return;
} }
mFrameCallbackElapsedTimer.invalidate(); mFrameCallbackElapsedTimer.invalidate();