From 95860d77fb882317da74ec614ff841fcacdcaa83 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Tue, 9 Oct 2018 22:10:29 +0000 Subject: Copy some fuzzer code from Chromium into PDFium. These came from Chromium's pdf/pdfium/fuzzer directory. The code has been modified to be buildable in standalone PDFium and modernized. Change-Id: Ia4ddad4a164f798689af4b9f92d41d635901dccb Reviewed-on: https://pdfium-review.googlesource.com/c/43530 Reviewed-by: Tom Sepez Commit-Queue: Lei Zhang --- testing/fuzzers/BUILD.gn | 37 +++++ testing/fuzzers/pdfium_fuzzer.cc | 21 +++ testing/fuzzers/pdfium_fuzzer_helper.cc | 264 ++++++++++++++++++++++++++++++++ testing/fuzzers/pdfium_fuzzer_helper.h | 25 +++ testing/fuzzers/pdfium_xfa_fuzzer.cc | 31 ++++ 5 files changed, 378 insertions(+) create mode 100644 testing/fuzzers/pdfium_fuzzer.cc create mode 100644 testing/fuzzers/pdfium_fuzzer_helper.cc create mode 100644 testing/fuzzers/pdfium_fuzzer_helper.h create mode 100644 testing/fuzzers/pdfium_xfa_fuzzer.cc diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn index 59211b1758..5a6af69f69 100644 --- a/testing/fuzzers/BUILD.gn +++ b/testing/fuzzers/BUILD.gn @@ -35,6 +35,7 @@ group("fuzzers") { ":pdf_psengine_fuzzer_src", ":pdf_streamparser_fuzzer_src", ":pdf_xml_fuzzer_src", + ":pdfium_fuzzer_src", ] if (pdf_enable_xfa) { deps += [ @@ -44,6 +45,7 @@ group("fuzzers") { ":pdf_css_fuzzer_src", ":pdf_fm2js_fuzzer_src", ":pdf_formcalc_fuzzer_src", + ":pdfium_xfa_fuzzer_src", ] if (pdf_enable_xfa_bmp) { deps += [ ":pdf_codec_bmp_fuzzer_src" ] @@ -78,6 +80,9 @@ template("pdfium_fuzzer") { "//build/config/compiler:no_chromium_code", ":fuzzer_config", ] + if (pdf_enable_v8) { + configs += [ "//v8:external_startup_data" ] + } } } @@ -175,6 +180,19 @@ if (pdf_enable_xfa) { "pdf_formcalc_fuzzer.cc", ] } + + pdfium_fuzzer("pdfium_xfa_fuzzer_src") { + sources = [ + "pdfium_fuzzer_helper.cc", + "pdfium_fuzzer_helper.h", + "pdfium_xfa_fuzzer.cc", + ] + deps = [ + "../../:test_support", + "//v8", + "//v8:v8_libplatform", + ] + } } pdfium_fuzzer("pdf_cmap_fuzzer_src") { @@ -251,3 +269,22 @@ pdfium_fuzzer("pdf_xml_fuzzer_src") { "pdf_xml_fuzzer.cc", ] } + +pdfium_fuzzer("pdfium_fuzzer_src") { + sources = [ + "pdfium_fuzzer.cc", + "pdfium_fuzzer_helper.cc", + "pdfium_fuzzer_helper.h", + ] + + deps = [ + "../../:test_support", + ] + + if (pdf_enable_v8) { + deps += [ + "//v8", + "//v8:v8_libplatform", + ] + } +} diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc new file mode 100644 index 0000000000..dc15378ac5 --- /dev/null +++ b/testing/fuzzers/pdfium_fuzzer.cc @@ -0,0 +1,21 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "testing/fuzzers/pdfium_fuzzer_helper.h" + +class PDFiumFuzzer : public PDFiumFuzzerHelper { + public: + PDFiumFuzzer() = default; + ~PDFiumFuzzer() override = default; + + int GetFormCallbackVersion() const override { return 1; } +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + PDFiumFuzzer fuzzer; + fuzzer.RenderPdf(reinterpret_cast(data), size); + return 0; +} diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc new file mode 100644 index 0000000000..ed000d251d --- /dev/null +++ b/testing/fuzzers/pdfium_fuzzer_helper.cc @@ -0,0 +1,264 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/fuzzers/pdfium_fuzzer_helper.h" + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#elif defined(__APPLE__) +#include +#else // Linux +#include +#endif // _WIN32 + +#include +#include +#include +#include + +#include "public/cpp/fpdf_scopers.h" +#include "public/fpdf_dataavail.h" +#include "public/fpdf_ext.h" +#include "public/fpdf_text.h" +#include "testing/test_support.h" +#include "v8/include/v8-platform.h" +#include "v8/include/v8.h" + +namespace { + +int ExampleAppAlert(IPDF_JSPLATFORM*, + FPDF_WIDESTRING, + FPDF_WIDESTRING, + int, + int) { + return 0; +} + +int ExampleAppResponse(IPDF_JSPLATFORM*, + FPDF_WIDESTRING question, + FPDF_WIDESTRING title, + FPDF_WIDESTRING default_value, + FPDF_WIDESTRING label, + FPDF_BOOL is_password, + void* response, + int length) { + // UTF-16, always LE regardless of platform. + uint8_t* ptr = static_cast(response); + ptr[0] = 'N'; + ptr[1] = 0; + ptr[2] = 'o'; + ptr[3] = 0; + return 4; +} + +void ExampleDocGotoPage(IPDF_JSPLATFORM*, int pageNumber) {} + +void ExampleDocMail(IPDF_JSPLATFORM*, + void* mailData, + int length, + FPDF_BOOL UI, + FPDF_WIDESTRING To, + FPDF_WIDESTRING Subject, + FPDF_WIDESTRING CC, + FPDF_WIDESTRING BCC, + FPDF_WIDESTRING Msg) {} + +void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {} + +FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) { + return true; +} + +void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {} + +#ifdef PDF_ENABLE_V8 +std::string ProgramPath() { + std::string result; + +#ifdef _WIN32 + char path[MAX_PATH]; + DWORD len = GetModuleFileNameA(NULL, path, MAX_PATH); + if (len != 0) + result = std::string(path, len); +#elif defined(__APPLE__) + char path[PATH_MAX]; + unsigned int len = PATH_MAX; + if (!_NSGetExecutablePath(path, &len)) { + std::unique_ptr resolved_path( + realpath(path, nullptr)); + if (resolved_path.get()) + result = std::string(resolved_path.get()); + } +#else // Linux + char path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", path, PATH_MAX); + if (len > 0) + result = std::string(path, len); +#endif + return result; +} +#endif // PDF_ENABLE_V8 + +} // namespace + +PDFiumFuzzerHelper::PDFiumFuzzerHelper() = default; + +PDFiumFuzzerHelper::~PDFiumFuzzerHelper() = default; + +bool PDFiumFuzzerHelper::OnFormFillEnvLoaded(FPDF_DOCUMENT doc) { + return true; +} + +void PDFiumFuzzerHelper::RenderPdf(const char* pBuf, size_t len) { + IPDF_JSPLATFORM platform_callbacks; + memset(&platform_callbacks, '\0', sizeof(platform_callbacks)); + platform_callbacks.version = 3; + platform_callbacks.app_alert = ExampleAppAlert; + platform_callbacks.app_response = ExampleAppResponse; + platform_callbacks.Doc_gotoPage = ExampleDocGotoPage; + platform_callbacks.Doc_mail = ExampleDocMail; + + FPDF_FORMFILLINFO form_callbacks; + memset(&form_callbacks, '\0', sizeof(form_callbacks)); + form_callbacks.version = GetFormCallbackVersion(); + form_callbacks.m_pJsPlatform = &platform_callbacks; + + TestLoader loader(pBuf, len); + FPDF_FILEACCESS file_access; + memset(&file_access, '\0', sizeof(file_access)); + file_access.m_FileLen = static_cast(len); + file_access.m_GetBlock = TestLoader::GetBlock; + file_access.m_Param = &loader; + + FX_FILEAVAIL file_avail; + memset(&file_avail, '\0', sizeof(file_avail)); + file_avail.version = 1; + file_avail.IsDataAvail = Is_Data_Avail; + + FX_DOWNLOADHINTS hints; + memset(&hints, '\0', sizeof(hints)); + hints.version = 1; + hints.AddSegment = Add_Segment; + + ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access)); + + int nRet = PDF_DATA_NOTAVAIL; + bool bIsLinearized = false; + ScopedFPDFDocument doc; + if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) { + doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr)); + if (doc) { + while (nRet == PDF_DATA_NOTAVAIL) + nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints); + + if (nRet == PDF_DATA_ERROR) + return; + + nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints); + if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) + return; + + bIsLinearized = true; + } + } else { + doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr)); + } + + if (!doc) + return; + + (void)FPDF_GetDocPermissions(doc.get()); + + ScopedFPDFFormHandle form( + FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks)); + if (!OnFormFillEnvLoaded(doc.get())) + return; + + FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD); + FPDF_SetFormFieldHighlightAlpha(form.get(), 100); + FORM_DoDocumentJSAction(form.get()); + FORM_DoDocumentOpenAction(form.get()); + + int page_count = FPDF_GetPageCount(doc.get()); + for (int i = 0; i < page_count; ++i) { + if (bIsLinearized) { + nRet = PDF_DATA_NOTAVAIL; + while (nRet == PDF_DATA_NOTAVAIL) + nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints); + + if (nRet == PDF_DATA_ERROR) + return; + } + RenderPage(doc.get(), form.get(), i); + } + FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC); +} + +bool PDFiumFuzzerHelper::RenderPage(FPDF_DOCUMENT doc, + FPDF_FORMHANDLE form, + const int page_index) { + ScopedFPDFPage page(FPDF_LoadPage(doc, page_index)); + if (!page) + return false; + + ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get())); + FORM_OnAfterLoadPage(page.get(), form); + FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN); + + const double scale = 1.0; + int width = static_cast(FPDF_GetPageWidth(page.get()) * scale); + int height = static_cast(FPDF_GetPageHeight(page.get()) * scale); + ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, 0)); + if (bitmap) { + FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, 0xFFFFFFFF); + FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0, 0); + FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0, 0); + } + FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE); + FORM_OnBeforeClosePage(page.get(), form); + return !!bitmap; +} + +// Initialize the library once for all runs of the fuzzer. +struct TestCase { + TestCase() { +#ifdef PDF_ENABLE_V8 +#ifdef V8_USE_EXTERNAL_STARTUP_DATA + platform = InitializeV8ForPDFiumWithStartupData( + ProgramPath(), "", &natives_blob, &snapshot_blob); +#else + platform = InitializeV8ForPDFium(ProgramPath()); +#endif // V8_USE_EXTERNAL_STARTUP_DATA +#endif // PDF_ENABLE_V8 + + memset(&config, '\0', sizeof(config)); + config.version = 2; + config.m_pUserFontPaths = nullptr; + config.m_pIsolate = nullptr; + config.m_v8EmbedderSlot = 0; + FPDF_InitLibraryWithConfig(&config); + + memset(&unsupport_info, '\0', sizeof(unsupport_info)); + unsupport_info.version = 1; + unsupport_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler; + FSDK_SetUnSpObjProcessHandler(&unsupport_info); + } + + std::unique_ptr platform; + v8::StartupData natives_blob; + v8::StartupData snapshot_blob; + FPDF_LIBRARY_CONFIG config; + UNSUPPORT_INFO unsupport_info; +}; + +static TestCase* test_case = new TestCase(); diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h new file mode 100644 index 0000000000..f9db00fa64 --- /dev/null +++ b/testing/fuzzers/pdfium_fuzzer_helper.h @@ -0,0 +1,25 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_ +#define TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_ + +#include "public/fpdfview.h" + +class PDFiumFuzzerHelper { + public: + void RenderPdf(const char* pBuf, size_t len); + + virtual int GetFormCallbackVersion() const = 0; + virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc); + + protected: + PDFiumFuzzerHelper(); + virtual ~PDFiumFuzzerHelper(); + + private: + bool RenderPage(FPDF_DOCUMENT doc, FPDF_FORMHANDLE form, int page_index); +}; + +#endif // TESTING_FUZZERS_PDFIUM_FUZZER_HELPER_H_ diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc new file mode 100644 index 0000000000..f9a69d4166 --- /dev/null +++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc @@ -0,0 +1,31 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "public/fpdf_formfill.h" +#include "testing/fuzzers/pdfium_fuzzer_helper.h" + +class PDFiumXFAFuzzer : public PDFiumFuzzerHelper { + public: + PDFiumXFAFuzzer() = default; + ~PDFiumXFAFuzzer() override = default; + + int GetFormCallbackVersion() const override { return 2; } + + // Return false if XFA doesn't load as otherwise we're duplicating the work + // done by the non-xfa fuzzer. + bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override { + int form_type = FPDF_GetFormType(doc); + if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND) + return false; + return FPDF_LoadXFA(doc); + } +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + PDFiumXFAFuzzer fuzzer; + fuzzer.RenderPdf(reinterpret_cast(data), size); + return 0; +} -- cgit v1.2.3