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

Use-after-free in QCocoaWindow::setVisible when closing window while it's being opened (or closed)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 5.15.2, 6.3.0 Beta2
    • GUI: Window management
    • macOS

    Description

      We have found a situation where there is a use-after-free in QCocoaWindow in situations where you close a window during the processing of a close or open of the same window.

      It's a fairly trivial thing to trigger if you do something like this:

      import QtQuick 2.15
      import QtQuick.Controls 2.15
      import QtQuick.Layouts 1.15
      import QtQuick.Window 2.15
      
      Window {
          width: 640
          height: 480
          visible: true
          title: "Main Window"
      
          Window {
              id: childWindow
              width: 100
              height: 100
              visible: false
              title: "Child Window"
      
              onActiveChanged: {
                  console.log("Active changed")
                  childWindow.close()
              }
          }
      
          Button {
              width: 100
              height: 20
              text: "Open"
              onClicked: {
                  childWindow.show()
              }
          }
      }
      

      In this case, the childWindow.show() call will trigger the onActiveChanged to fire while that call is being processed, and then that will in turn close the window. Internally, the platformWindow in QWindowPrivate ends up being destroyed while the call to QCocoaWindow::setVisible is still in progress on that now-destroyed object.

      If the above code (also attached as a sample project) is run under Guard Malloc, and then click on the button, then it will hit a violation inside QCocoaWindow::setVisible:

      DYLD_INSERT_LIBRARIES="/usr/lib/libgmalloc.dylib" ./test_qt_window_close_issue.app/Contents/MacOS/test_qt_window_close_issue
      GuardMalloc[test_qt_window_close_issue-38940]: Allocations will be placed on 16 byte boundaries.
      GuardMalloc[test_qt_window_close_issue-38940]:  - Some buffer overruns may not be noticed.
      GuardMalloc[test_qt_window_close_issue-38940]:  - Applications using vector instructions (e.g., SSE) should work.
      GuardMalloc[test_qt_window_close_issue-38940]: version 064550.77.2
      QML debugging is enabled. Only use this in a safe environment.
      qml: Active changed
      qml: Active changed
      [1]    38940 segmentation fault  DYLD_INSERT_LIBRARIES="/usr/lib/libgmalloc.dylib"
      

      The crash dump will look like this:

      ... etc ...
      Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
      0   QtGui                       0x10ee72eb4 QPlatformWindow::window() const + 4
      1   libqcocoa.dylib             0x11cacb536 QCocoaWindow::setVisible(bool) + 1798
      2   QtGui                       0x10ee8b758 QWindowPrivate::setVisible(bool) + 1112
      ... etc ...
      

      You can also see the problem by running against an ASan enabled build of Qt:

      ==45875==ERROR: AddressSanitizer: heap-use-after-free on address 0x60f00002af98 at pc 0x00011bc942d4 bp 0x7ff7b77f6530 sp 0x7ff7b77f6528
      READ of size 8 at 0x60f00002af98 thread T0
          #0 0x11bc942d3 in QCocoaWindow::setVisible(bool) qcocoawindow.mm:392
          #1 0x10c9ca7d7 in QWindowPrivate::setVisible(bool) qwindow.cpp:413
          #2 0x10c9e560e in QWindow::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) moc_qwindow.cpp:429
          #3 0x10c9e7f54 in QWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qwindow.cpp:698
          #4 0x109b1cef7 in QQuickWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindow.cpp:486
          #5 0x109cb2cf7 in QQuickWindowQmlImpl::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindowmodule_p.cpp:208
          #6 0x10bd304a5 in QQmlObjectOrGadget::metacall(QMetaObject::Call, int, void**) const qqmlobjectorgadget.cpp:51
          #7 0x10b8e1d84 in CallMethod(QQmlObjectOrGadget const&, int, int, int, int*, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1319
          #8 0x10b8d8597 in CallPrecise(QQmlObjectOrGadget const&, QQmlPropertyData const&, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1573
          #9 0x10b8d6bb2 in QV4::QObjectMethod::callInternal(QV4::Value const*, QV4::Value const*, int) const qv4qobjectwrapper.cpp:2131
          #10 0x10b931ba0 in QV4::Moth::VME::interpret(QV4::CppStackFrame*, QV4::ExecutionEngine*, char const*) qv4vme_moth.cpp:754
          #11 0x10b92c2cb in QV4::Moth::VME::exec(QV4::CppStackFrame*, QV4::ExecutionEngine*) qv4vme_moth.cpp:463
          #12 0x10b7d7005 in QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) qv4function.cpp:69
          #13 0x10bd8f09d in QQmlJavaScriptExpression::evaluate(QV4::CallData*, bool*) qqmljavascriptexpression.cpp:212
          #14 0x10bc75c49 in QQmlBoundSignalExpression::evaluate(void**) qqmlboundsignal.cpp:224
          #15 0x10bc77c11 in QQmlBoundSignal_callback(QQmlNotifierEndpoint*, void**) qqmlboundsignal.cpp:361
          #16 0x10bd2f225 in QQmlNotifier::emitNotify(QQmlNotifierEndpoint*, void**) qqmlnotifier.cpp:104
          #17 0x10bbfba74 in QQmlData::signalEmitted(QAbstractDeclarativeData*, QObject*, int, void**) qqmlengine.cpp:834
          #18 0x10f537837 in void doActivate<false>(QObject*, int, void**) qobject.cpp:3778
          #19 0x11f6f0615 in QQuickAbstractButtonPrivate::handleRelease(QPointF const&) qquickabstractbutton.cpp:182
          #20 0x11f75d0c5 in QQuickControl::mouseReleaseEvent(QMouseEvent*) qquickcontrol.cpp:2151
          #21 0x109a98ccc in QQuickItem::event(QEvent*) qquickitem.cpp
          #22 0x10f48773d in QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) qcoreapplication.cpp:1230
          #23 0x10f486967 in QCoreApplication::notify(QObject*, QEvent*) qcoreapplication.cpp:1145
          #24 0x10f4865d0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) qcoreapplication.cpp:1069
          #25 0x109af763a in QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent*) qquickwindow.cpp:2020
          #26 0x109afd8c8 in QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent*) qquickwindow.cpp:2612
          #27 0x109b0119d in QQuickWindowPrivate::handleMouseEvent(QMouseEvent*) qquickprofiler_p.h
          #28 0x10c9df9f5 in QWindow::event(QEvent*) qwindow.cpp
          #29 0x109aefc1e in QQuickWindow::event(QEvent*) qquickwindow.cpp:1902
          #30 0x10f48773d in QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) qcoreapplication.cpp:1230
          #31 0x10f486967 in QCoreApplication::notify(QObject*, QEvent*) qcoreapplication.cpp:1145
          #32 0x10f4865d0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) qcoreapplication.cpp:1069
          #33 0x10c9a36d0 in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) qguiapplication.cpp:2282
          #34 0x10c9a28fe in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) qguiapplication.cpp:2156
          #35 0x10c9a07bf in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) qguiapplication.cpp:2002
          #36 0x10c948e7b in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) qwindowsysteminterface.cpp:1169
          #37 0x11bcee489 in QCocoaEventDispatcherPrivate::processPostedEvents() qcocoaeventdispatcher.mm:905
          #38 0x11bceffd7 in QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void*) qcocoaeventdispatcher.mm:927
          #39 0x7ff814ae48fc in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__+0x10 (CoreFoundation:x86_64h+0x808fc)
          #40 0x7ff814ae4864 in __CFRunLoopDoSource0+0xb3 (CoreFoundation:x86_64h+0x80864)
          #41 0x7ff814ae45e3 in __CFRunLoopDoSources0+0xf1 (CoreFoundation:x86_64h+0x805e3)
          #42 0x7ff814ae301a in __CFRunLoopRun+0x37c (CoreFoundation:x86_64h+0x7f01a)
          #43 0x7ff814ae25dc in CFRunLoopRunSpecific+0x232 (CoreFoundation:x86_64h+0x7e5dc)
          #44 0x7ff81d71f4f0 in RunCurrentEventLoopInMode+0x123 (HIToolbox:x86_64+0x324f0)
          #45 0x7ff81d71f117 in ReceiveNextEventCommon+0x11b (HIToolbox:x86_64+0x32117)
          #46 0x7ff81d71efe4 in _BlockUntilNextEventMatchingListInModeWithFilter+0x45 (HIToolbox:x86_64+0x31fe4)
          #47 0x7ff817511d87 in _DPSNextEvent+0x375 (AppKit:x86_64+0x3ed87)
          #48 0x7ff8175103f3 in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0x582 (AppKit:x86_64+0x3d3f3)
          #49 0x7ff817502918 in -[NSApplication run]+0x249 (AppKit:x86_64+0x2f918)
          #50 0x11bcea59a in QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) qcocoaeventdispatcher.mm:437
          #51 0x10f478c6d in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) qeventloop.cpp:232
          #52 0x10f488135 in QCoreApplication::exec() qcoreapplication.cpp:1377
          #53 0x108707273 in main+0x373 (test_qt_window_close_issue:x86_64+0x100007273)
          #54 0x10b4694fd in start+0x1cd (dyld:x86_64+0x54fd)
      
      0x60f00002af98 is located 40 bytes inside of 168-byte region [0x60f00002af70,0x60f00002b018)
      freed by thread T0 here:
          #0 0x10a4d5d1d in wrap__ZdlPv+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x50d1d)
          #1 0x10c9c8c81 in QWindowPrivate::destroy() qwindow.cpp:2012
          #2 0x10c9df566 in QWindow::event(QEvent*) qwindow.cpp:2408
          #3 0x109aefc1e in QQuickWindow::event(QEvent*) qquickwindow.cpp:1902
          #4 0x10f48773d in QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) qcoreapplication.cpp:1230
          #5 0x10f486967 in QCoreApplication::notify(QObject*, QEvent*) qcoreapplication.cpp:1145
          #6 0x10f4865d0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) qcoreapplication.cpp:1069
          #7 0x10c9a0bc2 in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) qguiapplication.cpp:2046
          #8 0x10c950e49 in bool QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(QWindow*) qwindowsysteminterface.cpp:361
          #9 0x10c9e6204 in QWindow::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) moc_qwindow.cpp:435
          #10 0x10c9e7f54 in QWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qwindow.cpp:698
          #11 0x109b1cef7 in QQuickWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindow.cpp:486
          #12 0x109cb2cf7 in QQuickWindowQmlImpl::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindowmodule_p.cpp:208
          #13 0x10bd304a5 in QQmlObjectOrGadget::metacall(QMetaObject::Call, int, void**) const qqmlobjectorgadget.cpp:51
          #14 0x10b8e67d7 in CallMethod(QQmlObjectOrGadget const&, int, int, int, int*, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1312
          #15 0x10b8d8597 in CallPrecise(QQmlObjectOrGadget const&, QQmlPropertyData const&, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1573
          #16 0x10b8d6bb2 in QV4::QObjectMethod::callInternal(QV4::Value const*, QV4::Value const*, int) const qv4qobjectwrapper.cpp:2131
          #17 0x10b931ba0 in QV4::Moth::VME::interpret(QV4::CppStackFrame*, QV4::ExecutionEngine*, char const*) qv4vme_moth.cpp:754
          #18 0x10b92c2cb in QV4::Moth::VME::exec(QV4::CppStackFrame*, QV4::ExecutionEngine*) qv4vme_moth.cpp:463
          #19 0x10b7d7005 in QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) qv4function.cpp:69
          #20 0x10bd8f09d in QQmlJavaScriptExpression::evaluate(QV4::CallData*, bool*) qqmljavascriptexpression.cpp:212
          #21 0x10bc75c49 in QQmlBoundSignalExpression::evaluate(void**) qqmlboundsignal.cpp:224
          #22 0x10bc77c11 in QQmlBoundSignal_callback(QQmlNotifierEndpoint*, void**) qqmlboundsignal.cpp:361
          #23 0x10bd2f225 in QQmlNotifier::emitNotify(QQmlNotifierEndpoint*, void**) qqmlnotifier.cpp:104
          #24 0x10bbfba74 in QQmlData::signalEmitted(QAbstractDeclarativeData*, QObject*, int, void**) qqmlengine.cpp:834
          #25 0x10f537837 in void doActivate<false>(QObject*, int, void**) qobject.cpp:3778
          #26 0x10c9ae6ee in QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent*) qguiapplication.cpp:2525
          #27 0x10c99fe9a in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) qguiapplication.cpp:2023
          #28 0x10c94bf6c in void QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(QWindow*, Qt::FocusReason) qwindowsysteminterface.cpp:252
          #29 0x11bc9f193 in QCocoaWindow::windowDidBecomeKey() qcocoawindow.mm:1203
      
      previously allocated by thread T0 here:
          #0 0x10a4d58fd in wrap__Znwm+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x508fd)
          #1 0x11bc54973 in non-virtual thunk to QCocoaIntegration::createPlatformWindow(QWindow*) const qcocoaintegration.mm:286
          #2 0x10c9d0a17 in QWindowPrivate::create(bool, unsigned long long) qwindow.cpp:533
          #3 0x10c9ca034 in QWindowPrivate::setVisible(bool) qwindow.cpp:362
          #4 0x10c9e560e in QWindow::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) moc_qwindow.cpp:429
          #5 0x10c9e7f54 in QWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qwindow.cpp:698
          #6 0x109b1cef7 in QQuickWindow::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindow.cpp:486
          #7 0x109cb2cf7 in QQuickWindowQmlImpl::qt_metacall(QMetaObject::Call, int, void**) moc_qquickwindowmodule_p.cpp:208
          #8 0x10bd304a5 in QQmlObjectOrGadget::metacall(QMetaObject::Call, int, void**) const qqmlobjectorgadget.cpp:51
          #9 0x10b8e1d84 in CallMethod(QQmlObjectOrGadget const&, int, int, int, int*, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1319
          #10 0x10b8d8597 in CallPrecise(QQmlObjectOrGadget const&, QQmlPropertyData const&, QV4::ExecutionEngine*, QV4::CallData*, QMetaObject::Call) qv4qobjectwrapper.cpp:1573
          #11 0x10b8d6bb2 in QV4::QObjectMethod::callInternal(QV4::Value const*, QV4::Value const*, int) const qv4qobjectwrapper.cpp:2131
          #12 0x10b931ba0 in QV4::Moth::VME::interpret(QV4::CppStackFrame*, QV4::ExecutionEngine*, char const*) qv4vme_moth.cpp:754
          #13 0x10b92c2cb in QV4::Moth::VME::exec(QV4::CppStackFrame*, QV4::ExecutionEngine*) qv4vme_moth.cpp:463
          #14 0x10b7d7005 in QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) qv4function.cpp:69
          #15 0x10bd8f09d in QQmlJavaScriptExpression::evaluate(QV4::CallData*, bool*) qqmljavascriptexpression.cpp:212
          #16 0x10bc75c49 in QQmlBoundSignalExpression::evaluate(void**) qqmlboundsignal.cpp:224
          #17 0x10bc77c11 in QQmlBoundSignal_callback(QQmlNotifierEndpoint*, void**) qqmlboundsignal.cpp:361
          #18 0x10bd2f225 in QQmlNotifier::emitNotify(QQmlNotifierEndpoint*, void**) qqmlnotifier.cpp:104
          #19 0x10bbfba74 in QQmlData::signalEmitted(QAbstractDeclarativeData*, QObject*, int, void**) qqmlengine.cpp:834
          #20 0x10f537837 in void doActivate<false>(QObject*, int, void**) qobject.cpp:3778
          #21 0x11f6f0615 in QQuickAbstractButtonPrivate::handleRelease(QPointF const&) qquickabstractbutton.cpp:182
          #22 0x11f75d0c5 in QQuickControl::mouseReleaseEvent(QMouseEvent*) qquickcontrol.cpp:2151
          #23 0x109a98ccc in QQuickItem::event(QEvent*) qquickitem.cpp
          #24 0x10f48773d in QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) qcoreapplication.cpp:1230
          #25 0x10f486967 in QCoreApplication::notify(QObject*, QEvent*) qcoreapplication.cpp:1145
          #26 0x10f4865d0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) qcoreapplication.cpp:1069
          #27 0x109af763a in QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent*) qquickwindow.cpp:2020
          #28 0x109afd8c8 in QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent*) qquickwindow.cpp:2612
          #29 0x109b0119d in QQuickWindowPrivate::handleMouseEvent(QMouseEvent*) qquickprofiler_p.h
      
      SUMMARY: AddressSanitizer: heap-use-after-free qcocoawindow.mm:392 in QCocoaWindow::setVisible(bool)
      Shadow bytes around the buggy address:
        0x1c1e000055a0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
        0x1c1e000055b0: fa fa fd fd fd fd fd fd fd fd fd fd fd fd fd fd
        0x1c1e000055c0: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
        0x1c1e000055d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
        0x1c1e000055e0: fd fd fd fd fd fd fa fa fa fa fa fa fa fa fd fd
      =>0x1c1e000055f0: fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd
        0x1c1e00005600: fd fd fd fa fa fa fa fa fa fa fa fa fd fd fd fd
        0x1c1e00005610: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
        0x1c1e00005620: fd fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
        0x1c1e00005630: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
        0x1c1e00005640: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
      Shadow byte legend (one shadow byte represents 8 application bytes):
        Addressable:           00
        Partially addressable: 01 02 03 04 05 06 07
        Heap left redzone:       fa
        Freed heap region:       fd
        Stack left redzone:      f1
        Stack mid redzone:       f2
        Stack right redzone:     f3
        Stack after return:      f5
        Stack use after scope:   f8
        Global redzone:          f9
        Global init order:       f6
        Poisoned by user:        f7
        Container overflow:      fc
        Array cookie:            ac
        Intra object redzone:    bb
        ASan internal:           fe
        Left alloca redzone:     ca
        Right alloca redzone:    cb
        Shadow gap:              cc
      ==45875==ABORTING
      

      Note:
      Of course in this example, it's clear that it's a silly thing to do. But we have code that legitimately wants to call close inside onActiveChanged.

      Note:
      I have tested this in Qt 6.3.0-beta2 and also 5.15.2. Both exhibit the bug.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            vestbo Tor Arne Vestbø
            mattjgalloway Matt Galloway
            Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes