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

Crash in QTabBar when moving tabs due to QList::move deleting items

    XMLWordPrintable

Details

    • macOS
    • 359616066e64eed947c6c91cb8902285ed79dd0d (qt/qtbase/dev)

    Description

      The following example reproduces the crash reliably when running on macOS:

      #include <QApplication>
      #include <QMainWindow>
      #include <QTabWidget>
      #include <QVBoxLayout>
      
      class TestWindow : public QMainWindow
      {
      public:
          TestWindow(QWidget *parent = nullptr) : QMainWindow(parent)
          {
              QTabWidget* tw = new QTabWidget;
      
              QWidget* w1 = new QWidget;
              QWidget* w2 = new QWidget;
      
              tw->addTab( w1, "One" );
              tw->addTab( w2, "Two");
              tw->setMovable(true);
      
              QVBoxLayout* l = new QVBoxLayout;
              l->addWidget(tw);
      
              QWidget* w = new QWidget;
              w->setLayout(l);
      
              setCentralWidget(w);
          }
      };
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
          TestWindow w;
          w.show();
          return a.exec();
      }
      

      Drag the two tabs around a few times, it will crash with the following stack trace:

      libQt6Core_debug.6.dylib!std::__1::__atomic_base<int, false>::load(std::__1::memory_order) const (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/atomic:926)
      libQt6Core_debug.6.dylib!int QAtomicOps<int>::loadRelaxed<int>(std::__1::atomic<int> const&) (/Users/vohi/qt/dev/qtbase/src/corelib/thread/qatomic_cxx11.h:239)
      libQt6Core_debug.6.dylib!QBasicAtomicInteger<int>::loadRelaxed() const (/Users/vohi/qt/dev/qtbase/src/corelib/thread/qbasicatomic.h:107)
      libQt6Core_debug.6.dylib!QtPrivate::RefCount::ref() (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qrefcount.h:55)
      libQt6Core_debug.6.dylib!QMetaType::QMetaType(QtPrivate::QMetaTypeInterface*) (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qmetatype.cpp:683)
      libQt6Core_debug.6.dylib!QMetaType::QMetaType(QtPrivate::QMetaTypeInterface*) (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qmetatype.cpp:681)
      libQt6Core_debug.6.dylib!QVariant::Private::type() const (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qvariant.h:448)
      libQt6Core_debug.6.dylib!QVariant::Private::Private(QVariant::Private const&) (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qvariant.h:405)
      libQt6Core_debug.6.dylib!QVariant::QVariant(QVariant const&) (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qvariant.cpp:1783)
      libQt6Core_debug.6.dylib!QVariant::QVariant(QVariant const&) (/Users/vohi/qt/dev/qtbase/src/corelib/kernel/qvariant.cpp:1784)
      libQt6Core_debug.6.dylib!std::__1::pair<double, QVariant>::pair(std::__1::pair<double, QVariant> const&) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:328)
      libQt6Core_debug.6.dylib!std::__1::pair<double, QVariant>::pair(std::__1::pair<double, QVariant> const&) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:328)
      libQt6Core_debug.6.dylib!QtPrivate::QGenericArrayOps<std::__1::pair<double, QVariant> >::copyAppend(std::__1::pair<double, QVariant> const*, std::__1::pair<double, QVariant> const*) (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qarraydataops.h:276)
      libQt6Core_debug.6.dylib!QArrayDataPointer<std::__1::pair<double, QVariant> >::clone(QFlags<QArrayData::ArrayOption>) const (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qarraydatapointer.h:218)
      libQt6Core_debug.6.dylib!QArrayDataPointer<std::__1::pair<double, QVariant> >::detach() (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qarraydatapointer.h:175)
      libQt6Core_debug.6.dylib!QList<std::__1::pair<double, QVariant> >::detach() (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qlist.h:210)
      libQt6Core_debug.6.dylib!QList<std::__1::pair<double, QVariant> >::begin() (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qlist.h:358)
      libQt6Core_debug.6.dylib!QVariantAnimationPrivate::setValueAt(double, QVariant const&) (/Users/vohi/qt/dev/qtbase/src/corelib/animation/qvariantanimation.cpp:316)
      libQt6Core_debug.6.dylib!QVariantAnimation::setKeyValueAt(double, QVariant const&) (/Users/vohi/qt/dev/qtbase/src/corelib/animation/qvariantanimation.cpp:585)
      libQt6Core_debug.6.dylib!QVariantAnimation::setStartValue(QVariant const&) (/Users/vohi/qt/dev/qtbase/src/corelib/animation/qvariantanimation.cpp:532)
      

      This is a use-after-free issue, the Tab::animation instance has been deleted. This happens in QTabBarPrivate::~Tab:

              ~Tab() { delete animation; }
      

      And a call stack to that during the drag-operation is:

      libQt6Widgets_debug.6.dylib!QTabBarPrivate::Tab::~Tab() (/Users/vohi/qt/dev/qtbase/src/widgets/widgets/qtabbar_p.h:144)
      libQt6Widgets_debug.6.dylib!QTabBarPrivate::Tab::~Tab() (/Users/vohi/qt/dev/qtbase/src/widgets/widgets/qtabbar_p.h:143)
      libQt6Widgets_debug.6.dylib!std::__1::enable_if<(is_move_constructible<QTabBarPrivate::Tab>::value) && (is_move_assignable<QTabBarPrivate::Tab>::value), void>::type std::__1::swap<QTabBarPrivate::Tab>(QTabBarPrivate::Tab&, QTabBarPrivate::Tab&) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4529)
      libQt6Widgets_debug.6.dylib!QTabBarPrivate::Tab* std::__1::__rotate_forward<QTabBarPrivate::Tab*>(QTabBarPrivate::Tab*, QTabBarPrivate::Tab*, QTabBarPrivate::Tab*) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:2271)
      libQt6Widgets_debug.6.dylib!QTabBarPrivate::Tab* std::__1::__rotate<QTabBarPrivate::Tab*>(QTabBarPrivate::Tab*, QTabBarPrivate::Tab*, QTabBarPrivate::Tab*, std::__1::random_access_iterator_tag) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:2395)
      libQt6Widgets_debug.6.dylib!QTabBarPrivate::Tab* std::__1::rotate<QTabBarPrivate::Tab*>(QTabBarPrivate::Tab*, QTabBarPrivate::Tab*, QTabBarPrivate::Tab*) (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:2407)
      libQt6Widgets_debug.6.dylib!QList<QTabBarPrivate::Tab>::move(int, int) (/Users/vohi/qt/dev/qtbase/src/corelib/tools/qlist.h:352)
      libQt6Widgets_debug.6.dylib!QTabBar::moveTab(int, int) (/Users/vohi/qt/dev/qtbase/src/widgets/widgets/qtabbar.cpp:2039)
      libQt6Widgets_debug.6.dylib!QTabBarPrivate::slide(int, int) (/Users/vohi/qt/dev/qtbase/src/widgets/widgets/qtabbar.cpp:2080)
      libQt6Widgets_debug.6.dylib!QTabBar::mouseMoveEvent(QMouseEvent*) (/Users/vohi/qt/dev/qtbase/src/widgets/widgets/qtabbar.cpp:2192)
      libQt6Widgets_debug.6.dylib!QWidget::event(QEvent*) (/Users/vohi/qt/dev/qtbase/src/widgets/kernel/qwidget.cpp:8601)
      libQt6Widgets_debug.6.dylib!QTabBar::event(QEvent*)
      

      So, it would seem that QList<QTabBarPrivate::Tab>::move deletes tab instances, which destructs the animation object.

      Assumption is that this is because the type doesn't implement a move constructor, but it's a very bad side effect of the change of the QList implementation to be a vector.

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-85394
          # Subject Branch Project Status CR V

          Activity

            People

              vhilshei Volker Hilsheimer
              vhilshei Volker Hilsheimer
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes