From: Max Lin <mlin@suse.com>
Date: Tue, 25 Sep 2018 15:50:31 +0800
Subject: [PATCH] xcb: prevent crash with pixmap cursors on XRender-less X
 servers

This is a backport of 6c10c54aa805f79100a29b0648be51d8af41f26a and
d5ac11891d8237ca2f02390ffd0ff103578b520e.

We were using xcb_render_* APIs without checking if the server even
supports this extension. Attempting to use an extension which is
not present will always result in a crash. This patch adds the required
guards and refactors how we detect presence of XRender extension.

Also instead of falling back to some odd-looking bitmapped version
just leave the current cursor unchanged. That is how we did it in
Qt4 AFAICT.

Task-number: QTBUG-66935
---
 src/plugins/platforms/xcb/qxcbconnection.cpp | 11 ++++++++---
 src/plugins/platforms/xcb/qxcbconnection.h   | 10 +++++++++-
 src/plugins/platforms/xcb/qxcbcursor.cpp     | 15 +++++++++++----
 3 files changed, 28 insertions(+), 8 deletions(-)

Index: qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbconnection.cpp
===================================================================
--- qtbase-opensource-src-5.9.4.orig/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -556,6 +556,7 @@ QXcbConnection::QXcbConnection(QXcbNativ
     , m_defaultVisualId(defaultVisualId)
     , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
     , m_nativeInterface(nativeInterface)
+    , has_render_extension(false)
 {
 #if QT_CONFIG(xcb_xlib)
     Display *dpy = XOpenDisplay(m_displayName.constData());
@@ -1223,7 +1224,7 @@ void QXcbConnection::handleXcbEvent(xcb_
     }
 
     if (!handled) {
-        if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
+        if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
             xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
             setTime(notify_event->timestamp);
 #ifndef QT_NO_CLIPBOARD
@@ -2059,7 +2060,6 @@ void QXcbConnection::initializeXFixes()
     if (!reply || !reply->present)
         return;
 
-    xfixes_first_event = reply->first_event;
     xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection,
                                                                                      XCB_XFIXES_MAJOR_VERSION,
                                                                                      XCB_XFIXES_MINOR_VERSION);
@@ -2068,8 +2068,10 @@ void QXcbConnection::initializeXFixes()
     if (!xfixes_query || error || xfixes_query->major_version < 2) {
         qWarning("QXcbConnection: Failed to initialize XFixes");
         free(error);
-        xfixes_first_event = 0;
+        return;
     }
+    xfixes_first_event = reply->first_event;
+    has_xfixes = true;
     free(xfixes_query);
 }
 
@@ -2077,8 +2079,10 @@ void QXcbConnection::initializeXRender()
 {
 #if QT_CONFIG(xcb_render)
     const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_render_id);
-    if (!reply || !reply->present)
+    if (!reply || !reply->present) {
+        qDebug("XRender extension not present on the X server");
         return;
+    }
 
     xcb_generic_error_t *error = 0;
     xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection,
@@ -2086,10 +2090,14 @@ void QXcbConnection::initializeXRender()
                                                                                       XCB_RENDER_MINOR_VERSION);
     xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection,
                                                                                      xrender_query_cookie, &error);
-    if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
-        qWarning("QXcbConnection: Failed to initialize XRender");
+    if (!xrender_query) {
+        qWarning("xcb_render_query_version failed");
         free(error);
+        return;
     }
+    has_render_extension = true;
+    m_xrenderVersion.first = xrender_query->major_version;
+    m_xrenderVersion.second = xrender_query->minor_version;
     free(xrender_query);
 #endif
 }
Index: qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbconnection.h
===================================================================
--- qtbase-opensource-src-5.9.4.orig/src/plugins/platforms/xcb/qxcbconnection.h
+++ qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbconnection.h
@@ -464,11 +464,18 @@ public:
     inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
     inline void setNetWmUserTime(xcb_timestamp_t t) { if (t > m_netWmUserTime) m_netWmUserTime = t; }
 
-    bool hasXFixes() const { return xfixes_first_event > 0; }
+    bool hasXFixes() const { return has_xfixes; }
     bool hasXShape() const { return has_shape_extension; }
     bool hasXRandr() const { return has_randr_extension; }
     bool hasInputShape() const { return has_input_shape; }
     bool hasXKB() const { return has_xkb; }
+    bool hasXRender(int major = -1, int minor = -1) const
+    {
+        if (has_render_extension && major != -1 && minor != -1)
+            return m_xrenderVersion >= qMakePair(major, minor);
+
+        return has_render_extension;
+    }
 
     bool supportsThreadedRendering() const { return m_reader->isRunning(); }
     bool threadedEventHandling() const { return m_reader->isRunning(); }
@@ -674,11 +681,15 @@ private:
     uint32_t xrandr_first_event = 0;
     uint32_t xkb_first_event = 0;
 
+    bool has_xfixes = false;
     bool has_xinerama_extension = false;
     bool has_shape_extension = false;
     bool has_randr_extension = false;
     bool has_input_shape;
     bool has_xkb = false;
+    bool has_render_extension = false;
+
+    QPair<int, int> m_xrenderVersion;
 
     Qt::MouseButtons m_buttons = 0;
 
Index: qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbcursor.cpp
===================================================================
--- qtbase-opensource-src-5.9.4.orig/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ qtbase-opensource-src-5.9.4/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -607,12 +607,19 @@ xcb_cursor_t QXcbCursor::createFontCurso
 
 xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
 {
-    xcb_connection_t *conn = xcb_connection();
     QPoint spot = cursor->hotSpot();
     xcb_cursor_t c = XCB_NONE;
-    if (cursor->pixmap().depth() > 1)
-        c = qt_xcb_createCursorXRender(m_screen, cursor->pixmap().toImage(), spot);
-    if (!c) {
+    if (cursor->pixmap().depth() > 1) {
+#if QT_CONFIG(xcb_render)
+        if (connection()->hasXRender(0, 5))
+            c = qt_xcb_createCursorXRender(m_screen, cursor->pixmap().toImage(), spot);
+        else
+            qWarning("xrender >= 0.5 required to create pixmap cursors");
+#else
+        qWarning("This build of Qt does not support pixmap cursors");
+#endif
+    } else {
+        xcb_connection_t *conn = xcb_connection();
         xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage());
         xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage());
         c = xcb_generate_id(conn);
