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