Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/latexdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ LatexDocument::~LatexDocument()
mLineSnapshot.clear();

qDeleteAll(docStructure);

// make sure this (about to be freed) document can never linger in the
// shared project cache as a dangling pointer (see getListOfDocs)
if (lp) lp->projectDocuments.clear();
}

void LatexDocument::setFileName(const QString &fileName)
Expand Down Expand Up @@ -1943,11 +1947,13 @@ void LatexDocument::setMasterDocument(LatexDocument *doc, bool recheck)
void LatexDocument::addChild(LatexDocument *doc)
{
childDocs.insert(doc);
lp->projectDocuments.clear(); // graph changed, invalidate cache
}

void LatexDocument::removeChild(LatexDocument *doc)
{
childDocs.remove(doc);
lp->projectDocuments.clear(); // graph changed, invalidate cache
}

bool LatexDocument::containsChild(LatexDocument *doc) const
Expand Down Expand Up @@ -2168,6 +2174,9 @@ void LatexDocuments::addDocument(LatexDocument *document, bool hidden)
}
connect(document, SIGNAL(updateBibTeXFiles()), SLOT(bibTeXFilesNeedUpdate()));
document->parent = this;
// document set changed, invalidate cached project document lists (see getListOfDocs)
if (document->lp) document->lp->projectDocuments.clear();
if (masterDocument && masterDocument->lp) masterDocument->lp->projectDocuments.clear();
if (masterDocument) {
// repaint all docs
foreach (const LatexDocument *doc, documents) {
Expand All @@ -2179,6 +2188,10 @@ void LatexDocuments::addDocument(LatexDocument *document, bool hidden)

void LatexDocuments::deleteDocument(LatexDocument *document, bool hidden, bool purge)
{
// document set / master-child graph is about to change, invalidate cached
// project document lists so they can't keep dangling pointers (see getListOfDocs)
if (document->lp) document->lp->projectDocuments.clear();
if (masterDocument && masterDocument->lp) masterDocument->lp->projectDocuments.clear();
// save caching information
document->saveCachingData(m_cachingFolder);
if (!hidden)
Expand Down Expand Up @@ -2474,6 +2487,8 @@ LatexDocument *LatexDocuments::findDocumentFromName(const QString &fileName) con
void LatexDocuments::reorder(const QList<LatexDocument *> &order)
{
if (order.size() != documents.size()) qDebug() << "Warning: Size of list of documents for reordering differs from current documents";
// order of the cached project document list may change (see getListOfDocs)
if (masterDocument && masterDocument->lp) masterDocument->lp->projectDocuments.clear();
foreach (LatexDocument *doc, order) {
int n = documents.removeAll(doc);
if (n > 1) qDebug() << "Warning: document listed multiple times in LatexDocuments";
Expand Down Expand Up @@ -2627,6 +2642,9 @@ void LatexDocuments::updateBibFiles(bool updateFiles)

void LatexDocuments::removeDocs(QStringList removeIncludes)
{
// documents may be removed/deleted below, invalidate cached project
// document lists so they can't keep dangling pointers (see getListOfDocs)
if (masterDocument && masterDocument->lp) masterDocument->lp->projectDocuments.clear();
QSet<LatexDocument*> lstRecheckLabels;
foreach (QString fname, removeIncludes) {
LatexDocument *dc = findDocumentFromName(fname);
Expand Down
36 changes: 35 additions & 1 deletion src/tests/latexdocument_t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,39 @@ LatexDocumentTest::LatexDocumentTest(LatexEditorView* view): m_edView(view){
m_doc=m_edView->getDocument();
}

#endif
/*!
* Regression test for the use-after-free crash in LatexDocument::getListOfDocs()
* (SIGABRT in getListOfDocs while rescanning a multi-file project).
*/
void LatexDocumentTest::getListOfDocsCacheInvalidation(){
LatexDocuments docs;
LatexDocument *master = new LatexDocument();
LatexDocument *child = new LatexDocument();
master->setFileName("/tmp/texstudio_test_master.tex");
child->setFileName("/tmp/texstudio_test_child.tex");
docs.addDocument(master, false); // visible master
docs.addDocument(child, true); // hidden included document

// register child as an included document of master
master->addChild(child);

// first call builds and caches the project document list
QList<LatexDocument *> before = master->getListOfDocs();
QVERIFY2(before.contains(master), "master document missing from list");
QVERIFY2(before.contains(child), "child document missing from list");

// break the relationship: the cache must be invalidated so the stale
// (potentially freed) child pointer is not returned again
master->removeChild(child);
QList<LatexDocument *> after = master->getListOfDocs();
QVERIFY2(after.contains(master), "master document missing after removeChild");
QVERIFY2(!after.contains(child), "stale child returned from cached project document list (cache not invalidated)");

// docs has no destructor that deletes its documents, so free them here.
// master->childDocs no longer references child and neither document holds
// a masterDocument pointer, so deletion order is irrelevant.
delete master;
delete child;
}

#endif
1 change: 1 addition & 0 deletions src/tests/latexdocument_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class LatexDocumentTest: public QObject{
LatexEditorView *m_edView;
LatexDocument *m_doc;
private slots:
void getListOfDocsCacheInvalidation();
};

#endif
Expand Down