Repository.mk                     |    1 
 bin/oss-fuzz-setup.sh             |    1 
 sw/inc/shellio.hxx                |    2 
 sw/source/filter/rtf/swparrtf.cxx |  114 ++++++++++++++++++++++++++++++++++++++
 sw/source/filter/xml/xmlimp.cxx   |   14 ++--
 vcl/Executable_rtf2pdffuzzer.mk   |   50 ++++++++++++++++
 vcl/Module_vcl.mk                 |    1 
 vcl/workben/fftester.cxx          |   10 +++
 vcl/workben/rtf2pdffuzzer.cxx     |   62 ++++++++++++++++++++
 vcl/workben/rtf2pdffuzzer.options |    3 +
 10 files changed, 250 insertions(+), 8 deletions(-)

New commits:
commit 7198c5e49eff0b82cb423424eaed0ee94d66db7d
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Mon Sep 16 16:58:00 2024 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Wed Sep 25 22:28:32 2024 +0200

    add a rtf to pdf fuzzer
    
    Change-Id: Ib805b2e8245903f63096cc21f511ba7ae0a4f488
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173466
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/Repository.mk b/Repository.mk
index b4f3b36796bd..336452487890 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -140,6 +140,7 @@ $(eval $(call 
gb_Helper_register_executables_for_install,OOO,brand, \
        $(call gb_Helper_optional,FUZZERS,olefuzzer) \
        $(call gb_Helper_optional,FUZZERS,pptfuzzer) \
        $(call gb_Helper_optional,FUZZERS,rtffuzzer) \
+       $(call gb_Helper_optional,FUZZERS,rtf2pdffuzzer) \
        $(call gb_Helper_optional,FUZZERS,cgmfuzzer) \
        $(call gb_Helper_optional,FUZZERS,ww2fuzzer) \
        $(call gb_Helper_optional,FUZZERS,ww6fuzzer) \
diff --git a/bin/oss-fuzz-setup.sh b/bin/oss-fuzz-setup.sh
index 5bfc0ce887eb..565f972948e2 100755
--- a/bin/oss-fuzz-setup.sh
+++ b/bin/oss-fuzz-setup.sh
@@ -166,6 +166,7 @@ curl --no-progress-meter -S \
     -C - -O https://dev-www.libreoffice.org/corpus/htmlfuzzer_seed_corpus.zip \
     -C - -O https://dev-www.libreoffice.org/corpus/zipfuzzer_seed_corpus.zip
 cp fodtfuzzer_seed_corpus.zip fodt2pdffuzzer_seed_corpus.zip
+cp rtffuzzer_seed_corpus.zip rtf2pdffuzzer_seed_corpus.zip
 cp fodsfuzzer_seed_corpus.zip fods2xlsfuzzer_seed_corpus.zip
 cp htmlfuzzer_seed_corpus.zip schtmlfuzzer_seed_corpus.zip
 
diff --git a/sw/inc/shellio.hxx b/sw/inc/shellio.hxx
index a9245c8f3bba..48405803870e 100644
--- a/sw/inc/shellio.hxx
+++ b/sw/inc/shellio.hxx
@@ -204,6 +204,7 @@ namespace o3tl {
 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDOC(SvStream &rStream, const 
OUString &rFltName);
 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDOCX(SvStream &rStream);
 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportRTF(SvStream &rStream);
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestPDFExportRTF(SvStream &rStream);
 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportHTML(SvStream &rStream);
 SAL_DLLPUBLIC_EXPORT void FlushFontCache();
 
@@ -211,7 +212,6 @@ class SW_DLLPUBLIC Reader
 {
     friend class SwReader;
     friend bool TestImportDOC(SvStream &rStream, const OUString &rFltName);
-    friend bool TestImportRTF(SvStream &rStream);
     friend bool TestImportHTML(SvStream &rStream);
     rtl::Reference<SwDoc> mxTemplate;
     OUString m_aTemplateName;
diff --git a/sw/source/filter/rtf/swparrtf.cxx 
b/sw/source/filter/rtf/swparrtf.cxx
index f81c40eaee31..b126eb6b23ef 100644
--- a/sw/source/filter/rtf/swparrtf.cxx
+++ b/sw/source/filter/rtf/swparrtf.cxx
@@ -28,15 +28,22 @@
 
 #include <unotextrange.hxx>
 
+#include <unotools/fcm.hxx>
 #include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/diagnose_ex.hxx>
 
 #include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
 #include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
 #include <com/sun/star/frame/XModel.hpp>
 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
 
 using namespace ::com::sun::star;
 
@@ -209,4 +216,111 @@ extern "C" SAL_DLLPUBLIC_EXPORT bool 
TestImportRTF(SvStream& rStream)
     return bRet;
 }
 
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestPDFExportRTF(SvStream& rStream)
+{
+#if 0
+    //TODO: probably end up needing one of these too
+    // do the same sort of check as FilterDetect::detect
+    OString const str(read_uInt8s_ToOString(rStream, 4000));
+    rStream.Seek(STREAM_SEEK_TO_BEGIN);
+    OUString resultString(str.getStr(), str.getLength(), 
RTL_TEXTENCODING_ASCII_US,
+                          RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT
+                              | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
+                              | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT);
+    if (!resultString.startsWith("<?xml")
+        || 
resultString.indexOf("office:mimetype=\"application/vnd.oasis.opendocument.text\"")
+               == -1)
+        return false;
+#endif
+
+    uno::Reference<css::frame::XDesktop2> xDesktop
+        = 
css::frame::Desktop::create(comphelper::getProcessComponentContext());
+    uno::Reference<css::frame::XFrame> xTargetFrame = 
xDesktop->findFrame(u"_blank"_ustr, 0);
+
+    uno::Reference<uno::XComponentContext> 
xContext(comphelper::getProcessComponentContext());
+    uno::Reference<css::frame::XModel2> xModel(
+        xContext->getServiceManager()->createInstanceWithContext(
+            u"com.sun.star.text.TextDocument"_ustr, xContext),
+        uno::UNO_QUERY_THROW);
+
+    uno::Reference<css::frame::XLoadable> xModelLoad(xModel, 
uno::UNO_QUERY_THROW);
+    xModelLoad->initNew();
+
+    uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+        comphelper::getProcessServiceFactory());
+    uno::Reference<io::XInputStream> xStream(new 
utl::OSeekableInputStreamWrapper(rStream));
+
+    uno::Reference<uno::XInterface> xInterface(
+        
xMultiServiceFactory->createInstance(u"com.sun.star.comp.Writer.RtfFilter"_ustr),
+        uno::UNO_SET_THROW);
+
+    uno::Reference<document::XImporter> xImporter(xInterface, 
uno::UNO_QUERY_THROW);
+    xImporter->setTargetDocument(xModel);
+
+    uno::Reference<document::XFilter> xFilter(xInterface, 
uno::UNO_QUERY_THROW);
+    uno::Sequence<beans::PropertyValue> aArgs(
+        comphelper::InitPropertySequence({ { "InputStream", uno::Any(xStream) 
} }));
+
+    bool ret = true;
+    try
+    {
+        ret = xFilter->filter(aArgs);
+    }
+    catch (...)
+    {
+        ret = false;
+    }
+
+    if (ret)
+    {
+        uno::Reference<text::XTextDocument> xTextDocument(xModel, 
uno::UNO_QUERY);
+        uno::Reference<text::XText> xText(xTextDocument->getText());
+        uno::Reference<container::XEnumerationAccess> xParaAccess(xText, 
uno::UNO_QUERY);
+        uno::Reference<container::XEnumeration> 
xParaEnum(xParaAccess->createEnumeration());
+        while (xParaEnum->hasMoreElements())
+        {
+            uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), 
uno::UNO_QUERY);
+            // discourage very long paragraphs for fuzzing performance
+            if (xPara && xPara->getString().getLength() > 15000)
+            {
+                ret = false;
+                break;
+            }
+        }
+    }
+
+    if (ret)
+    {
+        css::uno::Reference<css::frame::XController2> xController(
+            xModel->createDefaultViewController(xTargetFrame), 
uno::UNO_SET_THROW);
+        utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel);
+
+        utl::TempFileFast aTempFile;
+
+        uno::Reference<document::XFilter> xPDFFilter(
+            
xMultiServiceFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr),
+            uno::UNO_QUERY);
+        uno::Reference<document::XExporter> xExporter(xPDFFilter, 
uno::UNO_QUERY);
+        xExporter->setSourceDocument(xModel);
+
+        uno::Reference<io::XOutputStream> xOutputStream(
+            new 
utl::OStreamWrapper(*aTempFile.GetStream(StreamMode::READWRITE)));
+
+        // ofz#60533 fuzzer learned to use fo:font-size="842pt" which generate 
timeouts trying
+        // to export thousands of pages from minimal input size
+        uno::Sequence<beans::PropertyValue> aFilterData(
+            comphelper::InitPropertySequence({ { "PageRange", 
uno::Any(u"1-100"_ustr) } }));
+        uno::Sequence<beans::PropertyValue> 
aDescriptor(comphelper::InitPropertySequence(
+            { { "FilterName", uno::Any(u"writer_pdf_Export"_ustr) },
+              { "OutputStream", uno::Any(xOutputStream) },
+              { "FilterData", uno::Any(aFilterData) } }));
+        xPDFFilter->filter(aDescriptor);
+    }
+
+    css::uno::Reference<css::util::XCloseable> xClose(xModel, 
css::uno::UNO_QUERY);
+    xClose->close(false);
+
+    return ret;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx
index 86d902c84704..a26a9fc4b863 100644
--- a/sw/source/filter/xml/xmlimp.cxx
+++ b/sw/source/filter/xml/xmlimp.cxx
@@ -1833,14 +1833,14 @@ extern "C" SAL_DLLPUBLIC_EXPORT bool 
TestPDFExportFODT(SvStream &rStream)
     if (!resultString.startsWith("<?xml") || 
resultString.indexOf("office:mimetype=\"application/vnd.oasis.opendocument.text\"")
 == -1)
         return false;
 
-    Reference<css::frame::XDesktop2> xDesktop = 
css::frame::Desktop::create(comphelper::getProcessComponentContext());
-    Reference<css::frame::XFrame> xTargetFrame = 
xDesktop->findFrame(u"_blank"_ustr, 0);
+    uno::Reference<css::frame::XDesktop2> xDesktop = 
css::frame::Desktop::create(comphelper::getProcessComponentContext());
+    uno::Reference<css::frame::XFrame> xTargetFrame = 
xDesktop->findFrame(u"_blank"_ustr, 0);
 
-    Reference<uno::XComponentContext> 
xContext(comphelper::getProcessComponentContext());
-    Reference<css::frame::XModel2> 
xModel(xContext->getServiceManager()->createInstanceWithContext(
-                u"com.sun.star.text.TextDocument"_ustr, xContext), 
UNO_QUERY_THROW);
+    uno::Reference<uno::XComponentContext> 
xContext(comphelper::getProcessComponentContext());
+    uno::Reference<css::frame::XModel2> 
xModel(xContext->getServiceManager()->createInstanceWithContext(
+                u"com.sun.star.text.TextDocument"_ustr, xContext), 
uno::UNO_QUERY_THROW);
 
-    Reference<css::frame::XLoadable> xModelLoad(xModel, UNO_QUERY_THROW);
+    uno::Reference<css::frame::XLoadable> xModelLoad(xModel, 
uno::UNO_QUERY_THROW);
     xModelLoad->initNew();
 
     uno::Reference<lang::XMultiServiceFactory> 
xMultiServiceFactory(comphelper::getProcessServiceFactory());
@@ -1909,7 +1909,7 @@ extern "C" SAL_DLLPUBLIC_EXPORT bool 
TestPDFExportFODT(SvStream &rStream)
 
     if (ret)
     {
-        css::uno::Reference<css::frame::XController2> 
xController(xModel->createDefaultViewController(xTargetFrame), UNO_SET_THROW);
+        css::uno::Reference<css::frame::XController2> 
xController(xModel->createDefaultViewController(xTargetFrame), 
uno::UNO_SET_THROW);
         utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel);
 
         utl::TempFileFast aTempFile;
diff --git a/vcl/Executable_rtf2pdffuzzer.mk b/vcl/Executable_rtf2pdffuzzer.mk
new file mode 100644
index 000000000000..78efef1748f4
--- /dev/null
+++ b/vcl/Executable_rtf2pdffuzzer.mk
@@ -0,0 +1,50 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+include $(SRCDIR)/vcl/commonfuzzer.mk
+
+$(eval $(call gb_Executable_Executable,rtf2pdffuzzer))
+
+$(eval $(call gb_Executable_use_api,rtf2pdffuzzer,\
+    offapi \
+    udkapi \
+))
+
+$(eval $(call gb_Executable_use_externals,rtf2pdffuzzer,\
+    $(fuzzer_externals) \
+    epubgen \
+    revenge \
+))
+
+$(eval $(call gb_Executable_set_include,rtf2pdffuzzer,\
+    $$(INCLUDE) \
+    -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call gb_Executable_use_libraries,rtf2pdffuzzer,\
+    $(fuzzer_writer_libraries) \
+    $(fuzzer_core_libraries) \
+    pdffilter \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,rtf2pdffuzzer,\
+    $(fuzzer_statics) \
+    fuzzer_writer \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,rtf2pdffuzzer,\
+    vcl/workben/rtf2pdffuzzer \
+))
+
+$(eval $(call gb_Executable_add_libs,rtf2pdffuzzer,\
+    $(LIB_FUZZING_ENGINE) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index c0a624acdc95..a79262b1c757 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -183,6 +183,7 @@ $(eval $(call gb_Module_add_targets,vcl,\
     Executable_olefuzzer \
     Executable_pptfuzzer \
     Executable_rtffuzzer \
+    Executable_rtf2pdffuzzer \
     Executable_cgmfuzzer \
     Executable_ww2fuzzer \
     Executable_ww6fuzzer \
diff --git a/vcl/workben/fftester.cxx b/vcl/workben/fftester.cxx
index e086e2e22e79..75d511b1278f 100644
--- a/vcl/workben/fftester.cxx
+++ b/vcl/workben/fftester.cxx
@@ -332,6 +332,16 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
             SvFileStream aFileStream(out, StreamMode::READ);
             ret = static_cast<int>((*pfnImport)(aFileStream));
         }
+        else if (strcmp(argv[2], "rtf2pdf") == 0)
+        {
+            static FFilterCall pfnImport(nullptr);
+            if (!pfnImport)
+            {
+                pfnImport = load(u"libmswordlo.so", "TestPDFExportRTF");
+            }
+            SvFileStream aFileStream(out, StreamMode::READ);
+            ret = static_cast<int>((*pfnImport)(aFileStream));
+        }
         else if (strcmp(argv[2], "fods2xls") == 0)
         {
             static FFilterCall pfnImport(nullptr);
diff --git a/vcl/workben/rtf2pdffuzzer.cxx b/vcl/workben/rtf2pdffuzzer.cxx
new file mode 100644
index 000000000000..9a5a6c5361c8
--- /dev/null
+++ b/vcl/workben/rtf2pdffuzzer.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tools/stream.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/awt/XToolkit.hpp>
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/ucb/XUniversalContentBroker.hpp>
+#include <libxml/parser.h>
+#include "commonfuzzer.hxx"
+
+extern "C" bool TestPDFExportRTF(SvStream& rStream);
+
+static void silent_error_func(void*, const char* /*format*/, ...) {}
+
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
+{
+    if (__lsan_disable)
+        __lsan_disable();
+
+    CommonInitialize(argc, argv);
+
+    // initialise unconfigured UCB:
+    css::uno::Reference<css::ucb::XUniversalContentBroker> xUcb(
+        comphelper::getProcessServiceFactory()->createInstance(
+            "com.sun.star.ucb.UniversalContentBroker"),
+        css::uno::UNO_QUERY_THROW);
+    css::uno::Sequence<css::uno::Any> aArgs{ 
css::uno::Any(OUString("NoConfig")) };
+    css::uno::Reference<css::ucb::XContentProvider> xFileProvider(
+        comphelper::getProcessServiceFactory()->createInstanceWithArguments(
+            "com.sun.star.ucb.FileContentProvider", aArgs),
+        css::uno::UNO_QUERY_THROW);
+    xUcb->registerContentProvider(xFileProvider, "file", true);
+
+    // create and hold a reference to XToolkit here to avoid the lsan warning 
about its leak
+    // due to getting created in the unusual case of no vcl main loop
+    static css::uno::Reference<css::awt::XToolkit> xTk(
+        
comphelper::getProcessServiceFactory()->createInstance("com.sun.star.awt.Toolkit"),
+        css::uno::UNO_QUERY_THROW);
+
+    if (__lsan_enable)
+        __lsan_enable();
+
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{
+    SvMemoryStream aStream(const_cast<uint8_t*>(data), size, StreamMode::READ);
+    bool bRTFLoaded = TestPDFExportRTF(aStream);
+    // if the rtf didn't load then reject so that input will not be added to 
the corpus
+    // we're not interested in input that doesn't go on to exercise the pdf 
export
+    return bRTFLoaded ? 0 : -1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/workben/rtf2pdffuzzer.options 
b/vcl/workben/rtf2pdffuzzer.options
new file mode 100644
index 000000000000..0a8e8c36b319
--- /dev/null
+++ b/vcl/workben/rtf2pdffuzzer.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 512
+dict = rtf.dict

Reply via email to