QmlDesigner: destroy BaseTextEditor before DesignDocument

Stale TextEditor::BaseTextEditor instances could still receive paint events after their DesignDocument had already been deleted.
This led to a use-after-free in
TextEditorWidgetPrivate::updateLineAnnotation()
and sporadic heap corruption on shutdown.

shutdown problem at
https://the-qt-company-00.sentry.io/issues/6409328760
paint problem at
https://the-qt-company-00.sentry.io/issues/6648131182

Change-Id: I5cc9762257d92cd1f6d9cf790ef77b3f1c2b0844
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tim Jenßen
2025-06-04 16:16:04 +02:00
committed by Tim Jenssen
parent a80281c6ed
commit 4e5ea43f30
2 changed files with 10 additions and 1 deletions

View File

@@ -63,8 +63,9 @@ void TextEditorView::modelAttached(Model *model)
AbstractView::modelAttached(model); AbstractView::modelAttached(model);
DesignDocument *designDocument = QmlDesignerPlugin::instance()->currentDesignDocument();
auto textEditor = Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor>( auto textEditor = Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor>(
QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); designDocument->textEditor()->duplicate());
static constexpr char qmlTextEditorContextId[] = "QmlDesigner::TextEditor"; static constexpr char qmlTextEditorContextId[] = "QmlDesigner::TextEditor";
IContext::attach(textEditor->widget(), IContext::attach(textEditor->widget(),
Context(qmlTextEditorContextId, Constants::qtQuickToolsMenuContextId), Context(qmlTextEditorContextId, Constants::qtQuickToolsMenuContextId),
@@ -72,6 +73,12 @@ void TextEditorView::modelAttached(Model *model)
m_widget->contextHelp(callback); m_widget->contextHelp(callback);
}); });
m_widget->setTextEditor(std::move(textEditor)); m_widget->setTextEditor(std::move(textEditor));
disconnect(m_designDocumentConnection);
m_designDocumentConnection = connect(designDocument,
&DesignDocument::designDocumentClosed,
m_widget,
[this] { m_widget->setTextEditor(nullptr); });
} }
void TextEditorView::modelAboutToBeDetached(Model *model) void TextEditorView::modelAboutToBeDetached(Model *model)
@@ -80,6 +87,7 @@ void TextEditorView::modelAboutToBeDetached(Model *model)
if (m_widget) if (m_widget)
m_widget->setTextEditor(nullptr); m_widget->setTextEditor(nullptr);
disconnect(m_designDocumentConnection);
} }
void TextEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) void TextEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)

View File

@@ -78,6 +78,7 @@ public:
private: private:
QPointer<TextEditorWidget> m_widget; QPointer<TextEditorWidget> m_widget;
QMetaObject::Connection m_designDocumentConnection;
bool m_errorState = false; bool m_errorState = false;
}; };