From b42a82a455fddac0ac4199596d5b3eaabaca3f47 Mon Sep 17 00:00:00 2001 From: Azhar Momin Date: Tue, 17 Feb 2026 19:30:55 +0530 Subject: [PATCH] Fix segfault in ContentProvider QCache in ContentProvider handles cleanup for ThreadModel. Setting NeoChatRoom as parent for ThreadModel caused it to be deleted outside of QCache control leading to a double-free. --- autotests/modeltest.cpp | 10 ++++---- src/messagecontent/models/threadmodel.cpp | 29 +++++++---------------- src/messagecontent/models/threadmodel.h | 2 ++ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/autotests/modeltest.cpp b/autotests/modeltest.cpp index 7399af754..7c7424f07 100644 --- a/autotests/modeltest.cpp +++ b/autotests/modeltest.cpp @@ -194,21 +194,23 @@ void ModelTest::testEventMessageContentModel() void ModelTest::testThreadModel() { - auto model = new ThreadModel(eventId, room); - auto tester = new QAbstractItemModelTester(model, model); + auto model = std::make_unique(eventId, room); + auto tester = new QAbstractItemModelTester(model.get(), model.get()); tester->setUseFetchMore(true); } void ModelTest::testThreadFetchModel() { - auto model = new ThreadFetchModel(new ThreadModel(eventId, room)); + auto threadModel = std::make_unique(eventId, room); + auto model = new ThreadFetchModel(threadModel.get()); auto tester = new QAbstractItemModelTester(model, model); tester->setUseFetchMore(true); } void ModelTest::testThreadChatBarModel() { - auto model = new ThreadChatBarModel(new ThreadModel(eventId, room), room); + auto threadModel = std::make_unique(eventId, room); + auto model = new ThreadChatBarModel(threadModel.get(), room); auto tester = new QAbstractItemModelTester(model, model); tester->setUseFetchMore(true); } diff --git a/src/messagecontent/models/threadmodel.cpp b/src/messagecontent/models/threadmodel.cpp index 9d1262231..a48f11883 100644 --- a/src/messagecontent/models/threadmodel.cpp +++ b/src/messagecontent/models/threadmodel.cpp @@ -15,7 +15,8 @@ #include "neochatroom.h" ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room) - : QConcatenateTablesProxyModel(room) + : QConcatenateTablesProxyModel() + , m_room(room) , m_threadRootId(threadRootId) , m_threadFetchModel(new ThreadFetchModel(this)) , m_threadChatBarModel(new ThreadChatBarModel(this, room)) @@ -51,12 +52,7 @@ ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room) void ThreadModel::checkPending() { - const auto room = dynamic_cast(QObject::parent()); - if (room == nullptr) { - return; - } - - for (auto i = room->pendingEvents().rbegin(); i != room->pendingEvents().rend(); i++) { + for (auto i = m_room->pendingEvents().rbegin(); i != m_room->pendingEvents().rend(); i++) { if (const auto roomMessageEvent = eventCast(i->event()); roomMessageEvent->isThreaded() && roomMessageEvent->threadRootEventId() == m_threadRootId) { addNewEvent(roomMessageEvent); @@ -83,9 +79,8 @@ bool ThreadModel::moreEventsAvailable(const QModelIndex &parent) const void ThreadModel::fetchMoreEvents(int max) { if (!m_currentJob && m_nextBatch.has_value()) { - const auto room = dynamic_cast(QObject::parent()); - const auto connection = room->connection(); - m_currentJob = connection->callApi(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max); + m_currentJob = + m_room->connection()->callApi(m_room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max); Q_EMIT moreEventsAvailableChanged(); connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() { auto newEvents = m_currentJob->chunk(); @@ -130,15 +125,11 @@ void ThreadModel::addModels() clearModels(); } - const auto room = dynamic_cast(QObject::parent()); - if (room == nullptr) { - return; - } addSourceModel(m_threadFetchModel); for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) { - const auto contentModel = ContentProvider::self().contentModelForEvent(room, *it); + const auto contentModel = ContentProvider::self().contentModelForEvent(m_room, *it); if (contentModel != nullptr) { - addSourceModel(ContentProvider::self().contentModelForEvent(room, *it)); + addSourceModel(contentModel); } } addSourceModel(m_threadChatBarModel); @@ -149,13 +140,9 @@ void ThreadModel::addModels() void ThreadModel::clearModels() { - const auto room = dynamic_cast(QObject::parent()); - if (room == nullptr) { - return; - } removeSourceModel(m_threadFetchModel); for (const auto &model : m_events) { - const auto contentModel = ContentProvider::self().contentModelForEvent(room, model); + const auto contentModel = ContentProvider::self().contentModelForEvent(m_room, model); if (sourceModels().contains(contentModel)) { removeSourceModel(contentModel); } diff --git a/src/messagecontent/models/threadmodel.h b/src/messagecontent/models/threadmodel.h index 8ea460707..f0adc35f9 100644 --- a/src/messagecontent/models/threadmodel.h +++ b/src/messagecontent/models/threadmodel.h @@ -164,6 +164,8 @@ Q_SIGNALS: void moreEventsAvailableChanged(); private: + QPointer m_room; + QString m_threadRootId; QPointer m_threadRootContentModel;