From 04a13fdefc307180d06e1294c2f244b832e8896d Mon Sep 17 00:00:00 2001 From: Julien Voisin Date: Sat, 5 Feb 2022 22:54:41 +0100 Subject: [PATCH] Improve the xpdf pdf fuzzer (#7241) - Do not fail silently on compilation issues - Use a static version of freetype - Render the PDF on a bitmap, to exercise more code paths. - I'm planning on adding more outputs (maybe in new fuzzers) for Postscript for example - Exercise more metadata gathering functions - Use a stream instead of a file, to speed the fuzzer up - Allocate the PDFDoc on the stack instead of the heap - Don't install recommended packages Co-authored-by: Autofuzz team --- projects/xpdf/Dockerfile | 4 +- projects/xpdf/build.sh | 24 ++++++-- projects/xpdf/fuzz_pdfload.cc | 101 ++++++++++++++++++++++++---------- 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/projects/xpdf/Dockerfile b/projects/xpdf/Dockerfile index 5ce843bf5..d44de6bfc 100755 --- a/projects/xpdf/Dockerfile +++ b/projects/xpdf/Dockerfile @@ -15,7 +15,9 @@ ################################################################################ FROM gcr.io/oss-fuzz-base/base-builder -RUN apt-get update && apt-get install software-properties-common -y && apt-get update && apt-get install -y make wget cmake libpng-dev libfreetype-dev zlib1g-dev +RUN git clone --depth 1 https://gitlab.freedesktop.org/freetype/freetype +RUN apt-get update +RUN apt-get install --no-install-recommends -y make wget cmake qtbase5-dev libcups2-dev autoconf automake autotools-dev libtool RUN wget https://dl.xpdfreader.com/xpdf-latest.tar.gz WORKDIR $SRC diff --git a/projects/xpdf/build.sh b/projects/xpdf/build.sh index 29a01dd07..c36123740 100755 --- a/projects/xpdf/build.sh +++ b/projects/xpdf/build.sh @@ -20,23 +20,37 @@ tar -zxf xpdf-latest.tar.gz dir_name=`tar -tzf xpdf-latest.tar.gz | head -1 | cut -f1 -d"/"` cd $dir_name +PREFIX=$WORK/prefix +mkdir -p $PREFIX + +export PKG_CONFIG="`which pkg-config` --static" +export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig +export PATH=$PREFIX/bin:$PATH +pushd $SRC/freetype +./autogen.sh +./configure --prefix="$PREFIX" --disable-shared PKG_CONFIG_PATH="$PKG_CONFIG_PATH" --with-png=no --with-zlib=no +make -j$(nproc) +make install +popd + # Make minor change in the CMakeFiles file. sed -i 's/#--- object files needed by XpdfWidget/add_library(testXpdfStatic STATIC $)\n#--- object files needed by XpdfWidget/' ./xpdf/CMakeLists.txt +sed -i 's/#--- pdftops/add_library(testXpdfWidgetStatic STATIC $\n $\n $\n ${FREETYPE_LIBRARY}\n ${FREETYPE_OTHER_LIBS})\n#--- pdftops/' ./xpdf/CMakeLists.txt # Build the project mkdir build && cd build export LD=$CXX cmake ../ -DCMAKE_C_FLAGS="$CFLAGS" -DCMAKE_CXX_FLAGS="$CXXFLAGS" \ - -DOPI_SUPPORT=ON -DMULTITHREADED=0 -DCMAKE_DISABLE_FIND_PACKAGE_Qt4=1 \ - -DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=1 -DSPLASH_CMYK=ON -make -i || true + -DOPI_SUPPORT=ON -DSPLASH_CMYK=ON -DMULTITHREADED=ON \ + -DUSE_EXCEPTIONS=ON -DXPDFWIDGET_PRINTING=ON -DFREETYPE_DIR="$PREFIX" +make # Build fuzzers for fuzzer in zxdoc pdfload; do cp ../../fuzz_$fuzzer.cc . $CXX fuzz_$fuzzer.cc -o $OUT/fuzz_$fuzzer $CXXFLAGS $LIB_FUZZING_ENGINE \ - ./xpdf/libtestXpdfStatic.a ./fofi/libfofi.a ./goo/libgoo.a \ - -I../ -I../goo -I../fofi -I. -I../xpdf + ./xpdf/libtestXpdfStatic.a ./fofi/libfofi.a ./goo/libgoo.a ./splash/libsplash.a ./xpdf/libtestXpdfWidgetStatic.a /work/prefix/lib/libfreetype.a \ + -I../ -I../goo -I../fofi -I. -I../xpdf -I../splash done # Copy over options files diff --git a/projects/xpdf/fuzz_pdfload.cc b/projects/xpdf/fuzz_pdfload.cc index 48eb22de0..375e84cb8 100644 --- a/projects/xpdf/fuzz_pdfload.cc +++ b/projects/xpdf/fuzz_pdfload.cc @@ -12,57 +12,92 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include -#include +#include + +#include +#include +#include #include -#include -#include "PDFDoc.h" +#include +#include +#include +#include + +#include "gmem.h" +#include "gmempp.h" +#include "parseargs.h" +#include "GString.h" +#include "gfile.h" #include "GlobalParams.h" -#include "Zoox.h" +#include "Object.h" +#include "PDFDoc.h" +#include "SplashBitmap.h" +#include "Splash.h" +#include "SplashOutputDev.h" +#include "Stream.h" +#include "config.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - char filename[256]; - sprintf(filename, "/tmp/libfuzzer.%d", getpid()); - FILE *fp = fopen(filename, "wb"); - if (!fp) - return 0; - fwrite(data, size, 1, fp); - fclose(fp); + FuzzedDataProvider fdp (data, size); + double hdpi = fdp.ConsumeFloatingPoint(); + double vdpi = fdp.ConsumeFloatingPoint(); + int rotate = fdp.ConsumeIntegral(); + bool useMediaBox = fdp.ConsumeBool(); + bool crop = fdp.ConsumeBool(); + bool printing = fdp.ConsumeBool(); + std::vector payload = fdp.ConsumeRemainingBytes(); + + Object xpdf_obj; + xpdf_obj.initNull(); + BaseStream *stream = new MemStream(payload.data(), 0, payload.size(), &xpdf_obj); - // Main fuzzing logic Object info, xfa; Object *acroForm; globalParams = new GlobalParams(NULL); globalParams->setErrQuiet(1); globalParams->setupBaseFonts(NULL); + char yes[] = "yes"; + globalParams->setEnableFreeType(yes); // Yes, it's a string and not a bool. + globalParams->setErrQuiet(1); PDFDoc *doc = NULL; try { - doc = new PDFDoc(filename, NULL, NULL); - if (doc->isOk() == gTrue) + PDFDoc doc(stream); + if (doc.isOk() == gTrue) { - doc->getNumPages(); - doc->getOutline(); - doc->getStructTreeRoot(); - doc->getXRef(); - doc->readMetadata(); + doc.getNumPages(); + doc.getOutline(); + doc.getStructTreeRoot(); + doc.getXRef(); + doc.okToPrint(gTrue); + doc.okToCopy(gTrue); + doc.okToChange(gTrue); + doc.okToAddNotes(gTrue); + doc.isLinearized(); + doc.getPDFVersion(); + + GString *metadata; + if ((metadata = doc.readMetadata())) { + (void)metadata->getCString(); + } + delete metadata; Object info; - doc->getDocInfo(&info); + doc.getDocInfo(&info); if (info.isDict()) { info.getDict(); } info.free(); - if ((acroForm = doc->getCatalog()->getAcroForm())->isDict()) { + if ((acroForm = doc.getCatalog()->getAcroForm())->isDict()) { acroForm->dictLookup("XFA", &xfa); xfa.free(); } - for (size_t i = 0; i < doc->getNumPages(); i++) { - doc->getLinks(i); - auto page = doc->getCatalog()->getPage(i); + for (size_t i = 0; i < doc.getNumPages(); i++) { + doc.getLinks(i); + auto page = doc.getCatalog()->getPage(i); if (!page->isOk()) { continue; } @@ -70,18 +105,24 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) page->getMetadata(); page->getResourceDict(); } + + SplashColor paperColor = {0xff, 0xff, 0xff}; + SplashOutputDev *splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor); + splashOut->setNoComposite(gTrue); + splashOut->startDoc(doc.getXRef()); + for (size_t i = 0; i <= doc.getNumPages(); ++i) { + doc.displayPage(splashOut, i, hdpi, vdpi, rotate, useMediaBox, crop, printing); + } + (void)splashOut->getBitmap(); + + delete splashOut; } } catch (...) { } - // Cleanup - if (doc != NULL) - delete doc; delete globalParams; - // cleanup temporary file - unlink(filename); return 0; }