From c6476ae915b8ac64121d379274363227b1c300ff Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 8 Apr 2019 13:51:28 +0200 Subject: [PATCH] Client: Add safer fromObject function to scanner Makes the scanner produce generated static functions such as QtWaylandClient::wl_surface *wl_surface::fromObject(struct ::wl_surface *object); Which casts from the wayland-scanner generated types, such as struct ::wl_surface *, to types types generated by qtwaylandscanner, but performs a check to see if the listener is set to the wrapper class first (at least for interfaces with events). This lets us easily fix crashes in a couple of places where we receive events with wayland objects that we didn't create. Also adds nullptr checks whenever we use the fromWlSurface() and fromWlOutput() functions to handle failed conversions. Task-number: QTBUG-73801 Fixes: QTBUG-74085 Change-Id: I9f33c31c7d1a939ccb3ebbbcb0eb67af10037237 Reviewed-by: Jaroslaw Kubik Reviewed-by: Paul Olav Tvete --- .../platforms/wayland/qwaylanddatadevice.cpp | 8 ++++++-- .../platforms/wayland/qwaylandinputdevice.cpp | 17 +++++++++++++---- src/plugins/platforms/wayland/qwaylandqtkey.cpp | 6 +++++- .../platforms/wayland/qwaylandscreen.cpp | 5 +++-- .../platforms/wayland/qwaylandsurface.cpp | 11 ++++++++++- src/tools/qtwaylandscanner/qtwaylandscanner.cpp | 11 +++++++++++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp index fde0f71a250..7cdcd108cd1 100644 --- a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp +++ b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp @@ -151,9 +151,13 @@ void QWaylandDataDevice::data_device_drop() void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) { - m_enterSerial = serial; - m_dragWindow = QWaylandWindow::fromWlSurface(surface)->window(); + auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface); + if (!dragWaylandWindow) + return; // Ignore foreign surfaces + + m_dragWindow = dragWaylandWindow->window(); m_dragPoint = calculateDragPosition(x, y, m_dragWindow); + m_enterSerial = serial; QMimeData *dragData = nullptr; Qt::DropActions supportedActions; diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index daad6f15c26..8231dd9c30c 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -555,6 +555,9 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + if (mFocus) { qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a" << "leave event first, this is not allowed by the wayland protocol" @@ -596,10 +599,12 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac if (!surface) return; - if (!QWaylandWindow::mouseGrab()) { - QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + auto *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + + if (!QWaylandWindow::mouseGrab()) setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos)); - } invalidateFocus(); mButtons = Qt::NoButton; @@ -1264,9 +1269,13 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial, if (!surface) return; + auto *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + mParent->mTime = time; mParent->mSerial = serial; - mFocus = QWaylandWindow::fromWlSurface(surface); + mFocus = window; mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed); } diff --git a/src/plugins/platforms/wayland/qwaylandqtkey.cpp b/src/plugins/platforms/wayland/qwaylandqtkey.cpp index a60185bd641..19261973853 100644 --- a/src/plugins/platforms/wayland/qwaylandqtkey.cpp +++ b/src/plugins/platforms/wayland/qwaylandqtkey.cpp @@ -70,7 +70,11 @@ void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface, } QWaylandInputDevice *dev = inputDevices.first(); - QWaylandWindow *win = surface ? QWaylandWindow::fromWlSurface(surface) : dev->keyboardFocus(); + + auto *win = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr; + + if (!win) + win = dev->keyboardFocus(); if (!win || !win->window()) { qWarning("qt_key_extension: handle_qtkey: No keyboard focus"); diff --git a/src/plugins/platforms/wayland/qwaylandscreen.cpp b/src/plugins/platforms/wayland/qwaylandscreen.cpp index b2e3ce819b9..ee6f949aa59 100644 --- a/src/plugins/platforms/wayland/qwaylandscreen.cpp +++ b/src/plugins/platforms/wayland/qwaylandscreen.cpp @@ -190,8 +190,9 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output) { - auto wlOutput = static_cast(wl_output_get_user_data(output)); - return static_cast(wlOutput); + if (auto *o = QtWayland::wl_output::fromObject(output)) + return static_cast(o); + return nullptr; } void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh) diff --git a/src/plugins/platforms/wayland/qwaylandsurface.cpp b/src/plugins/platforms/wayland/qwaylandsurface.cpp index 03a1c1f526b..c35f01b56c3 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandsurface.cpp @@ -65,7 +65,9 @@ QWaylandScreen *QWaylandSurface::oldestEnteredScreen() QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface) { - return static_cast(static_cast(wl_surface_get_user_data(surface))); + if (auto *s = QtWayland::wl_surface::fromObject(surface)) + return static_cast(s); + return nullptr; } void QWaylandSurface::handleScreenRemoved(QScreen *qScreen) @@ -79,6 +81,9 @@ void QWaylandSurface::surface_enter(wl_output *output) { auto addedScreen = QWaylandScreen::fromWlOutput(output); + if (!addedScreen) + return; + if (m_screens.contains(addedScreen)) { qCWarning(lcQpaWayland) << "Ignoring unexpected wl_surface.enter received for output with id:" @@ -95,6 +100,10 @@ void QWaylandSurface::surface_enter(wl_output *output) void QWaylandSurface::surface_leave(wl_output *output) { auto *removedScreen = QWaylandScreen::fromWlOutput(output); + + if (!removedScreen) + return; + bool wasRemoved = m_screens.removeOne(removedScreen); if (!wasRemoved) { qCWarning(lcQpaWayland) diff --git a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp index 56045e88058..7f3dc5ad6bb 100644 --- a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp @@ -986,6 +986,7 @@ bool Scanner::process() printf("\n"); printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName); printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName); + printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName); printf("\n"); printf(" bool isInitialized() const;\n"); printf("\n"); @@ -1130,6 +1131,16 @@ bool Scanner::process() printf(" }\n"); printf("\n"); + printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName); + printf(" {\n"); + if (hasEvents) { + printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName); + printf(" return nullptr;\n"); + } + printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName); + printf(" }\n"); + printf("\n"); + printf(" bool %s::isInitialized() const\n", interfaceName); printf(" {\n"); printf(" return m_%s != nullptr;\n", interfaceName);