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:
Marc Mutz 2016-09-27 10:41:41 +02:00
parent c65621b362
commit 24314c73ae

View File

@ -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;