sw/source/uibase/inc/content.hxx   |   17 +++++++++++++++--
 sw/source/uibase/utlui/content.cxx |   37 +++++++++++++++++++++++++------------
 2 files changed, 40 insertions(+), 14 deletions(-)

New commits:
commit f55ab60cff8b15aaf220fc3f75b0a35fe0b2c1ef
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Tue Jun 17 16:52:00 2025 +0200
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Sat Jun 28 22:11:36 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
    (cherry picked from commit 0322f4c69d7d615063e0f8f6a1b39b5fbfee81af)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186741
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

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)

Reply via email to