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.
This commit is contained in:
Azhar Momin
2026-02-17 19:30:55 +05:30
committed by Joshua Goins
parent 99aed0993e
commit b42a82a455
3 changed files with 16 additions and 25 deletions

View File

@@ -194,21 +194,23 @@ void ModelTest::testEventMessageContentModel()
void ModelTest::testThreadModel() void ModelTest::testThreadModel()
{ {
auto model = new ThreadModel(eventId, room); auto model = std::make_unique<ThreadModel>(eventId, room);
auto tester = new QAbstractItemModelTester(model, model); auto tester = new QAbstractItemModelTester(model.get(), model.get());
tester->setUseFetchMore(true); tester->setUseFetchMore(true);
} }
void ModelTest::testThreadFetchModel() void ModelTest::testThreadFetchModel()
{ {
auto model = new ThreadFetchModel(new ThreadModel(eventId, room)); auto threadModel = std::make_unique<ThreadModel>(eventId, room);
auto model = new ThreadFetchModel(threadModel.get());
auto tester = new QAbstractItemModelTester(model, model); auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true); tester->setUseFetchMore(true);
} }
void ModelTest::testThreadChatBarModel() void ModelTest::testThreadChatBarModel()
{ {
auto model = new ThreadChatBarModel(new ThreadModel(eventId, room), room); auto threadModel = std::make_unique<ThreadModel>(eventId, room);
auto model = new ThreadChatBarModel(threadModel.get(), room);
auto tester = new QAbstractItemModelTester(model, model); auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true); tester->setUseFetchMore(true);
} }

View File

@@ -15,7 +15,8 @@
#include "neochatroom.h" #include "neochatroom.h"
ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room) ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
: QConcatenateTablesProxyModel(room) : QConcatenateTablesProxyModel()
, m_room(room)
, m_threadRootId(threadRootId) , m_threadRootId(threadRootId)
, m_threadFetchModel(new ThreadFetchModel(this)) , m_threadFetchModel(new ThreadFetchModel(this))
, m_threadChatBarModel(new ThreadChatBarModel(this, room)) , m_threadChatBarModel(new ThreadChatBarModel(this, room))
@@ -51,12 +52,7 @@ ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
void ThreadModel::checkPending() void ThreadModel::checkPending()
{ {
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent()); for (auto i = m_room->pendingEvents().rbegin(); i != m_room->pendingEvents().rend(); i++) {
if (room == nullptr) {
return;
}
for (auto i = room->pendingEvents().rbegin(); i != room->pendingEvents().rend(); i++) {
if (const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(i->event()); if (const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(i->event());
roomMessageEvent->isThreaded() && roomMessageEvent->threadRootEventId() == m_threadRootId) { roomMessageEvent->isThreaded() && roomMessageEvent->threadRootEventId() == m_threadRootId) {
addNewEvent(roomMessageEvent); addNewEvent(roomMessageEvent);
@@ -83,9 +79,8 @@ bool ThreadModel::moreEventsAvailable(const QModelIndex &parent) const
void ThreadModel::fetchMoreEvents(int max) void ThreadModel::fetchMoreEvents(int max)
{ {
if (!m_currentJob && m_nextBatch.has_value()) { if (!m_currentJob && m_nextBatch.has_value()) {
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent()); m_currentJob =
const auto connection = room->connection(); m_room->connection()->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(m_room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max);
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max);
Q_EMIT moreEventsAvailableChanged(); Q_EMIT moreEventsAvailableChanged();
connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() { connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() {
auto newEvents = m_currentJob->chunk(); auto newEvents = m_currentJob->chunk();
@@ -130,15 +125,11 @@ void ThreadModel::addModels()
clearModels(); clearModels();
} }
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
if (room == nullptr) {
return;
}
addSourceModel(m_threadFetchModel); addSourceModel(m_threadFetchModel);
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) { 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) { if (contentModel != nullptr) {
addSourceModel(ContentProvider::self().contentModelForEvent(room, *it)); addSourceModel(contentModel);
} }
} }
addSourceModel(m_threadChatBarModel); addSourceModel(m_threadChatBarModel);
@@ -149,13 +140,9 @@ void ThreadModel::addModels()
void ThreadModel::clearModels() void ThreadModel::clearModels()
{ {
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
if (room == nullptr) {
return;
}
removeSourceModel(m_threadFetchModel); removeSourceModel(m_threadFetchModel);
for (const auto &model : m_events) { 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)) { if (sourceModels().contains(contentModel)) {
removeSourceModel(contentModel); removeSourceModel(contentModel);
} }

View File

@@ -164,6 +164,8 @@ Q_SIGNALS:
void moreEventsAvailableChanged(); void moreEventsAvailableChanged();
private: private:
QPointer<NeoChatRoom> m_room;
QString m_threadRootId; QString m_threadRootId;
QPointer<MessageContentModel> m_threadRootContentModel; QPointer<MessageContentModel> m_threadRootContentModel;