QTapAndHoldGestureRecognizer: Fix several UBs (invalid cast) in recognize()
As found by UBSan: qstandardgestures.cpp:511:67: runtime error: downcast of address 0x7ffc9beb1b90 which does not point to an object of type 'QTouchEvent' 0x7ffc9beb1b90: note: object is of type 'QPlatformSurfaceEvent' fc 7f 00 00 08 93 b1 6f f5 2a 00 00 00 00 00 00 00 00 00 00 d9 00 ec 9b 00 00 00 00 49 01 c1 5e ^~~~~~~~~~~~~~~~~~~~~~~ vptr for 'QPlatformSurfaceEvent' #0 0x2af55edfa66a in QTapAndHoldGestureRecognizer::recognize(QGesture*, QObject*, QEvent*) qstandardgestures.cpp:511 #1 0x2af55ee3d9bb in QGestureManager::filterEventThroughContexts(QMultiMap<QObject*, Qt::GestureType> const&, QEvent*) qgesturemanager.cpp:276 #2 0x2af55ee4565b in QGestureManager::filterEvent(QWidget*, QEvent*) qgesturemanager.cpp:512 #3 0x2af55ee53945 in QGestureManager::filterEvent(QObject*, QEvent*) qgesturemanager.cpp:556 #4 0x2af55ea1b83a in QApplication::notify(QObject*, QEvent*) qapplication.cpp:3053 #5 0x2af573949d0f in QCoreApplication::notifyInternal2(QObject*, QEvent*) qcoreapplication.cpp:988 #6 0x2af56982ff94 in QCoreApplication::sendEvent(QObject*, QEvent*) qcoreapplication.h:231 #7 0x2af56982ff94 in QWindowPrivate::create(bool) qwindow.cpp:435 #8 0x2af55ecd10fe in QWidgetPrivate::create_sys(unsigned long long, bool, bool) qwidget.cpp:1471 #9 0x2af55ecc770e in QWidget::create(unsigned long long, bool, bool) qwidget.cpp:1333 #10 0x2af55ed80618 in QWidget::setVisible(bool) qwidget.cpp:8156 #11 0x4feec4 in tst_QWidget::touchEventsForGesturePendingWidgets() tst_qwidget.cpp:9824 qstandardgestures.cpp:512:67: runtime error: downcast of address 0x7ffc9beb1b90 which does not point to an object of type 'QMouseEvent' 0x7ffc9beb1b90: note: object is of type 'QPlatformSurfaceEvent' fc 7f 00 00 08 93 b1 6f f5 2a 00 00 00 00 00 00 00 00 00 00 d9 00 ec 9b 00 00 00 00 49 01 c1 5e ^~~~~~~~~~~~~~~~~~~~~~~ vptr for 'QPlatformSurfaceEvent' #0 0x2af55edfaa19 in QTapAndHoldGestureRecognizer::recognize(QGesture*, QObject*, QEvent*) qstandardgestures.cpp:512 [... skipping common lines ...] qstandardgestures.cpp:514:95: runtime error: downcast of address 0x 0x7ffc9beb1b90: note: object is of type 'QPlatformSurfaceEvent' fc 7f 00 00 08 93 b1 6f f5 2a 00 00 00 00 00 00 00 00 00 00 d9 00 ec 9b 00 00 00 0 ^~~~~~~~~~~~~~~~~~~~~~~ vptr for 'QPlatformSurfaceEvent' #0 0x2af55edfa966 in QTapAndHoldGestureRecognizer::recognize(QGesture*, QObject*, QEvent*) qstandardgestures.cpp:514 [... skipping common lines ...] The problem is that the casts are done outside the switch that determines the event's type, so for any given event object, at least any two of the casts are invalid. This could actually be a real problem, because it's trivial for a compiler to prove that these three lines unconditionally invoke UB, so it has all the right in the world to decide to drop the complete rest of the function, using this line of reasoning: 1. The only way for these three casts not to be UB is if event == nullptr. 2. If event == nullptr, then event->type() invokes UB, so event cannot be nullptr. 3. The only way both can be true is if this code path is never taken. I can thus assume that object == state && event->type() == QEvent::Timer is always true, drop the check and execute the if block unconditionally (I need to call QEvent::type(), to satisfy the as-if-rule, but I needn't check its return value). Fix by moving the casts where they belong: into each case of the switch, where the type of the event has been checked to match the target type of the cast. Change-Id: I3aee8e213dc19d2f51636bcc5221cc92b3142e58 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
c65621b362
commit
24314c73ae
@ -502,45 +502,46 @@ QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object,
|
||||
return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint;
|
||||
}
|
||||
|
||||
const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
|
||||
const QMouseEvent *me = static_cast<const QMouseEvent *>(event);
|
||||
#ifndef QT_NO_GRAPHICSVIEW
|
||||
const QGraphicsSceneMouseEvent *gsme = static_cast<const QGraphicsSceneMouseEvent *>(event);
|
||||
#endif
|
||||
|
||||
enum { TapRadius = 40 };
|
||||
|
||||
switch (event->type()) {
|
||||
#ifndef QT_NO_GRAPHICSVIEW
|
||||
case QEvent::GraphicsSceneMousePress:
|
||||
case QEvent::GraphicsSceneMousePress: {
|
||||
const QGraphicsSceneMouseEvent *gsme = static_cast<const QGraphicsSceneMouseEvent *>(event);
|
||||
d->position = gsme->screenPos();
|
||||
q->setHotSpot(d->position);
|
||||
if (d->timerId)
|
||||
q->killTimer(d->timerId);
|
||||
d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout);
|
||||
return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout
|
||||
}
|
||||
#endif
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonPress: {
|
||||
const QMouseEvent *me = static_cast<const QMouseEvent *>(event);
|
||||
d->position = me->globalPos();
|
||||
q->setHotSpot(d->position);
|
||||
if (d->timerId)
|
||||
q->killTimer(d->timerId);
|
||||
d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout);
|
||||
return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout
|
||||
case QEvent::TouchBegin:
|
||||
}
|
||||
case QEvent::TouchBegin: {
|
||||
const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
|
||||
d->position = ev->touchPoints().at(0).startScreenPos();
|
||||
q->setHotSpot(d->position);
|
||||
if (d->timerId)
|
||||
q->killTimer(d->timerId);
|
||||
d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout);
|
||||
return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout
|
||||
}
|
||||
#ifndef QT_NO_GRAPHICSVIEW
|
||||
case QEvent::GraphicsSceneMouseRelease:
|
||||
#endif
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::TouchEnd:
|
||||
return QGestureRecognizer::CancelGesture; // get out of the MayBeGesture state
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchUpdate: {
|
||||
const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
|
||||
if (d->timerId && ev->touchPoints().size() == 1) {
|
||||
QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
|
||||
QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
|
||||
@ -548,7 +549,9 @@ QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object,
|
||||
return QGestureRecognizer::MayBeGesture;
|
||||
}
|
||||
return QGestureRecognizer::CancelGesture;
|
||||
}
|
||||
case QEvent::MouseMove: {
|
||||
const QMouseEvent *me = static_cast<const QMouseEvent *>(event);
|
||||
QPoint delta = me->globalPos() - d->position.toPoint();
|
||||
if (d->timerId && delta.manhattanLength() <= TapRadius)
|
||||
return QGestureRecognizer::MayBeGesture;
|
||||
@ -556,6 +559,7 @@ QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object,
|
||||
}
|
||||
#ifndef QT_NO_GRAPHICSVIEW
|
||||
case QEvent::GraphicsSceneMouseMove: {
|
||||
const QGraphicsSceneMouseEvent *gsme = static_cast<const QGraphicsSceneMouseEvent *>(event);
|
||||
QPoint delta = gsme->screenPos() - d->position.toPoint();
|
||||
if (d->timerId && delta.manhattanLength() <= TapRadius)
|
||||
return QGestureRecognizer::MayBeGesture;
|
||||
|
Loading…
Reference in New Issue
Block a user