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

QML evaluates bindings after destruction, possibly resulting in segmentation faults

    XMLWordPrintable

Details

    Description

      Consider the following basic example of a C++ model exposed to QML:

      // main.cpp
      #include <QGuiApplication>
      #include <QQmlApplicationEngine>
      #include <QAbstractListModel>
      #include <QQmlContext>
      #include <QDebug>
      #include "RootData.h"
      
      class CppModel : public QAbstractListModel
      {
      public:
        virtual int rowCount(const QModelIndex &parent) const override {return 1;}
        virtual QVariant data(const QModelIndex &index, int role) const override {qDebug () << "CppModel::data"; return "test";}
        
        void Reset () {beginResetModel(); endResetModel();}
      };
      
      int main(int argc, char *argv[])
      {
        QGuiApplication app(argc, argv);
      
        CppModel *model = new CppModel();
        RootData *rootData = new RootData();
        
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("cppModel", model);
        engine.rootContext()->setContextProperty("loaderActive", true);
        engine.rootContext()->setContextProperty("rootData", rootData);
        engine.load(QStringLiteral("qrc:/main.qml"));
        
        qDebug () << "\nDisabling loader...";
        engine.rootContext()->setContextProperty("loaderActive", false); // correctly destroys the Repeater, but after destruction some bindings will still be evaluated.
        qDebug () << "loader disabled";
        
        qDebug () << "\nDelete rootData...";
        rootData->DeleteObject(); // should never be used anymore, as loader is inactive
        
        qDebug () << "\nReset model...";
        model->Reset(); // trigger the re-evaluation of destroyed bindings
      
        return app.exec();
      }
      
      // RootData.h
      #include "QObject"
      
      class RootData : public QObject
      {
        Q_OBJECT
      public:
        RootData() : object(new QObject()) {object->setObjectName("objectName");}
        ~RootData() {delete object;}
      
        Q_INVOKABLE QString getValue () {return object->objectName();}
        void DeleteObject() {delete object; object = nullptr;}
        
        QObject *object;
      };
      
      // main.qml
      ApplicationWindow 
      {
        visible: true
      
        Loader
        {
          active: loaderActive
          
          sourceComponent: comp
        }
      
        Component
        {
          id: comp
          Column
          {
            Repeater
            {
              id: repeater
              
              model: cppModel
              Component.onCompleted: console.log("Repeater constructed");
              Component.onDestruction: console.log("Repeater destroyed");
              
              delegate: Text {text: {console.log("updating text"); return display + rootData.getValue();}}
            }
          }
        }
      }
      

      The output of this code is:

      qml: updating text
      CppModel::data
      qml: Repeater constructed
      
      Disabling loader...
      qml: Repeater destroyed
      loader disabled
      
      Delete rootData...
      
      Reset model...
      qml: updating text       // this should not occur
      CppModel::data          // this should not occur
      
      // application crashes due to accessing destructed RootData::object (inside RootData::getValue called from QML): RootData::getValue should not have been called.
      

      The main issue is that some bindings are evaluated due to the Repeater being repopulated as a result of a model reset, even after the Repeater itself is already destructed. This results in unexpected function calls, potentially accessing destructed objects (with C++ ownership) as it the case in this example (i.e. QML calls RootData::getValue() which access RootData::object after it is destructed).

      Attachments

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

        Activity

          People

            qtqmlteam Qt Qml Team User
            m3197d - -
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes