sw/source/uibase/inc/content.hxx | 17 +++++++++++++++-- sw/source/uibase/utlui/content.cxx | 37 +++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 14 deletions(-)
New commits: commit 0322f4c69d7d615063e0f8f6a1b39b5fbfee81af Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Tue Jun 17 16:52:00 2025 +0200 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Jun 19 17:14:57 2025 +0200 sw: Navigator: fix SwPostItContent::m_pField UAF ASAN reports UAF on SwPostItContent::m_pField and it's apparent that the only thing that removes a SwPostItContent from the Navigator is a timer. Clearly it becomes invalid when the SwFromatField that m_pField points to is destroyed, so implement a listener for this and try to add some null checks on uses to avoid crashing. (Doesn't look obvious how to remove it from the tree immediately.) Change-Id: Iad94ddb923f0126b5e6d76d0b77dc26043cefaba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186654 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> Tested-by: Jenkins diff --git a/sw/source/uibase/inc/content.hxx b/sw/source/uibase/inc/content.hxx index 21ed85f8e0d3..8c790b2ec1c4 100644 --- a/sw/source/uibase/inc/content.hxx +++ b/sw/source/uibase/inc/content.hxx @@ -22,6 +22,7 @@ #include "swcont.hxx" #include <ndarr.hxx> +#include <fmtfld.hxx> #include <tools/long.hxx> #include <utility> @@ -125,7 +126,7 @@ public: const SwTextFootnote* GetTextFootnote() const {return m_pTextFootnote;} }; -class SwPostItContent final : public SwContent +class SwPostItContent final : public SwContent, public SfxListener { const SwFormatField* m_pField; public: @@ -135,9 +136,21 @@ public: tools::Long nYPos ) : SwContent(pCnt, rName, nYPos) , m_pField(pFormatField) - {} + { + assert(m_pField); + StartListening(*const_cast<SwFormatField*>(m_pField)); + } + + virtual void Notify(SfxBroadcaster &, SfxHint const& rHint) override + { + if (rHint.GetId() == SfxHintId::Dying) + { + m_pField = nullptr; + } + } const SwFormatField* GetPostIt() const { return m_pField; } + SwPostItField const* GetPostItField() const; virtual bool IsProtect() const override; }; diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 3fdad2456ca7..8531e8bb4dbb 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -298,6 +298,13 @@ bool SwTextFieldContent::IsProtect() const return m_pFormatField->IsProtect(); } +SwPostItField const* SwPostItContent::GetPostItField() const +{ + return m_pField + ? static_cast<SwPostItField const*>(m_pField->GetField()) + : nullptr; +} + bool SwPostItContent::IsProtect() const { return m_pField->IsProtect(); @@ -2545,15 +2552,13 @@ bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent) OUString sEntry = pCnt->GetName(); OUString sId(weld::toId(pCnt)); - const SwPostItField* pPostItField = - static_cast<const SwPostItField*>(pCnt->GetPostIt()->GetField()); + const SwPostItField* pPostItField = pCnt->GetPostItField(); auto lambda = [&pPostItField, this](const std::unique_ptr<weld::TreeIter>& xEntry) { SwPostItContent* pParentCandidateCnt = weld::fromId<SwPostItContent*>(m_xTreeView->get_id(*xEntry)); return pPostItField->GetParentPostItId() == - static_cast<const SwPostItField*>(pParentCandidateCnt->GetPostIt() - ->GetField())->GetPostItId(); + pParentCandidateCnt->GetPostItField()->GetPostItId(); }; // if a parent candidate is not found use the passed root node @@ -4522,7 +4527,7 @@ static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeVi { assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData); - p = pCnt->GetPostIt()->GetField(); + p = pCnt->GetPostItField(); break; } default: @@ -5084,7 +5089,7 @@ static bool lcl_IsSelectedCompareByContentTypeAndAddress(const weld::TreeIter& r { assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pContent))); SwPostItContent* pCnt = static_cast<SwPostItContent*>(pContent); - p = pCnt->GetPostIt()->GetField(); + p = pCnt->GetPostItField(); break; } case ContentTypeId::INDEX: @@ -6708,7 +6713,10 @@ void SwContentTree::DeleteAllContentOfEntryContentType(const weld::TreeIter& rEn { const SwPostItContent* pPostItContent = static_cast<const SwPostItContent*>(pContentType->GetMember(i)); - m_pActiveShell->GotoFormatField(*pPostItContent->GetPostIt()); + if (pPostItContent->GetPostIt()) + { + m_pActiveShell->GotoFormatField(*pPostItContent->GetPostIt()); + } m_pActiveShell->DelRight(); } m_pActiveShell->EndUndo(); @@ -6930,7 +6938,10 @@ void SwContentTree::GotoContent(const SwContent* pCnt) } break; case ContentTypeId::POSTIT: - m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt()); + if (SwFormatField const*const pField{static_cast<const SwPostItContent*>(pCnt)->GetPostIt()}) + { + m_pActiveShell->GotoFormatField(*pField); + } break; case ContentTypeId::DRAWOBJECT: { @@ -7186,11 +7197,13 @@ void SwContentTree::BringEntryToAttention(const weld::TreeIter& rEntry) } else if (nType == ContentTypeId::POSTIT) { - if (const SwTextAttr* pTextAttr = - static_cast<SwPostItContent*>(pCnt)->GetPostIt()->GetTextField()) + if (SwFormatField const*const pField{static_cast<SwPostItContent*>(pCnt)->GetPostIt()}) { - std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; - BringPostItFieldsToAttention(aTextAttrArr); + if (const SwTextAttr* pTextAttr = pField->GetTextField()) + { + std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; + BringPostItFieldsToAttention(aTextAttrArr); + } } } else if (nType == ContentTypeId::DRAWOBJECT)