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 <security-tps@google.com>
This commit is contained in:
Julien Voisin 2022-02-05 22:54:41 +01:00 committed by GitHub
parent 35d3c728c3
commit 04a13fdefc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 36 deletions

View File

@ -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

View File

@ -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 $<TARGET_OBJECTS:xpdf_objs>)\n#--- object files needed by XpdfWidget/' ./xpdf/CMakeLists.txt
sed -i 's/#--- pdftops/add_library(testXpdfWidgetStatic STATIC $<TARGET_OBJECTS:xpdf_widget_objs>\n $<TARGET_OBJECTS:splash_objs>\n $<TARGET_OBJECTS:xpdf_objs>\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

View File

@ -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 <stdlib.h>
#include <string.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <vector>
#include <aconf.h>
#include <stdio.h>
#include <stdint.h>
#include <exception>
#include "PDFDoc.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <png.h>
#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>();
double vdpi = fdp.ConsumeFloatingPoint<double>();
int rotate = fdp.ConsumeIntegral<int>();
bool useMediaBox = fdp.ConsumeBool();
bool crop = fdp.ConsumeBool();
bool printing = fdp.ConsumeBool();
std::vector<char> payload = fdp.ConsumeRemainingBytes<char>();
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;
}