forked from qt-creator/qt-creator
		
	C++: Fix crash after triggering completion and closing editor
Fix use-after-free for the following case:
  1. Open an editor
  2. Trigger a long processing completion
     (e.g. simulate with QThread::msleep in
      CppCompletionAssistInterface::getCppSpecifics)
  3. ...and immediately close the editor (e.g. with Ctrl+W)
  4. Wait until it crashes.
The completion thread relied on the BuiltinEditorDocumentParser object,
which is deleted once the editor is closed. Fixed by sharing the
ownership of that object between the *EditorDocumentProcessor and the
completion assist interface.
This case came up when doing tests for the bug report below.
Task-number: QTCREATORBUG-14991
Change-Id: I0b009229e68fc6b7838740858cdc41a32403fe6f
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
			
			
This commit is contained in:
		@@ -104,14 +104,14 @@ ProjectPart::Ptr BaseEditorDocumentParser::projectPart() const
 | 
			
		||||
    return state().projectPart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BaseEditorDocumentParser *BaseEditorDocumentParser::get(const QString &filePath)
 | 
			
		||||
BaseEditorDocumentParser::Ptr BaseEditorDocumentParser::get(const QString &filePath)
 | 
			
		||||
{
 | 
			
		||||
    CppModelManager *cmmi = CppModelManager::instance();
 | 
			
		||||
    if (CppEditorDocumentHandle *cppEditorDocument = cmmi->cppEditorDocument(filePath)) {
 | 
			
		||||
        if (BaseEditorDocumentProcessor *processor = cppEditorDocument->processor())
 | 
			
		||||
            return processor->parser();
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
    return BaseEditorDocumentParser::Ptr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart(const QString &filePath,
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,8 @@ class CPPTOOLS_EXPORT BaseEditorDocumentParser : public QObject
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    static BaseEditorDocumentParser *get(const QString &filePath);
 | 
			
		||||
    using Ptr = QSharedPointer<BaseEditorDocumentParser>;;
 | 
			
		||||
    static Ptr get(const QString &filePath);
 | 
			
		||||
 | 
			
		||||
    struct Configuration {
 | 
			
		||||
        bool stickToPreviousProjectPart = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -118,7 +118,7 @@ QList<QTextEdit::ExtraSelection> BaseEditorDocumentProcessor::toTextEditorSelect
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
 | 
			
		||||
                                            BaseEditorDocumentParser *parser,
 | 
			
		||||
                                            BaseEditorDocumentParser::Ptr parser,
 | 
			
		||||
                                            BaseEditorDocumentParser::InMemoryInfo info)
 | 
			
		||||
{
 | 
			
		||||
    future.setProgressRange(0, 1);
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ public:
 | 
			
		||||
    virtual void recalculateSemanticInfoDetached(bool force) = 0;
 | 
			
		||||
    virtual CppTools::SemanticInfo recalculateSemanticInfo() = 0;
 | 
			
		||||
    virtual CPlusPlus::Snapshot snapshot() = 0;
 | 
			
		||||
    virtual BaseEditorDocumentParser *parser() = 0;
 | 
			
		||||
    virtual BaseEditorDocumentParser::Ptr parser() = 0;
 | 
			
		||||
    virtual bool isParserRunning() const = 0;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@@ -85,7 +85,7 @@ protected:
 | 
			
		||||
            QTextDocument *textDocument);
 | 
			
		||||
 | 
			
		||||
    static void runParser(QFutureInterface<void> &future,
 | 
			
		||||
                          CppTools::BaseEditorDocumentParser *parser,
 | 
			
		||||
                          BaseEditorDocumentParser::Ptr parser,
 | 
			
		||||
                          BaseEditorDocumentParser::InMemoryInfo info);
 | 
			
		||||
 | 
			
		||||
    // Convenience
 | 
			
		||||
 
 | 
			
		||||
@@ -226,11 +226,11 @@ ProjectPart::HeaderPaths BuiltinEditorDocumentParser::headerPaths() const
 | 
			
		||||
    return extraState().headerPaths;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BuiltinEditorDocumentParser *BuiltinEditorDocumentParser::get(const QString &filePath)
 | 
			
		||||
BuiltinEditorDocumentParser::Ptr BuiltinEditorDocumentParser::get(const QString &filePath)
 | 
			
		||||
{
 | 
			
		||||
    if (BaseEditorDocumentParser *b = BaseEditorDocumentParser::get(filePath))
 | 
			
		||||
        return qobject_cast<BuiltinEditorDocumentParser *>(b);
 | 
			
		||||
    return 0;
 | 
			
		||||
    if (BaseEditorDocumentParser::Ptr b = BaseEditorDocumentParser::get(filePath))
 | 
			
		||||
        return b.objectCast<BuiltinEditorDocumentParser>();
 | 
			
		||||
    return BuiltinEditorDocumentParser::Ptr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BuiltinEditorDocumentParser::addFileAndDependencies(Snapshot *snapshot,
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,8 @@ signals:
 | 
			
		||||
    void finished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    static BuiltinEditorDocumentParser *get(const QString &filePath);
 | 
			
		||||
    using Ptr = QSharedPointer<BuiltinEditorDocumentParser>;
 | 
			
		||||
    static Ptr get(const QString &filePath);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void updateHelper(const InMemoryInfo &info) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ BuiltinEditorDocumentProcessor::BuiltinEditorDocumentProcessor(
 | 
			
		||||
        TextEditor::TextDocument *document,
 | 
			
		||||
        bool enableSemanticHighlighter)
 | 
			
		||||
    : BaseEditorDocumentProcessor(document)
 | 
			
		||||
    , m_parser(document->filePath().toString())
 | 
			
		||||
    , m_parser(new BuiltinEditorDocumentParser(document->filePath().toString()))
 | 
			
		||||
    , m_codeWarningsUpdated(false)
 | 
			
		||||
    , m_semanticHighlighter(enableSemanticHighlighter
 | 
			
		||||
                            ? new CppTools::SemanticHighlighter(document)
 | 
			
		||||
@@ -135,9 +135,9 @@ BuiltinEditorDocumentProcessor::BuiltinEditorDocumentProcessor(
 | 
			
		||||
 | 
			
		||||
    QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
 | 
			
		||||
 | 
			
		||||
    BaseEditorDocumentParser::Configuration config = m_parser.configuration();
 | 
			
		||||
    BaseEditorDocumentParser::Configuration config = m_parser->configuration();
 | 
			
		||||
    config.usePrecompiledHeaders = cms->pchUsage() != CppCodeModelSettings::PchUse_None;
 | 
			
		||||
    m_parser.setConfiguration(config);
 | 
			
		||||
    m_parser->setConfiguration(config);
 | 
			
		||||
 | 
			
		||||
    if (m_semanticHighlighter) {
 | 
			
		||||
        m_semanticHighlighter->setHighlightingRunner(
 | 
			
		||||
@@ -152,7 +152,7 @@ BuiltinEditorDocumentProcessor::BuiltinEditorDocumentProcessor(
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connect(&m_parser, &BuiltinEditorDocumentParser::finished,
 | 
			
		||||
    connect(m_parser.data(), &BuiltinEditorDocumentParser::finished,
 | 
			
		||||
            this, &BuiltinEditorDocumentProcessor::onParserFinished);
 | 
			
		||||
    connect(&m_semanticInfoUpdater, &SemanticInfoUpdater::updated,
 | 
			
		||||
            this, &BuiltinEditorDocumentProcessor::onSemanticInfoUpdated);
 | 
			
		||||
@@ -171,14 +171,14 @@ void BuiltinEditorDocumentProcessor::run()
 | 
			
		||||
                                       BuiltinEditorDocumentParser::InMemoryInfo(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BaseEditorDocumentParser *BuiltinEditorDocumentProcessor::parser()
 | 
			
		||||
BaseEditorDocumentParser::Ptr BuiltinEditorDocumentProcessor::parser()
 | 
			
		||||
{
 | 
			
		||||
    return &m_parser;
 | 
			
		||||
    return m_parser;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CPlusPlus::Snapshot BuiltinEditorDocumentProcessor::snapshot()
 | 
			
		||||
{
 | 
			
		||||
    return m_parser.snapshot();
 | 
			
		||||
    return m_parser->snapshot();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BuiltinEditorDocumentProcessor::recalculateSemanticInfoDetached(bool force)
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ public:
 | 
			
		||||
    void recalculateSemanticInfoDetached(bool force) override;
 | 
			
		||||
    void semanticRehighlight() override;
 | 
			
		||||
    CppTools::SemanticInfo recalculateSemanticInfo() override;
 | 
			
		||||
    BaseEditorDocumentParser *parser() override;
 | 
			
		||||
    BaseEditorDocumentParser::Ptr parser() override;
 | 
			
		||||
    CPlusPlus::Snapshot snapshot() override;
 | 
			
		||||
    bool isParserRunning() const override;
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +66,7 @@ private:
 | 
			
		||||
    SemanticInfo::Source createSemanticInfoSource(bool force) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    BuiltinEditorDocumentParser m_parser;
 | 
			
		||||
    BuiltinEditorDocumentParser::Ptr m_parser;
 | 
			
		||||
    QFuture<void> m_parserFuture;
 | 
			
		||||
 | 
			
		||||
    CPlusPlus::Snapshot m_documentSnapshot;
 | 
			
		||||
 
 | 
			
		||||
@@ -426,13 +426,13 @@ AssistInterface *InternalCompletionAssistProvider::createAssistInterface(
 | 
			
		||||
{
 | 
			
		||||
    QTC_ASSERT(textEditorWidget, return 0);
 | 
			
		||||
 | 
			
		||||
    CppModelManager *modelManager = CppModelManager::instance();
 | 
			
		||||
    return new CppCompletionAssistInterface(filePath,
 | 
			
		||||
                                            textEditorWidget,
 | 
			
		||||
                                            BuiltinEditorDocumentParser::get(filePath),
 | 
			
		||||
                                            languageFeatures,
 | 
			
		||||
                                            position,
 | 
			
		||||
                                            reason,
 | 
			
		||||
                                            modelManager->workingCopy());
 | 
			
		||||
                                            CppModelManager::instance()->workingCopy());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// -----------------
 | 
			
		||||
@@ -2187,11 +2187,11 @@ void CppCompletionAssistInterface::getCppSpecifics() const
 | 
			
		||||
        return;
 | 
			
		||||
    m_gotCppSpecifics = true;
 | 
			
		||||
 | 
			
		||||
    if (BuiltinEditorDocumentParser *parser = BuiltinEditorDocumentParser::get(fileName())) {
 | 
			
		||||
        parser->update(BuiltinEditorDocumentParser::InMemoryInfo(false));
 | 
			
		||||
        m_snapshot = parser->snapshot();
 | 
			
		||||
        m_headerPaths = parser->headerPaths();
 | 
			
		||||
        if (Document::Ptr document = parser->document())
 | 
			
		||||
    if (m_parser) {
 | 
			
		||||
        m_parser->update(BuiltinEditorDocumentParser::InMemoryInfo(false));
 | 
			
		||||
        m_snapshot = m_parser->snapshot();
 | 
			
		||||
        m_headerPaths = m_parser->headerPaths();
 | 
			
		||||
        if (Document::Ptr document = m_parser->document())
 | 
			
		||||
            m_languageFeatures = document->languageFeatures();
 | 
			
		||||
        else
 | 
			
		||||
            m_languageFeatures = LanguageFeatures::defaultFeatures();
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
#ifndef CPPCOMPLETIONASSIST_H
 | 
			
		||||
#define CPPCOMPLETIONASSIST_H
 | 
			
		||||
 | 
			
		||||
#include "builtineditordocumentparser.h"
 | 
			
		||||
#include "cppcompletionassistprocessor.h"
 | 
			
		||||
#include "cppcompletionassistprovider.h"
 | 
			
		||||
#include "cppmodelmanager.h"
 | 
			
		||||
@@ -171,11 +172,13 @@ class CppCompletionAssistInterface : public TextEditor::AssistInterface
 | 
			
		||||
public:
 | 
			
		||||
    CppCompletionAssistInterface(const QString &filePath,
 | 
			
		||||
                                 const TextEditor::TextEditorWidget *textEditorWidget,
 | 
			
		||||
                                 BuiltinEditorDocumentParser::Ptr parser,
 | 
			
		||||
                                 const CPlusPlus::LanguageFeatures &languageFeatures,
 | 
			
		||||
                                 int position,
 | 
			
		||||
                                 TextEditor::AssistReason reason,
 | 
			
		||||
                                 const WorkingCopy &workingCopy)
 | 
			
		||||
        : TextEditor::AssistInterface(textEditorWidget->document(), position, filePath, reason)
 | 
			
		||||
        , m_parser(parser)
 | 
			
		||||
        , m_gotCppSpecifics(false)
 | 
			
		||||
        , m_workingCopy(workingCopy)
 | 
			
		||||
        , m_languageFeatures(languageFeatures)
 | 
			
		||||
@@ -204,6 +207,7 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
    void getCppSpecifics() const;
 | 
			
		||||
 | 
			
		||||
    BuiltinEditorDocumentParser::Ptr m_parser;
 | 
			
		||||
    mutable bool m_gotCppSpecifics;
 | 
			
		||||
    WorkingCopy m_workingCopy;
 | 
			
		||||
    mutable CPlusPlus::Snapshot m_snapshot;
 | 
			
		||||
 
 | 
			
		||||
@@ -910,7 +910,7 @@ void CppToolsPlugin::test_modelmanager_precompiled_headers()
 | 
			
		||||
        QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
 | 
			
		||||
        QVERIFY(mm->isCppEditor(editor));
 | 
			
		||||
 | 
			
		||||
        auto *parser = BuiltinEditorDocumentParser::get(fileName);
 | 
			
		||||
        auto parser = BuiltinEditorDocumentParser::get(fileName);
 | 
			
		||||
        QVERIFY(parser);
 | 
			
		||||
        BaseEditorDocumentParser::Configuration config = parser->configuration();
 | 
			
		||||
        config.usePrecompiledHeaders = true;
 | 
			
		||||
@@ -994,7 +994,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor()
 | 
			
		||||
        QVERIFY(mm->isCppEditor(editor));
 | 
			
		||||
 | 
			
		||||
        const QString filePath = editor->document()->filePath().toString();
 | 
			
		||||
        BaseEditorDocumentParser *parser = BaseEditorDocumentParser::get(filePath);
 | 
			
		||||
        const auto parser = BaseEditorDocumentParser::get(filePath);
 | 
			
		||||
        BaseEditorDocumentParser::Configuration config = parser->configuration();
 | 
			
		||||
        config.editorDefines = editorDefines.toUtf8();
 | 
			
		||||
        parser->setConfiguration(config);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user