include/sfx2/objsh.hxx       |    9 +++++++++
 sfx2/source/doc/objcont.cxx  |    1 +
 sfx2/source/doc/objxtor.cxx  |   38 +++++++++++++++++++++++++++++++++++---
 sfx2/source/inc/objshimp.hxx |    3 +++
 4 files changed, 48 insertions(+), 3 deletions(-)

New commits:
commit 99c1bd1a4ef5365d8c26a41c8e858c67e673beb4
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sun Mar 10 20:44:46 2024 +0600
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Mar 11 04:43:46 2024 +0100

    Disallow closing document during generation of preview
    
    Using an external (Java) script that opens a document, and then quickly
    closes it, it may happen that the idle that generates a preview is still
    active, when the control thread calls XCloseable::close, which destroys
    still used document. This leads to use-after-free.
    
    This uses the close listener in SfxObjectShell, to veto closing document
    that is still in use by the preview generation code. After completion,
    when the caller of close() transferred ownership, then the call to close
    is repeated.
    
    Change-Id: I39691f61ad5141d7e8ee54723d5aef87f0bc01dc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164632
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index c961394db08c..075b3e57f4b9 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -836,6 +836,15 @@ public:
         }
     };
 
+class SfxCloseVetoLock
+{
+public:
+    SfxCloseVetoLock(const SfxObjectShell& rDocShell);
+    ~SfxCloseVetoLock();
+
+private:
+    const SfxObjectShell& m_rDocShell;
+};
 
 typedef rtl::Reference<SfxObjectShell> SfxObjectShellRef;
 
diff --git a/sfx2/source/doc/objcont.cxx b/sfx2/source/doc/objcont.cxx
index b762e9d11084..acaeb1047339 100644
--- a/sfx2/source/doc/objcont.cxx
+++ b/sfx2/source/doc/objcont.cxx
@@ -105,6 +105,7 @@ SfxObjectShell::GetPreviewMetaFile( bool bFullContent ) 
const
 
 BitmapEx SfxObjectShell::GetPreviewBitmap() const
 {
+    SfxCloseVetoLock lock(*this);
     ScopedVclPtrInstance< VirtualDevice > pDevice;
     pDevice->SetAntialiasing(AntialiasingFlags::Enable | 
pDevice->GetAntialiasing());
     if(!CreatePreview_Impl(/*bFullContent*/false, pDevice, nullptr))
diff --git a/sfx2/source/doc/objxtor.cxx b/sfx2/source/doc/objxtor.cxx
index 88c13826e6b1..8a34dc40812f 100644
--- a/sfx2/source/doc/objxtor.cxx
+++ b/sfx2/source/doc/objxtor.cxx
@@ -28,6 +28,7 @@
 #include <com/sun/star/util/XCloseable.hpp>
 #include <com/sun/star/frame/XComponentLoader.hpp>
 #include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
 #include <com/sun/star/util/XCloseListener.hpp>
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/frame/XTitle.hpp>
@@ -137,8 +138,14 @@ public:
 
 } // namespace
 
-void SAL_CALL SfxModelListener_Impl::queryClosing( const 
css::lang::EventObject& , sal_Bool )
+void SAL_CALL SfxModelListener_Impl::queryClosing( const 
css::lang::EventObject& , sal_Bool bDeliverOwnership)
 {
+    if (mpDoc->Get_Impl()->m_nClosingLockLevel)
+    {
+        if (bDeliverOwnership)
+            mpDoc->Get_Impl()->m_bCloseModelScheduled = true;
+        throw util::CloseVetoException(u"Closing document is blocked"_ustr, 
getXWeak());
+    }
 }
 
 void SAL_CALL SfxModelListener_Impl::notifyClosing( const 
css::lang::EventObject& )
@@ -305,8 +312,6 @@ SfxObjectShell::~SfxObjectShell()
     if ( pSfxApp && pSfxApp->GetDdeService() )
         pSfxApp->RemoveDdeTopic( this );
 
-    pImpl->pBaseModel.clear();
-
     // don't call GetStorage() here, in case of Load Failure it's possible 
that a storage was never assigned!
     if ( pMedium && pMedium->HasStorage_Impl() && pMedium->GetStorage( false ) 
== pImpl->m_xDocStorage )
         pMedium->CanDisposeStorage_Impl( false );
@@ -341,6 +346,33 @@ SfxObjectShell::~SfxObjectShell()
     }
 }
 
+SfxCloseVetoLock::SfxCloseVetoLock(const SfxObjectShell& rDocShell)
+    : m_rDocShell(rDocShell)
+{
+    osl_atomic_increment(&m_rDocShell.Get_Impl()->m_nClosingLockLevel);
+}
+
+SfxCloseVetoLock::~SfxCloseVetoLock()
+{
+    if (osl_atomic_decrement(&m_rDocShell.Get_Impl()->m_nClosingLockLevel) == 
0)
+    {
+        if (m_rDocShell.Get_Impl()->m_bCloseModelScheduled)
+        {
+            m_rDocShell.Get_Impl()->m_bCloseModelScheduled = false; // pass 
ownership
+            if (rtl::Reference model = 
static_cast<SfxBaseModel*>(m_rDocShell.GetBaseModel().get()))
+            {
+                try
+                {
+                    model->close(true);
+                }
+                catch (const util::CloseVetoException&)
+                {
+                    DBG_UNHANDLED_EXCEPTION("sfx.doc");
+                }
+            }
+        }
+    }
+}
 
 void SfxObjectShell::Stamp_SetPrintCancelState(bool bState)
 {
diff --git a/sfx2/source/inc/objshimp.hxx b/sfx2/source/inc/objshimp.hxx
index acd85eb0d1a9..87499380e997 100644
--- a/sfx2/source/inc/objshimp.hxx
+++ b/sfx2/source/inc/objshimp.hxx
@@ -139,6 +139,9 @@ struct SfxObjectShell_Impl final : public 
::sfx2::IMacroDocumentAccess
     // Recent colors used by toolbar buttons
     std::unordered_map<sal_uInt16, NamedColor> m_aRecentColors;
 
+    mutable oslInterlockedCount m_nClosingLockLevel = 0;
+    mutable bool m_bCloseModelScheduled = false;
+
     SfxObjectShell_Impl( SfxObjectShell& _rDocShell );
     virtual ~SfxObjectShell_Impl();
 

Reply via email to