207 lines
6.2 KiB
C++
207 lines
6.2 KiB
C++
// 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 <fuzzer/FuzzedDataProvider.h>
|
|
|
|
#include <string>
|
|
|
|
#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""(<xfa:config xmlns:xfa="http://www.xfa.org/schema/xci/3.1/">)"",
|
|
.tag_end = "</xfa:config>"},
|
|
{.tag_name = "template",
|
|
.tag_start =
|
|
R""(<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">)"",
|
|
.tag_end = "</template>"},
|
|
{.tag_name = "sourceSet",
|
|
.tag_start =
|
|
R""(<sourceSet xmlns="http://www.xfa.org/schema/xfa-source-set/2.7/">)"",
|
|
.tag_end = "</sourceSet>"},
|
|
{.tag_name = "localeSet",
|
|
.tag_start =
|
|
R""(<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">)"",
|
|
.tag_end = "</localeSet>"},
|
|
{.tag_name = "dataSet",
|
|
.tag_start =
|
|
R""(<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">)"",
|
|
.tag_end = "</xfa:datasets>"},
|
|
{.tag_name = "connectionSet",
|
|
.tag_start =
|
|
R""(<connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">)"",
|
|
.tag_end = "</connectionSet>"},
|
|
{.tag_name = "xdc",
|
|
.tag_start =
|
|
R""(<xsl:xdc xmlns:xdc="http://www.xfa.org/schema/xdc/1.0/">)"",
|
|
.tag_end = "</xsl:xdc>"},
|
|
{.tag_name = "signature",
|
|
.tag_start = R""(<signature xmlns="http://www.w3.org/2000/09/xmldsig#">)"",
|
|
.tag_end = "</signature>"},
|
|
{.tag_name = "stylesheet",
|
|
.tag_start =
|
|
R""(<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="identifier">)"",
|
|
.tag_end = "</stylesheet>"},
|
|
{.tag_name = "xfdf",
|
|
.tag_start =
|
|
R""(<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">)"",
|
|
.tag_end = "</xfdf>"},
|
|
{.tag_name = "xmpmeta",
|
|
.tag_start =
|
|
R""(<xmpmeta xmlns="http://ns.adobe.com/xmpmeta/" xml:space="preserve">)"",
|
|
.tag_end = "</xmpmeta>"}};
|
|
|
|
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
|
|
<</Length $2>>
|
|
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<std::string> pdf_objects;
|
|
std::string pdf_header =
|
|
std::string(reinterpret_cast<const char*>(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
|
|
<</Root 1 0 R /Size 10>>
|
|
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<const char*>(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;
|
|
}
|