// Copyright 2021 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "public/fpdf_formfill.h" #include "testing/fuzzers/pdf_fuzzer_templates.h" #include "testing/fuzzers/pdfium_fuzzer_helper.h" class PDFiumXDPFuzzer : public PDFiumFuzzerHelper { public: PDFiumXDPFuzzer() = default; ~PDFiumXDPFuzzer() override = default; int GetFormCallbackVersion() const override { return 2; } 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); } }; struct Tag { const char* tag_name; const char* tag_start; const char* tag_end; }; const Tag kTagData[]{ {.tag_name = "config", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "template", .tag_start = R""("}, {.tag_name = "sourceSet", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "localeSet", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "dataSet", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "connectionSet", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "xdc", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "signature", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "stylesheet", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "xfdf", .tag_start = R""()"", .tag_end = ""}, {.tag_name = "xmpmeta", .tag_start = R""()"", .tag_end = ""}}; std::string CreateObject(int obj_num, const std::string& body) { std::string obj_template = R""($1 0 obj $2 endobj )""; obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num)); obj_template.replace(obj_template.find("$2"), 2, body); return obj_template; } std::string CreateStreamObject(int obj_num, const std::string& body) { std::string obj_template = R""($1 0 obj <> stream $3 endstream endobj )""; obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num)); obj_template.replace(obj_template.find("$2"), 2, std::to_string(body.size() + 1)); obj_template.replace(obj_template.find("$3"), 2, body); return obj_template; } std::string GenXrefEntry(size_t offset) { return std::string(10 - std::to_string(offset).size(), '0') + std::to_string(offset) + " 00000 n\n"; } std::string GenTagBody(const Tag& tag, FuzzedDataProvider* data_provider) { std::string tag_content = data_provider->ConsumeRandomLengthString(); return tag.tag_start + tag_content + tag.tag_end; } std::string GenXDPPdfFile(FuzzedDataProvider* data_provider) { std::vector pdf_objects; std::string pdf_header = std::string(reinterpret_cast(kSimplePdfHeader), sizeof(kSimplePdfHeader)); pdf_objects.push_back(CreateObject(1, kCatalog)); std::string xfa_obj = kSimpleXfaObjWrapper; Tag tag1 = data_provider->PickValueInArray(kTagData); Tag tag2 = data_provider->PickValueInArray(kTagData); Tag tag3 = data_provider->PickValueInArray(kTagData); xfa_obj.replace(xfa_obj.find("$1"), 2, tag1.tag_name); xfa_obj.replace(xfa_obj.find("$2"), 2, tag2.tag_name); xfa_obj.replace(xfa_obj.find("$3"), 2, tag3.tag_name); pdf_objects.push_back(CreateObject(2, xfa_obj)); pdf_objects.push_back(CreateObject(3, kSimplePagesObj)); pdf_objects.push_back(CreateObject(4, kSimplePageObj)); // preamble pdf_objects.push_back(CreateStreamObject(5, kSimplePreamble)); // The three XFA tags pdf_objects.push_back(CreateStreamObject(6, GenTagBody(tag1, data_provider))); pdf_objects.push_back(CreateStreamObject(7, GenTagBody(tag2, data_provider))); pdf_objects.push_back(CreateStreamObject(8, GenTagBody(tag3, data_provider))); // postamble pdf_objects.push_back(CreateStreamObject(9, kSimplePostamble)); // Create the xref table std::string xref = R""(xref 0 10 0000000000 65535 f )""; // Add xref entries size_t curr_offset = pdf_header.size(); for (const auto& ostr : pdf_objects) { xref += GenXrefEntry(curr_offset); curr_offset += ostr.size(); } std::string footer = R""(trailer <> startxref $1 %%EOF)""; footer.replace(footer.find("$1"), 2, std::to_string(curr_offset)); std::string pdf_core; for (const auto& ostr : pdf_objects) { pdf_core += ostr; } // Return the full PDF return pdf_header + pdf_core + xref + footer; } bool IsValidForFuzzing(const uint8_t* data, size_t size) { if (size > 2048) { return false; } const char* ptr = reinterpret_cast(data); for (size_t i = 0; i < size; i++) { if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) { return false; } } return true; } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (!IsValidForFuzzing(data, size)) { return 0; } FuzzedDataProvider data_provider(data, size); std::string xfa_final_str = GenXDPPdfFile(&data_provider); #ifdef PDFIUM_FUZZER_DUMP for (size_t i = 0; i < xfa_final_str.size(); i++) { putc(xfa_final_str[i], stdout); } #endif PDFiumXDPFuzzer fuzzer; fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size()); return 0; }