xref: /aosp_15_r20/external/pdfium/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2021 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fuzzer/FuzzedDataProvider.h>
6 
7 #include <string>
8 
9 #include "public/fpdf_formfill.h"
10 #include "testing/fuzzers/pdf_fuzzer_templates.h"
11 #include "testing/fuzzers/pdfium_fuzzer_helper.h"
12 
13 class PDFiumXDPFuzzer : public PDFiumFuzzerHelper {
14  public:
15   PDFiumXDPFuzzer() = default;
16   ~PDFiumXDPFuzzer() override = default;
17 
GetFormCallbackVersion() const18   int GetFormCallbackVersion() const override { return 2; }
19 
OnFormFillEnvLoaded(FPDF_DOCUMENT doc)20   bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
21     int form_type = FPDF_GetFormType(doc);
22     if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
23       return false;
24     return FPDF_LoadXFA(doc);
25   }
26 };
27 
28 struct Tag {
29   const char* tag_name;
30   const char* tag_start;
31   const char* tag_end;
32 };
33 
34 const Tag kTagData[]{
35     {.tag_name = "config",
36      .tag_start =
37          R""(<xfa:config xmlns:xfa="http://www.xfa.org/schema/xci/3.1/">)"",
38      .tag_end = "</xfa:config>"},
39     {.tag_name = "template",
40      .tag_start =
41          R""(<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">)"",
42      .tag_end = "</template>"},
43     {.tag_name = "sourceSet",
44      .tag_start =
45          R""(<sourceSet xmlns="http://www.xfa.org/schema/xfa-source-set/2.7/">)"",
46      .tag_end = "</sourceSet>"},
47     {.tag_name = "localeSet",
48      .tag_start =
49          R""(<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">)"",
50      .tag_end = "</localeSet>"},
51     {.tag_name = "dataSet",
52      .tag_start =
53          R""(<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">)"",
54      .tag_end = "</xfa:datasets>"},
55     {.tag_name = "connectionSet",
56      .tag_start =
57          R""(<connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">)"",
58      .tag_end = "</connectionSet>"},
59     {.tag_name = "xdc",
60      .tag_start =
61          R""(<xsl:xdc xmlns:xdc="http://www.xfa.org/schema/xdc/1.0/">)"",
62      .tag_end = "</xsl:xdc>"},
63     {.tag_name = "signature",
64      .tag_start = R""(<signature xmlns="http://www.w3.org/2000/09/xmldsig#">)"",
65      .tag_end = "</signature>"},
66     {.tag_name = "stylesheet",
67      .tag_start =
68          R""(<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="identifier">)"",
69      .tag_end = "</stylesheet>"},
70     {.tag_name = "xfdf",
71      .tag_start =
72          R""(<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">)"",
73      .tag_end = "</xfdf>"},
74     {.tag_name = "xmpmeta",
75      .tag_start =
76          R""(<xmpmeta xmlns="http://ns.adobe.com/xmpmeta/" xml:space="preserve">)"",
77      .tag_end = "</xmpmeta>"}};
78 
CreateObject(int obj_num,const std::string & body)79 std::string CreateObject(int obj_num, const std::string& body) {
80   std::string obj_template = R""($1 0 obj
81 $2
82 endobj
83 )"";
84 
85   obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
86   obj_template.replace(obj_template.find("$2"), 2, body);
87   return obj_template;
88 }
89 
CreateStreamObject(int obj_num,const std::string & body)90 std::string CreateStreamObject(int obj_num, const std::string& body) {
91   std::string obj_template = R""($1 0 obj
92 <</Length $2>>
93 stream
94 $3
95 endstream
96 endobj
97 )"";
98 
99   obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
100   obj_template.replace(obj_template.find("$2"), 2,
101                        std::to_string(body.size() + 1));
102   obj_template.replace(obj_template.find("$3"), 2, body);
103 
104   return obj_template;
105 }
106 
GenXrefEntry(size_t offset)107 std::string GenXrefEntry(size_t offset) {
108   return std::string(10 - std::to_string(offset).size(), '0') +
109          std::to_string(offset) + " 00000 n\n";
110 }
111 
GenTagBody(const Tag & tag,FuzzedDataProvider * data_provider)112 std::string GenTagBody(const Tag& tag, FuzzedDataProvider* data_provider) {
113   std::string tag_content = data_provider->ConsumeRandomLengthString();
114   return tag.tag_start + tag_content + tag.tag_end;
115 }
116 
GenXDPPdfFile(FuzzedDataProvider * data_provider)117 std::string GenXDPPdfFile(FuzzedDataProvider* data_provider) {
118   std::vector<std::string> pdf_objects;
119   std::string pdf_header =
120       std::string(reinterpret_cast<const char*>(kSimplePdfHeader),
121                   sizeof(kSimplePdfHeader));
122 
123   pdf_objects.push_back(CreateObject(1, kCatalog));
124 
125   std::string xfa_obj = kSimpleXfaObjWrapper;
126   Tag tag1 = data_provider->PickValueInArray(kTagData);
127   Tag tag2 = data_provider->PickValueInArray(kTagData);
128   Tag tag3 = data_provider->PickValueInArray(kTagData);
129   xfa_obj.replace(xfa_obj.find("$1"), 2, tag1.tag_name);
130   xfa_obj.replace(xfa_obj.find("$2"), 2, tag2.tag_name);
131   xfa_obj.replace(xfa_obj.find("$3"), 2, tag3.tag_name);
132   pdf_objects.push_back(CreateObject(2, xfa_obj));
133   pdf_objects.push_back(CreateObject(3, kSimplePagesObj));
134   pdf_objects.push_back(CreateObject(4, kSimplePageObj));
135 
136   // preamble
137   pdf_objects.push_back(CreateStreamObject(5, kSimplePreamble));
138 
139   // The three XFA tags
140   pdf_objects.push_back(CreateStreamObject(6, GenTagBody(tag1, data_provider)));
141   pdf_objects.push_back(CreateStreamObject(7, GenTagBody(tag2, data_provider)));
142   pdf_objects.push_back(CreateStreamObject(8, GenTagBody(tag3, data_provider)));
143 
144   // postamble
145   pdf_objects.push_back(CreateStreamObject(9, kSimplePostamble));
146 
147   // Create the xref table
148   std::string xref = R""(xref
149 0 10
150 0000000000 65535 f
151 )"";
152 
153   // Add xref entries
154   size_t curr_offset = pdf_header.size();
155   for (const auto& ostr : pdf_objects) {
156     xref += GenXrefEntry(curr_offset);
157     curr_offset += ostr.size();
158   }
159 
160   std::string footer = R""(trailer
161 <</Root 1 0 R /Size 10>>
162 startxref
163 $1
164 %%EOF)"";
165   footer.replace(footer.find("$1"), 2, std::to_string(curr_offset));
166 
167   std::string pdf_core;
168   for (const auto& ostr : pdf_objects) {
169     pdf_core += ostr;
170   }
171 
172   // Return the full PDF
173   return pdf_header + pdf_core + xref + footer;
174 }
175 
IsValidForFuzzing(const uint8_t * data,size_t size)176 bool IsValidForFuzzing(const uint8_t* data, size_t size) {
177   if (size > 2048) {
178     return false;
179   }
180   const char* ptr = reinterpret_cast<const char*>(data);
181   for (size_t i = 0; i < size; i++) {
182     if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) {
183       return false;
184     }
185   }
186   return true;
187 }
188 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)189 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
190   if (!IsValidForFuzzing(data, size)) {
191     return 0;
192   }
193 
194   FuzzedDataProvider data_provider(data, size);
195   std::string xfa_final_str = GenXDPPdfFile(&data_provider);
196 
197 #ifdef PDFIUM_FUZZER_DUMP
198   for (size_t i = 0; i < xfa_final_str.size(); i++) {
199     putc(xfa_final_str[i], stdout);
200   }
201 #endif
202 
203   PDFiumXDPFuzzer fuzzer;
204   fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
205   return 0;
206 }
207