Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-62443

Crash due to use-after-free in QSGDefaultDistanceFieldGlyphCache

    XMLWordPrintable

Details

    • Bug
    • Resolution: Incomplete
    • P1: Critical
    • None
    • 5.9.1
    • Quick: SceneGraph
    • None
    • I have observed this crash on CentOS 7.3, Ubuntu 17.04, and macOS Sierra.[^qt-5.9.1.patch]
    • macOS

    Description

      I don't have an easily-distillable example that demonstrates this problem, but I have confirmed that QSGDefaultDistanceFieldGlyphCache can exhibit a use-after-free bug that causes unpredictable behavior (often a segmentation fault).

      Steps to reproduce:

      1. Create a QQuickWidget whose scene graph contains a QML Text item (represented by a QQuickTextNode). I don't think it's key to this issue, but my scene graph contains a Map item, with the Text item added to the Map as a MapQuickItem object.
      2. Place the QQuickWidget into a QDockWidget that is docked into a QMainWindow.
      3. Create some code that updates the contents of the text periodically (I don't know how the internals of the scene graph works, so I'm not sure what is required to trigger the bug). For instance, a timer that changes the contents of the text.
      4. Undock the widget from the main window so it becomes its own top-level window.
      5. Redock the widget to the main window.
      6. Repeat steps 4-5 until you see a segmentation fault.

      By tracing through the Qt source (the below stack traces are from a macOS build), I found that when you undock the widget from the main window, a resizeEvent() on the QQuickWidget is triggered. This triggers the creation of a new frame buffer object, which results in recreating the OpenGL context. Here is the stack trace where the OpenGL context is destroyed:

      * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 11.2
      * frame #0: 0x0000000107fc24f0 QtGui_debug`QOpenGLFunctions::~QOpenGLFunctions(this=0x000000012555c640) at qopenglfunctions.cpp:285
      frame #1: 0x0000000107c84f73 QtGui_debug`QOpenGLContext::destroy(this=0x0000000128094e60) at qopenglcontext.cpp:655
      frame #2: 0x0000000107c84d58 QtGui_debug`QOpenGLContext::create(this=0x0000000128094e60) at qopenglcontext.cpp:610
      frame #3: 0x0000000110f330f7 QtQuickWidgets_debug`QQuickWidget::createFramebufferObject(this=0x0000000128787a00) at qquickwidget.cpp:934
      frame #4: 0x0000000110f3403c QtQuickWidgets_debug`QQuickWidget::resizeEvent(this=0x0000000128787a00, e=0x00007fff5fbf7d08) at qquickwidget.cpp:1196
      frame #5: 0x0000000106fa7ad5 QtWidgets_debug`QWidget::event(this=0x0000000128787a00, event=0x00007fff5fbf7d08) at qwidget.cpp:8962
      frame #6: 0x0000000110f34e69 QtQuickWidgets_debug`QQuickWidget::event(this=0x0000000128787a00, e=0x00007fff5fbf7d08) at qquickwidget.cpp:1450
      frame #7: 0x0000000106f4b74f QtWidgets_debug`QApplicationPrivate::notify_helper(this=0x000000010b70a390, receiver=0x0000000128787a00, e=0x00007fff5fbf7d08) at qapplication.cpp:3717
      frame #8: 0x0000000106f50979 QtWidgets_debug`QApplication::notify(this=0x00007fff5fbfd148, receiver=0x0000000128787a00, e=0x00007fff5fbf7d08) at qapplication.cpp:3680
      frame #9: 0x0000000108afc665 QtCore_debug`QCoreApplication::notifyInternal2(receiver=0x0000000128787a00, event=0x00007fff5fbf7d08) at qcoreapplication.cpp:1018
      frame #10: 0x0000000106f379a8 QtWidgets_debug`QCoreApplication::sendEvent(receiver=0x0000000128787a00, event=0x00007fff5fbf7d08) at qcoreapplication.h:233
      frame #11: 0x0000000106f9de01 QtWidgets_debug`QWidgetPrivate::sendPendingMoveAndResizeEvents(this=0x0000000128787de0, recursive=false, disableUpdates=false) at qwidget.cpp:7826
      frame #12: 0x0000000106fa5a5a QtWidgets_debug`QWidgetPrivate::show_helper(this=0x0000000128787de0) at qwidget.cpp:7877
      frame #13: 0x0000000106fa5a02 QtWidgets_debug`QWidgetPrivate::show_recursive(this=0x0000000128787de0) at qwidget.cpp:7807
      frame #14: 0x0000000106fa5e6e QtWidgets_debug`QWidgetPrivate::showChildren(this=0x00000001274d1650, spontaneous=false) at qwidget.cpp:8313
      frame #15: 0x0000000106fa5a78 QtWidgets_debug`QWidgetPrivate::show_helper(this=0x00000001274d1650) at qwidget.cpp:7883
      frame #16: 0x0000000106fa6afd QtWidgets_debug`QWidget::setVisible(this=0x00000001274d1600, visible=true) at qwidget.cpp:8232
      frame #17: 0x0000000106fa5906 QtWidgets_debug`QWidget::show(this=0x00000001274d1600) at qwidget.cpp:7784
      frame #18: 0x00000001070eb28d QtWidgets_debug`QDockWidgetPrivate::setWindowState(this=0x00000001274d1650, floating=true, unplug=true, rect=0x00007fff5fbf8118) at qdockwidget.cpp:1120
      frame #19: 0x00000001070eb063 QtWidgets_debug`QDockWidgetPrivate::unplug(this=0x00000001274d1650, rect=0x00007fff5fbf8238) at qdockwidget.cpp:1068
      frame #20: 0x000000010712a9e9 QtWidgets_debug`QMainWindowLayout::unplug(this=0x000000010d063600, widget=0x00000001274d1600, group=true) at qmainwindowlayout.cpp:2425
      frame #21: 0x00000001070e9f0f QtWidgets_debug`QDockWidgetPrivate::startDrag(this=0x00000001274d1650, group=true) at qdockwidget.cpp:762
      frame #22: 0x00000001070ea867 QtWidgets_debug`QDockWidgetPrivate::mouseMoveEvent(this=0x00000001274d1650, event=0x00007fff5fbf9068) at qdockwidget.cpp:921
      frame #23: 0x00000001070ec31b QtWidgets_debug`QDockWidget::event(this=0x00000001274d1600, event=0x00007fff5fbf9068) at qdockwidget.cpp:1519
      frame #24: 0x0000000106f4b74f QtWidgets_debug`QApplicationPrivate::notify_helper(this=0x000000010b70a390, receiver=0x00000001274d1600, e=0x00007fff5fbf9068) at qapplication.cpp:3717
      frame #25: 0x0000000106f4e044 QtWidgets_debug`QApplication::notify(this=0x00007fff5fbfd148, receiver=0x0000000129440a50, e=0x00007fff5fbf9658) at qapplication.cpp:3193
      frame #26: 0x0000000108afc665 QtCore_debug`QCoreApplication::notifyInternal2(receiver=0x0000000129440a50, event=0x00007fff5fbf9658) at qcoreapplication.cpp:1018
      frame #27: 0x0000000106f4a2cc QtWidgets_debug`QCoreApplication::sendSpontaneousEvent(receiver=0x0000000129440a50, event=0x00007fff5fbf9658) at qcoreapplication.h:236
      frame #28: 0x0000000106f4c7f0 QtWidgets_debug`QApplicationPrivate::sendMouseEvent(receiver=0x0000000129440a50, event=0x00007fff5fbf9658, alienWidget=0x0000000129440a50, nativeWidget=0x00007fff5fbfcfa8, buttonDown=0x00000001075a5048, lastMouseReceiver=0x00000001075a5060, spontaneous=true) at qapplication.cpp:2697
      frame #29: 0x0000000106fd60e2 QtWidgets_debug`QWidgetWindow::handleMouseEvent(this=0x000000010c973cd0, event=0x00007fff5fbfaa28) at qwidgetwindow.cpp:621
      frame #30: 0x0000000106fd47f6 QtWidgets_debug`QWidgetWindow::event(this=0x000000010c973cd0, event=0x00007fff5fbfaa28) at qwidgetwindow.cpp:243
      frame #31: 0x0000000106f4b74f QtWidgets_debug`QApplicationPrivate::notify_helper(this=0x000000010b70a390, receiver=0x000000010c973cd0, e=0x00007fff5fbfaa28) at qapplication.cpp:3717
      frame #32: 0x0000000106f4d827 QtWidgets_debug`QApplication::notify(this=0x00007fff5fbfd148, receiver=0x000000010c973cd0, e=0x00007fff5fbfaa28) at qapplication.cpp:3089
      frame #33: 0x0000000108afc665 QtCore_debug`QCoreApplication::notifyInternal2(receiver=0x000000010c973cd0, event=0x00007fff5fbfaa28) at qcoreapplication.cpp:1018
      frame #34: 0x0000000107c21c0c QtGui_debug`QCoreApplication::sendSpontaneousEvent(receiver=0x000000010c973cd0, event=0x00007fff5fbfaa28) at qcoreapplication.h:236
      frame #35: 0x0000000107c1d619 QtGui_debug`QGuiApplicationPrivate::processMouseEvent(e=0x00000001287d7d40) at qguiapplication.cpp:1949
      frame #36: 0x0000000107c1caa7 QtGui_debug`QGuiApplicationPrivate::processWindowSystemEvent(e=0x00000001287d7d40) at qguiapplication.cpp:1733
      frame #37: 0x0000000107bfa464 QtGui_debug`QWindowSystemInterface::sendWindowSystemEvents(flags=(i = 0)) at qwindowsysteminterface.cpp:939
      frame #38: 0x00000001191d2ee1 libqcocoa_debug.dylib`QCocoaEventDispatcherPrivate::processPostedEvents(this=0x000000010c928190) at qcocoaeventdispatcher.mm:889
      frame #39: 0x00000001191d3d8f libqcocoa_debug.dylib`QCocoaEventDispatcherPrivate::postedEventsSourceCallback(info=0x000000010c928190) at qcocoaeventdispatcher.mm:925
      

      The QQuickTextNode in the scene graph creates a QSGDistanceFieldGlyphNode object to represent the distance field glyphs that are used to render the text. The QSGDistanceFieldGlyphNode object has a member variable called m_glyph_cache of type QSGDefaultDistanceFieldGlyphCache *. This cache is used during the rendering process.

      QSGDefaultDistanceFieldGlyphCache has a member variable called m_funcs of type QOpenGLFunctions *. This pointer is used to access the OpenGL API function table as needed. However, it is initialized in the QSGDefaultDistanceFieldGlyphCache constructor to the QOpenGLFunctions object associated with the context provided to the constructor. There is no protection to catch the case where that context is later destroyed while the QSGDefaultDistanceFieldGlyphCache object is still in use (which is what occurs during the undocking process described above). The cache retains a dangling pointer to the functions table associated with the now-destroyed context, which resides in memory that is now freed for use by the allocator again. Therefore, the memory pointed to by m_funcs may be reallocated somewhere else in the application and overwritten, resulting in corruption of the function table (and a segmentation fault).

      Here's an example of the stack trace at the point of the crash:

      * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xd00000003)
      * frame #0: 0x0000000d00000003
      frame #1: 0x0000000109dd1baf QtQuick_debug`QOpenGLFunctions::glGetIntegerv(this=0x000000012555c640, pname=3317, params=0x00007fff5fbf92ac) at qopenglfunctions.h:851
      frame #2: 0x0000000109e345de QtQuick_debug`QSGDefaultDistanceFieldGlyphCache::storeGlyphs(this=0x000000012ad65bc0, glyphs=0x00007fff5fbf9380) at qsgdefaultdistancefieldglyphcache.cpp:166
      frame #3: 0x0000000109e14327 QtQuick_debug`QSGDistanceFieldGlyphCache::update(this=0x000000012ad65bc0) at qsgadaptationlayer.cpp:197
      frame #4: 0x0000000109e39c55 QtQuick_debug`QSGDistanceFieldGlyphNode::preprocess(this=0x000000010c963120) at qsgdistancefieldglyphnode.cpp:165
      frame #5: 0x0000000109dd9552 QtQuick_debug`QSGRenderer::preprocess(this=0x000000012ad82ac0) at qsgrenderer.cpp:302
      frame #6: 0x0000000109dd8da7 QtQuick_debug`QSGRenderer::renderScene(this=0x000000012ad82ac0, bindable=0x00007fff5fbf9530) at qsgrenderer.cpp:218
      frame #7: 0x0000000109dd8cb5 QtQuick_debug`QSGRenderer::renderScene(this=0x000000012ad82ac0, fboId=1) at qsgrenderer.cpp:187
      frame #8: 0x0000000109e41514 QtQuick_debug`QSGDefaultRenderContext::renderNextFrame(this=0x0000000128786ed0, renderer=0x000000012ad82ac0, fboId=1) at qsgdefaultrendercontext.cpp:181
      frame #9: 0x0000000109ebf8c9 QtQuick_debug`QQuickWindowPrivate::renderSceneGraph(this=0x0000000128786fb0, size=0x00007fff5fbf9700) at qquickwindow.cpp:470
      frame #10: 0x0000000109fe6f4b QtQuick_debug`QQuickRenderControl::render(this=0x0000000128788070) at qquickrendercontrol.cpp:344
      frame #11: 0x0000000110f30880 QtQuickWidgets_debug`QQuickWidgetPrivate::render(this=0x0000000128787de0, needsSync=true) at qquickwidget.cpp:287
      frame #12: 0x0000000110f30bd9 QtQuickWidgets_debug`QQuickWidgetPrivate::renderSceneGraph(this=0x0000000128787de0) at qquickwidget.cpp:334
      frame #13: 0x0000000110f33d1b QtQuickWidgets_debug`QQuickWidget::timerEvent(this=0x0000000128787a00, e=0x00007fff5fbfad58) at qquickwidget.cpp:1114
      frame #14: 0x0000000108b495c1 QtCore_debug`QObject::event(this=0x0000000128787a00, e=0x00007fff5fbfad58) at qobject.cpp:1227
      frame #15: 0x0000000106fa890b QtWidgets_debug`QWidget::event(this=0x0000000128787a00, event=0x00007fff5fbfad58) at qwidget.cpp:9244
      frame #16: 0x0000000110f34e69 QtQuickWidgets_debug`QQuickWidget::event(this=0x0000000128787a00, e=0x00007fff5fbfad58) at qquickwidget.cpp:1450
      frame #17: 0x0000000106f4b74f QtWidgets_debug`QApplicationPrivate::notify_helper(this=0x000000010b70a390, receiver=0x0000000128787a00, e=0x00007fff5fbfad58) at qapplication.cpp:3717
      frame #18: 0x0000000106f50979 QtWidgets_debug`QApplication::notify(this=0x00007fff5fbfd148, receiver=0x0000000128787a00, e=0x00007fff5fbfad58) at qapplication.cpp:3680
      frame #19: 0x0000000108afc665 QtCore_debug`QCoreApplication::notifyInternal2(receiver=0x0000000128787a00, event=0x00007fff5fbfad58) at qcoreapplication.cpp:1018
      frame #20: 0x0000000108afdfa8 QtCore_debug`QCoreApplication::sendEvent(receiver=0x0000000128787a00, event=0x00007fff5fbfad58) at qcoreapplication.h:233
      frame #21: 0x0000000108ba07e8 QtCore_debug`QTimerInfoList::activateTimers(this=0x000000010c928210) at qtimerinfo_unix.cpp:643
      frame #22: 0x00000001191d0db4 libqcocoa_debug.dylib`QCocoaEventDispatcherPrivate::activateTimersSourceCallback(info=0x000000010c928190) at qcocoaeventdispatcher.mm:124

      It's not clear to me what the appropriate fix is here. I found that if I discontinued use of m_funcs altogether and instead always used the current OpenGL context to provide the QOpenGLFunctions object, the crash seemed to go away. For reference, I attached a quick patch that implements this (although I'm sure it needs some massaging before it would be suitable for merging).

      Attachments

        1. qt-5.9.1.patch
          16 kB
          Jason Roehm
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            esabraha Eskil Abrahamsen Blomfeldt
            jroehm Jason Roehm
            Votes:
            2 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes