[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdevelop] plugins/standardoutputview: Fix segfaults in OutputWidget
From:       Anton Anikin <null () kde ! org>
Date:       2018-09-15 0:28:39
Message-ID: E1g0yRz-0002s9-4v () code ! kde ! org
[Download RAW message or body]

Git commit f856a9a63910a7e9f2fbcbcd63f85100599805ae by Anton Anikin.
Committed on 15/09/2018 at 00:26.
Pushed by antonanikin into branch 'master'.

Fix segfaults in OutputWidget

Summary:
This patch fixes regressions provided by D14931.

Steps to reproduce (my system is neon/bionic with Qt 5.11.1 and KDevelop/master):

1) Start KDevelop and load some project
2) Start project build
3) Close KDevelop
4) Segfault happens

Main problems:

1) Incorrect signal processing. `OutputWidget::updateFilter()` slot is called from \
parent's destructor when `m_tabwidget`/`m_stackwidget` is deleted so we have \
destroyed `m_views` hash and segfault as a result.

2) Incorrect `QSharedPointer` usage. All handled objects have `QWidget` as a parent \
so we have double-free problem and and segfault as a result.

BUG: 398615

Test Plan: Tested on different output widgets \
(`KDevelop::IOutputView::OneView/HistoryView/MultipleView`) with no segfaults. All \
our `QTreeView`/`QSortFilterProxyModel` objects are deleted (no memory leaks).

Reviewers: #kdevelop, kossebau

Reviewed By: #kdevelop, kossebau

Subscribers: kfunk, kossebau, vkorneev, kdevelop-devel

Tags: #kdevelop

Differential Revision: https://phabricator.kde.org/D15326

M  +35   -26   plugins/standardoutputview/outputwidget.cpp
M  +5    -4    plugins/standardoutputview/outputwidget.h

https://commits.kde.org/kdevelop/f856a9a63910a7e9f2fbcbcd63f85100599805ae

diff --git a/plugins/standardoutputview/outputwidget.cpp \
b/plugins/standardoutputview/outputwidget.cpp index 6c7b4e496b..5bae159db5 100644
--- a/plugins/standardoutputview/outputwidget.cpp
+++ b/plugins/standardoutputview/outputwidget.cpp
@@ -192,6 +192,17 @@ OutputWidget::OutputWidget(QWidget* parent, const ToolViewData* \
tvdata)  enableActions();
 }
 
+OutputWidget::~OutputWidget()
+{
+    // Disconnect our widget to prevent updateFilter() slot calling from parent's \
destructor, +    // which leads to segfault since m_views hash will be destroyed \
before. +    if (m_tabwidget) {
+        m_tabwidget->disconnect(this);
+    } else if (m_stackwidget) {
+        m_stackwidget->disconnect(this);
+    }
+}
+
 void OutputWidget::clearModel()
 {
     auto view = qobject_cast<QAbstractItemView*>(currentWidget());
@@ -254,7 +265,7 @@ void OutputWidget::removeOutput( int id )
 {
     if( data->outputdata.contains( id ) && m_views.contains( id ) )
     {
-        QTreeView *view = m_views.value(id).view.data();
+        auto view = m_views.value(id).view;
         if( data->type & KDevelop::IOutputView::MultipleView || data->type & \
KDevelop::IOutputView::HistoryView )  {
             if( data->type & KDevelop::IOutputView::MultipleView )
@@ -272,19 +283,16 @@ void OutputWidget::removeOutput( int id )
                     m_stackwidget->removeWidget(view);
                 }
             }
-        } else {  // KDevelop::IOutputView::OneView
-            /* TODO: this branch of execution has no result because of the \
                "m_views.remove( id );"
-             * after the if-else block. Need to find out which behavior has sense.
-             */
-            FilteredView& fview = m_views[id];
-            fview.view->setModel(nullptr);
-            fview.view->setItemDelegate(nullptr);
-            if (fview.proxyModel) {
-                fview.proxyModel = QSharedPointer<QSortFilterProxyModel>();
-                fview.filter = QString();
-            }
+        } else {
+            // KDevelop::IOutputView::OneView case
+            // Do nothig here since our single view will be automatically removed \
from layout +            // during it's destroy
         }
-        m_views.remove(id);
+
+        auto fv = m_views.take(id);
+        // remove our view with proxy model which is view's child (see \
outputFilter() method). +        delete fv.view;
+
         emit outputRemoved( data->toolViewId, id );
     }
     enableActions();
@@ -339,7 +347,7 @@ QWidget* OutputWidget::currentWidget() const
         widget = m_stackwidget->currentWidget();
     } else
     {
-        widget = m_views.begin().value().view.data();
+        widget = m_views.begin()->view;
     }
     return widget;
 }
@@ -471,7 +479,7 @@ void OutputWidget::activate(const QModelIndex& index)
 
 QTreeView* OutputWidget::createListView(int id)
 {
-    auto createHelper = [&]() -> QSharedPointer<QTreeView> {
+    auto createHelper = [&]() -> QTreeView* {
         KDevelop::FocusedTreeView* listview = new KDevelop::FocusedTreeView(this);
         listview->setEditTriggers( QAbstractItemView::NoEditTriggers );
         listview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); //Always \
enable the scrollbar, so it doesn't flash around @@ -487,10 +495,10 @@ QTreeView* \
                OutputWidget::createListView(int id)
         connect(listview, &QTreeView::activated, this, &OutputWidget::activate);
         connect(listview, &QTreeView::clicked, this, &OutputWidget::activate);
 
-        return QSharedPointer<QTreeView>(listview);
+        return listview;
     };
 
-    QSharedPointer<QTreeView> listview;
+    QTreeView* listview = nullptr;
     if( !m_views.contains(id) )
     {
         bool newView = true;
@@ -502,18 +510,18 @@ QTreeView* OutputWidget::createListView(int id)
 
             if( data->type & KDevelop::IOutputView::MultipleView )
             {
-                m_tabwidget->addTab(listview.data(), \
data->outputdata.value(id)->title); +                m_tabwidget->addTab(listview, \
data->outputdata.value(id)->title);  } else
             {
-                m_stackwidget->addWidget(listview.data());
-                m_stackwidget->setCurrentWidget(listview.data());
+                const int index = m_stackwidget->addWidget(listview);
+                m_stackwidget->setCurrentIndex(index);
             }
         } else
         {
             if( m_views.isEmpty() )
             {
                 listview = createHelper();
-                layout()->addWidget(listview.data());
+                layout()->addWidget(listview);
             } else
             {
                 listview = m_views.begin().value().view;
@@ -532,14 +540,14 @@ QTreeView* OutputWidget::createListView(int id)
         listview = m_views.value(id).view;
     }
     enableActions();
-    return listview.data();
+    return listview;
 }
 
 void OutputWidget::raiseOutput(int id)
 {
     if( m_views.contains(id) )
     {
-        auto view = m_views.value(id).view.data();
+        auto view = m_views.value(id).view;
         if( data->type & KDevelop::IOutputView::MultipleView )
         {
             int idx = m_tabwidget->indexOf(view);
@@ -646,10 +654,11 @@ void OutputWidget::outputFilter(const QString& filter)
     auto proxyModel = qobject_cast<QSortFilterProxyModel*>(view->model());
     if( !proxyModel )
     {
-        proxyModel = new QSortFilterProxyModel(view->model());
+        // create new proxy model and make view it's parent. This allows us destroy \
view and +        // it's model with "one shot" (see removeOutput() method).
+        fvIt->proxyModel = proxyModel = new QSortFilterProxyModel(view);
         proxyModel->setDynamicSortFilter(true);
         proxyModel->setSourceModel(view->model());
-        fvIt->proxyModel = QSharedPointer<QSortFilterProxyModel>(proxyModel);
         view->setModel(proxyModel);
     }
     QRegExp regExp(filter, Qt::CaseInsensitive);
@@ -676,7 +685,7 @@ void OutputWidget::setTitle(int outputId, const QString& title)
 {
     auto fview = m_views.value(outputId, FilteredView{});
     if (fview.view && (data->type & KDevelop::IOutputView::MultipleView)) {
-        int idx = m_tabwidget->indexOf(fview.view.data());
+        const int idx = m_tabwidget->indexOf(fview.view);
         if (idx >= 0) {
             m_tabwidget->setTabText(idx, title);
         }
diff --git a/plugins/standardoutputview/outputwidget.h \
b/plugins/standardoutputview/outputwidget.h index a9db4717d6..fe1f4a9383 100644
--- a/plugins/standardoutputview/outputwidget.h
+++ b/plugins/standardoutputview/outputwidget.h
@@ -22,9 +22,8 @@
 #ifndef KDEVPLATFORM_PLUGIN_OUTPUTWIDGET_H
 #define KDEVPLATFORM_PLUGIN_OUTPUTWIDGET_H
 
-#include <QMap>
+#include <QHash>
 #include <QWidget>
-#include <QSharedPointer>
 
 #include <interfaces/itoolviewactionlistener.h>
 #include <outputview/ioutputviewmodel.h>
@@ -55,6 +54,8 @@ class OutputWidget : public QWidget, public \
KDevelop::IToolViewActionListener  
 public:
     OutputWidget(QWidget* parent, const ToolViewData* data);
+    ~OutputWidget() override;
+
     void removeOutput( int id );
     void raiseOutput( int id );
 public Q_SLOTS:
@@ -103,8 +104,8 @@ private:
     int currentOutputIndex();
 
     struct FilteredView {
-        QSharedPointer<QTreeView> view;
-        QSharedPointer<QSortFilterProxyModel> proxyModel;
+        QTreeView* view = nullptr;
+        QSortFilterProxyModel* proxyModel = nullptr;
         QString filter;
     };
     QHash<int, FilteredView>::iterator findFilteredView(QAbstractItemView *view);


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic