xref: /aosp_15_r20/external/pdfium/testing/fuzzers/pdf_xfa_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 #include <vector>
9 
10 #include "public/fpdf_formfill.h"
11 #include "testing/fuzzers/pdf_fuzzer_templates.h"
12 #include "testing/fuzzers/pdfium_fuzzer_helper.h"
13 #include "third_party/base/containers/adapters.h"
14 
15 class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
16  public:
17   PDFiumXFAFuzzer() = default;
18   ~PDFiumXFAFuzzer() override = default;
19 
GetFormCallbackVersion() const20   int GetFormCallbackVersion() const override { return 2; }
21 
SetFdp(FuzzedDataProvider * fdp)22   void SetFdp(FuzzedDataProvider* fdp) { fdp_ = fdp; }
23 
24   // Return false if XFA doesn't load as otherwise we're duplicating the work
25   // done by the non-xfa fuzzer.
OnFormFillEnvLoaded(FPDF_DOCUMENT doc)26   bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
27     int form_type = FPDF_GetFormType(doc);
28     if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
29       return false;
30     return FPDF_LoadXFA(doc);
31   }
32 
FormActionHandler(FPDF_FORMHANDLE form,FPDF_DOCUMENT doc,FPDF_PAGE page)33   void FormActionHandler(FPDF_FORMHANDLE form,
34                          FPDF_DOCUMENT doc,
35                          FPDF_PAGE page) override {
36     if (!fdp_) {
37       return;
38     }
39     char local_buf[50];
40     int number_of_calls = fdp_->ConsumeIntegralInRange<int>(0, 250);
41     for (int i = 0; i < number_of_calls; i++) {
42       UserInteraction selector = fdp_->ConsumeEnum<UserInteraction>();
43       switch (selector) {
44         case kOnLButtonUp: {
45           FORM_OnLButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
46                            fdp_->ConsumeIntegralInRange<int>(-100, 1000),
47                            fdp_->ConsumeIntegralInRange<int>(-100, 1000));
48           break;
49         }
50         case kOnRButtonUp: {
51           FORM_OnRButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
52                            fdp_->ConsumeIntegralInRange<int>(-100, 1000),
53                            fdp_->ConsumeIntegralInRange<int>(-100, 1000));
54           break;
55         }
56         case kOnLButtonDown: {
57           FORM_OnLButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
58                              fdp_->ConsumeIntegralInRange<int>(-100, 1000),
59                              fdp_->ConsumeIntegralInRange<int>(-100, 1000));
60           break;
61         }
62         case kOnRButtonDown: {
63           FORM_OnRButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
64                              fdp_->ConsumeIntegralInRange<int>(-100, 1000),
65                              fdp_->ConsumeIntegralInRange<int>(-100, 1000));
66           break;
67         }
68         case kOnChar: {
69           FORM_OnChar(form, page, fdp_->ConsumeIntegral<int>(),
70                       fdp_->ConsumeIntegral<int>());
71           break;
72         }
73         case kOnKeyDown: {
74           FORM_OnKeyDown(form, page, fdp_->ConsumeIntegral<int>(),
75                          fdp_->ConsumeIntegral<int>());
76           break;
77         }
78         case kOnKeyUp: {
79           FORM_OnKeyUp(form, page, fdp_->ConsumeIntegral<int>(),
80                        fdp_->ConsumeIntegral<int>());
81           break;
82         }
83         case kOnLButtonDoubleClick: {
84           FORM_OnLButtonDoubleClick(form, page, fdp_->ConsumeIntegral<int>(),
85                                     fdp_->ConsumeIntegral<int>(),
86                                     fdp_->ConsumeIntegral<int>());
87           break;
88         }
89         case kOnMouseMove: {
90           FORM_OnMouseMove(form, page, fdp_->ConsumeIntegral<int>(),
91                            fdp_->ConsumeIntegral<int>(),
92                            fdp_->ConsumeIntegral<int>());
93           break;
94         }
95         case kOnMouseWheel: {
96           const FS_POINTF point = {fdp_->ConsumeFloatingPoint<float>(),
97                                    fdp_->ConsumeFloatingPoint<float>()};
98           FORM_OnMouseWheel(form, page, fdp_->ConsumeIntegral<int>(), &point,
99                             fdp_->ConsumeIntegral<int>(),
100                             fdp_->ConsumeIntegral<int>());
101           break;
102         }
103         case kOnFocus: {
104           FORM_OnFocus(form, page, fdp_->ConsumeIntegral<int>(),
105                        fdp_->ConsumeIntegral<int>(),
106                        fdp_->ConsumeIntegral<int>());
107           break;
108         }
109         case kUndo: {
110           if (FORM_CanUndo(form, page)) {
111             FORM_Undo(form, page);
112           }
113           break;
114         }
115         case kSelectAllText: {
116           FORM_SelectAllText(form, page);
117           break;
118         }
119         case kRedo: {
120           if (FORM_CanRedo(form, page)) {
121             FORM_Redo(form, page);
122           }
123           break;
124         }
125         case kAnnot: {
126           FPDF_ANNOTATION annot = nullptr;
127           int page_index = -2;
128           FORM_GetFocusedAnnot(form, &page_index, &annot);
129           if (annot) {
130             FORM_SetFocusedAnnot(form, annot);
131           }
132           break;
133         }
134         case kSetIndexSelected: {
135           FORM_SetIndexSelected(form, page, fdp_->ConsumeIntegral<int>(),
136                                 fdp_->ConsumeBool());
137           break;
138         }
139         case kIsIndexSelected: {
140           FORM_IsIndexSelected(form, page, fdp_->ConsumeIntegral<int>());
141           break;
142         }
143         case kHasFormFieldAtPoint: {
144           FPDFPage_HasFormFieldAtPoint(form, page, fdp_->ConsumeIntegral<int>(),
145                                        fdp_->ConsumeIntegral<int>());
146           break;
147         }
148         case kFormFieldZOrderAtPoint: {
149           FPDFPage_FormFieldZOrderAtPoint(form, page,
150                                           fdp_->ConsumeIntegral<int>(),
151                                           fdp_->ConsumeIntegral<int>());
152           break;
153         }
154         case kGetSelectedText: {
155           FORM_GetSelectedText(form, page, local_buf, sizeof(local_buf));
156           break;
157         }
158         case kGetFocusedText: {
159           FORM_GetFocusedText(form, page, local_buf, sizeof(local_buf));
160           break;
161         }
162       }
163     }
164   }
165 
166  private:
167   enum UserInteraction {
168     kOnLButtonUp = 0,
169     kOnRButtonUp,
170     kOnLButtonDown,
171     kOnRButtonDown,
172     kOnChar,
173     kOnKeyDown,
174     kOnKeyUp,
175     kOnLButtonDoubleClick,
176     kOnMouseMove,
177     kOnMouseWheel,
178     kOnFocus,
179     kUndo,
180     kSelectAllText,
181     kRedo,
182     kAnnot,
183     kSetIndexSelected,
184     kIsIndexSelected,
185     kHasFormFieldAtPoint,
186     kFormFieldZOrderAtPoint,
187     kGetSelectedText,
188     kGetFocusedText,
189     kMaxValue = kGetFocusedText
190   };
191   FuzzedDataProvider* fdp_ = nullptr;
192 };
193 
194 // Possible names of an XFA FormCalc script function
GenXfaFormCalcScriptFuncName(FuzzedDataProvider * data_provider)195 std::string GenXfaFormCalcScriptFuncName(FuzzedDataProvider* data_provider) {
196   static const char* const kXfaScriptFuncs[] = {
197       "Abs",       "Apr",        "At",           "Avg",          "Ceil",
198       "Choose",    "Concat",     "Count",        "Cterm",        "Date",
199       "Date2Num",  "DateFmt",    "Decode",       "Encode",       "Eval",
200       "Exists",    "Floor",      "Format",       "FV",           "Get",
201       "HasValue",  "If",         "Ipmt",         "IsoDate2Num",  "IsoTime2Num",
202       "Left",      "Len",        "LocalDateFmt", "LocalTimeFmt", "Lower",
203       "Ltrim",     "Max",        "Min",          "Mod",          "NPV",
204       "Num2Date",  "Num2GMTime", "Num2Time",     "Oneof",        "Parse",
205       "Pmt",       "Post",       "PPmt",         "Put",          "PV",
206       "Rate",      "Ref",        "Replace",      "Right",        "Round",
207       "Rtrim",     "Space",      "Str",          "Stuff",        "Substr",
208       "Sum",       "Term",       "Time",         "Time2Num",     "TimeFmt",
209       "Translate", "UnitType",   "UnitValue",    "Upper",        "Uuid",
210       "Within",    "WordNum",
211   };
212 
213   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
214       0, std::size(kXfaScriptFuncs) - 1);
215   return kXfaScriptFuncs[elem_selector];
216 }
217 
MaybeQuote(FuzzedDataProvider * data_provider,std::string body)218 std::string MaybeQuote(FuzzedDataProvider* data_provider, std::string body) {
219   if (data_provider->ConsumeIntegralInRange<uint32_t>(0, 100) < 20) {
220     return "\"" + body + "\"";
221   }
222   return body;
223 }
224 
225 // Possible arguments to a XFA script function
GenXfaScriptParam(FuzzedDataProvider * data_provider)226 std::string GenXfaScriptParam(FuzzedDataProvider* data_provider) {
227   static const char* const kXfaFuncParams[] = {
228       "$",
229       "-0",
230       "04/13/2019",
231       ".05",
232       "-1",
233       "1",
234       " 1 | 0",
235       "10 * 10 * 10 * 9 * 123",
236       "1024",
237       "10 * a + 9",
238       "1.2131",
239       "[1,2,3]",
240       "%123",
241       "[1,2,3][0]",
242       "123124",
243       "123342123",
244       "13:13:13",
245       "13:13:13 GMT",
246       "19960315T20:20:20",
247       "1 and 1",
248       "1 and 2",
249       "2",
250       "20000201",
251       "2009-06-01T13:45:30",
252       "2009-06-15T01:45:30",
253       "2009-06-15T13:45:30-07:00",
254       "2009-06-15T13:45:30.5275000",
255       " 2 < 3 + 1",
256       "2 + 3 + 9",
257       "3",
258       "3 * 1",
259       "3 -9",
260       "5 < 5",
261       "-99",
262       "99",
263       "9999999",
264       "99999999999",
265       "A",
266       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
267       "\xc3\x81\xc3\x82\xc3\x83\xc3\x84\xc3\x85\xc3\x86",
268       "<a><b></b></a>",
269       "&Acirc;",
270       "&AElig;&Aacute;&Acirc;&Aacute;",
271       "Amount[*]",
272       "~!@#$%^&amp;*()_+",
273       "&amp;|",
274       "&apos",
275       "apr",
276       "april",
277       "B",
278       "<br>",
279       "C",
280       "de_DE",
281       "es_ES",
282       "feb",
283       "febuary",
284       "HH:MM:SS",
285       "<html>",
286       "html",
287       "HTML",
288       "jan",
289       "january",
290       "json",
291       "lkdjfglsdkfgj",
292       "mar",
293       "march",
294       "name[0]",
295       "name1",
296       "name2",
297       "name3",
298       "name4",
299       "name[*].numAmount",
300       "&quot;",
301       "Space",
302       "Str",
303       "url",
304       "xhtml",
305       "xml",
306       "XML&quot;",
307   };
308 
309   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
310       0, std::size(kXfaFuncParams) - 1);
311   return MaybeQuote(data_provider, kXfaFuncParams[elem_selector]);
312 }
313 
314 // Possible XFA tags
GenXfaTag(FuzzedDataProvider * data_provider)315 std::string GenXfaTag(FuzzedDataProvider* data_provider) {
316   static const char* const kXfaElemTags[] = {
317       "accessibleContent",
318       "acrobat",
319       "acrobat",
320       "acrobat7",
321       "ADBE_JSConsole",
322       "ADBE_JSDebugger",
323       "addSilentPrint",
324       "addViewerPreferences",
325       "adjustData",
326       "adobeExtensionLevel",
327       "agent",
328       "alwaysEmbed",
329       "amd",
330       "appearanceFilter",
331       "arc",
332       "area",
333       "assist",
334       "attributes",
335       "autoSave",
336       "barcode",
337       "base",
338       "batchOutput",
339       "behaviorOverride",
340       "bind",
341       "bindItems",
342       "bookend",
343       "boolean",
344       "border",
345       "break",
346       "breakAfter",
347       "breakBefore",
348       "button",
349       "cache",
350       "calculate",
351       "calendarSymbols",
352       "caption",
353       "certificate",
354       "certificates",
355       "change",
356       "checkButton",
357       "choiceList",
358       "color",
359       "comb",
360       "command",
361       "common",
362       "compress",
363       "compression",
364       "compressLogicalStructure",
365       "compressObjectStream",
366       "config",
367       "config",
368       "conformance",
369       "connect",
370       "connectionSet",
371       "connectString",
372       "contentArea",
373       "contentCopy",
374       "copies",
375       "corner",
376       "creator",
377       "currencySymbol",
378       "currencySymbols",
379       "currentPage",
380       "data",
381       "dataGroup",
382       "dataModel",
383       "dataValue",
384       "dataWindow",
385       "date",
386       "datePattern",
387       "datePatterns",
388       "dateTime",
389       "dateTimeEdit",
390       "dateTimeSymbols",
391       "day",
392       "dayNames",
393       "debug",
394       "decimal",
395       "defaultTypeface",
396       "defaultUi",
397       "delete",
398       "delta",
399       "deltas",
400       "desc",
401       "destination",
402       "digestMethod",
403       "digestMethods",
404       "documentAssembly",
405       "draw",
406       "driver",
407       "dSigData",
408       "duplexOption",
409       "dynamicRender",
410       "edge",
411       "effectiveInputPolicy",
412       "effectiveOutputPolicy",
413       "embed",
414       "encoding",
415       "encodings",
416       "encrypt",
417       "encryption",
418       "encryptionLevel",
419       "encryptionMethod",
420       "encryptionMethods",
421       "enforce",
422       "equate",
423       "equateRange",
424       "era",
425       "eraNames",
426       "event",
427       "eventPseudoModel",
428       "exclGroup",
429       "exclude",
430       "excludeNS",
431       "exData",
432       "execute",
433       "exObject",
434       "extras",
435       "field",
436       "fill",
437       "filter",
438       "flipLabel",
439       "float",
440       "font",
441       "fontInfo",
442       "form",
443       "format",
444       "formFieldFilling",
445       "groupParent",
446       "handler",
447       "hostPseudoModel",
448       "hyphenation",
449       "ifEmpty",
450       "image",
451       "imageEdit",
452       "includeXDPContent",
453       "incrementalLoad",
454       "incrementalMerge",
455       "insert",
456       "instanceManager",
457       "integer",
458       "interactive",
459       "issuers",
460       "items",
461       "jog",
462       "keep",
463       "keyUsage",
464       "labelPrinter",
465       "layout",
466       "layoutPseudoModel",
467       "level",
468       "line",
469       "linear",
470       "linearized",
471       "list",
472       "locale",
473       "localeSet",
474       "lockDocument",
475       "log",
476       "logPseudoModel",
477       "manifest",
478       "map",
479       "margin",
480       "mdp",
481       "medium",
482       "mediumInfo",
483       "meridiem",
484       "meridiemNames",
485       "message",
486       "messaging",
487       "mode",
488       "modifyAnnots",
489       "month",
490       "monthNames",
491       "msgId",
492       "nameAttr",
493       "neverEmbed",
494       "numberOfCopies",
495       "numberPattern",
496       "numberPatterns",
497       "numberSymbol",
498       "numberSymbols",
499       "numericEdit",
500       "object",
501       "occur",
502       "oid",
503       "oids",
504       "openAction",
505       "operation",
506       "output",
507       "outputBin",
508       "outputXSL",
509       "overflow",
510       "overprint",
511       "packet",
512       "packets",
513       "pageArea",
514       "pageOffset",
515       "pageRange",
516       "pageSet",
517       "pagination",
518       "paginationOverride",
519       "para",
520       "part",
521       "password",
522       "passwordEdit",
523       "pattern",
524       "pcl",
525       "pdf",
526       "pdfa",
527       "permissions",
528       "pickTrayByPDFSize",
529       "picture",
530       "plaintextMetadata",
531       "presence",
532       "present",
533       "present",
534       "print",
535       "printerName",
536       "printHighQuality",
537       "printScaling",
538       "producer",
539       "proto",
540       "ps",
541       "psMap",
542       "query",
543       "radial",
544       "range",
545       "reason",
546       "reasons",
547       "record",
548       "recordSet",
549       "rectangle",
550       "ref",
551       "relevant",
552       "rename",
553       "renderPolicy",
554       "rootElement",
555       "runScripts",
556       "script",
557       "scriptModel",
558       "select",
559       "setProperty",
560       "severity",
561       "signature",
562       "signatureProperties",
563       "signaturePseudoModel",
564       "signData",
565       "signing",
566       "silentPrint",
567       "soapAction",
568       "soapAddress",
569       "solid",
570       "source",
571       "sourceSet",
572       "speak",
573       "staple",
574       "startNode",
575       "startPage",
576       "stipple",
577       "subform",
578       "subform",
579       "subformSet",
580       "subjectDN",
581       "subjectDNs",
582       "submit",
583       "submitFormat",
584       "submitUrl",
585       "subsetBelow",
586       "suppressBanner",
587       "tagged",
588       "template",
589       "template",
590       "templateCache",
591       "#text",
592       "text",
593       "textedit",
594       "textEdit",
595       "threshold",
596       "time",
597       "timePattern",
598       "timePatterns",
599       "timeStamp",
600       "to",
601       "toolTip",
602       "trace",
603       "transform",
604       "traversal",
605       "traverse",
606       "treeList",
607       "type",
608       "typeface",
609       "typefaces",
610       "ui",
611       "update",
612       "uri",
613       "user",
614       "validate",
615       "validate",
616       "validateApprovalSignatures",
617       "validationMessaging",
618       "value",
619       "variables",
620       "version",
621       "versionControl",
622       "viewerPreferences",
623       "webClient",
624       "whitespace",
625       "window",
626       "wsdlAddress",
627       "wsdlConnection",
628       "xdc",
629       "xdp",
630       "xfa",
631       "#xHTML",
632       "#xml",
633       "xmlConnection",
634       "xsdConnection",
635       "xsl",
636       "zpl",
637   };
638 
639   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
640       0, std::size(kXfaElemTags) - 1);
641   return kXfaElemTags[elem_selector];
642 }
643 
644 // Possible XFA attributes values
GenXfaTagValue(FuzzedDataProvider * data_provider)645 std::string GenXfaTagValue(FuzzedDataProvider* data_provider) {
646   static const char* const kXfaTagVals[] = {
647       "0",         "0pt",         "-1",
648       "123",       "1pt",         "203.2mm",
649       "22.1404mm", "255",         "256",
650       "321",       "5431.21mm",   "6.35mm",
651       "8in",       "8pt",         "application/x-javascript",
652       "bold",      "bold",        "change",
653       "click",     "consumeData", "docReady",
654       "en_US",     "form1",       "initialize",
655       "italic",    "middle",      "name2",
656       "name3",     "name4",       "name5",
657       "onEnter",   "Page1",       "RadioList[0]",
658       "subform_1", "tb",          "Verdana",
659   };
660 
661   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
662       0, std::size(kXfaTagVals) - 1);
663   return MaybeQuote(data_provider, kXfaTagVals[elem_selector]);
664 }
665 
666 // possible XFA attributes
GenXfaTagName(FuzzedDataProvider * data_provider)667 std::string GenXfaTagName(FuzzedDataProvider* data_provider) {
668   static const char* const kXfaTagNames[] = {
669       "activity",    "baselineShift",
670       "contentType", "h",
671       "id",          "layout",
672       "layout",      "leftInset",
673       "locale",      "long",
674       "marginLeft",  "marginRight",
675       "marginRight", "mergeMode",
676       "name",        "ref",
677       "scriptTest",  "short",
678       "size",        "spaceAbove",
679       "spaceBelow",  "startNew",
680       "stock",       "textIndent",
681       "timeStamp",   "typeface",
682       "uuid",        "vAlign",
683       "value",       "w",
684       "weight",      "x",
685       "y",
686   };
687   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
688       0, std::size(kXfaTagNames) - 1);
689   return kXfaTagNames[elem_selector];
690 }
691 
692 // Will create a simple XFA FormCalc script that calls a single function.
GenXfaFormCalcScript(FuzzedDataProvider * data_provider)693 std::string GenXfaFormCalcScript(FuzzedDataProvider* data_provider) {
694   std::string xfa_string = GenXfaFormCalcScriptFuncName(data_provider);
695   xfa_string += "(";
696 
697   // Generate parameters
698   size_t num_params = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
699   for (size_t i = 0; i < num_params; i++) {
700     if (i != 0) {
701       xfa_string += ",";
702     }
703     xfa_string += GenXfaScriptParam(data_provider);
704   }
705   xfa_string += ")";
706   return xfa_string;
707 }
708 
709 // XFA Javascript logic
GenXfaName(FuzzedDataProvider * data_provider)710 std::string GenXfaName(FuzzedDataProvider* data_provider) {
711   return "name" + std::to_string(data_provider->ConsumeIntegralInRange(0, 25));
712 }
713 
GetXfaJSPrimitiveType(FuzzedDataProvider * data_provider)714 std::string GetXfaJSPrimitiveType(FuzzedDataProvider* data_provider) {
715   return GenXfaScriptParam(data_provider);
716 }
717 
GenXfaJSRValue(FuzzedDataProvider * data_provider)718 std::string GenXfaJSRValue(FuzzedDataProvider* data_provider) {
719   if (data_provider->ConsumeBool()) {
720     return GenXfaScriptParam(data_provider);
721   }
722 
723   std::string xfa_string;
724   if (data_provider->ConsumeBool()) {
725     xfa_string += "xfa.form.";
726   }
727 
728   // Handle the possibility of nested names
729   size_t num_nests = data_provider->ConsumeIntegralInRange<size_t>(1, 3);
730   for (size_t i = 0; i < num_nests; i++) {
731     if (i != 0) {
732       xfa_string += ".";
733     }
734     xfa_string += GenXfaName(data_provider);
735   }
736   return MaybeQuote(data_provider, xfa_string);
737 }
738 
GenXfaJSAssignment(FuzzedDataProvider * data_provider)739 std::string GenXfaJSAssignment(FuzzedDataProvider* data_provider) {
740   return GenXfaName(data_provider) + " = " + GenXfaJSRValue(data_provider);
741 }
742 
GenXfaJSMethodCall(FuzzedDataProvider * data_provider)743 std::string GenXfaJSMethodCall(FuzzedDataProvider* data_provider) {
744   static const char* const kXfaJSFuncs[] = {
745       "addItem",
746       "boundItem",
747       "clearItems",
748       "deleteItem",
749       "execCalculate",
750       "execEvent",
751       "execInitialize",
752       "execValidate",
753       "getDisplayItem",
754       "getItemState",
755       "getSaveItem",
756       "exec.form.formNodes",
757       "exec.form.recalculate",
758       "setItemState",
759       "xfa.container.getDelta",
760       "xfa.container.getDeltas",
761       "xfa.event.emit",
762       "xfa.event.reset",
763       "xfa.form.execCalculat",
764       "xfa.form.execInitialize",
765       "xfa.form.execValidate",
766       "xfa.form.remerge",
767       "xfa.host.beep",
768       "xfa.host.documentCountInBatch",
769       "xfa.host.documentInBatch",
770       "xfa.host.exportData",
771       "xfa.host.getFocus",
772       "xfa.host.gotoURL",
773       "xfa.host.importData",
774       "xfa.host.messageBox",
775       "xfa.host.openList",
776       "xfa.host.pageDown",
777       "xfa.host.pageUp",
778       "xfa.host.print",
779       "xfa.host.resetData",
780       "xfa.host.setFocus",
781       "xfa.host.response",
782       "xfa.resolveNode",
783   };
784 
785   std::string xfa_string = data_provider->PickValueInArray(kXfaJSFuncs);
786   xfa_string += "(";
787 
788   // Get the params
789   size_t param_count = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
790   for (size_t i = 0; i < param_count; i++) {
791     if (i != 0) {
792       xfa_string += ",";
793     }
794     xfa_string += GenXfaJSRValue(data_provider);
795   }
796   xfa_string += ")";
797   return xfa_string;
798 }
799 
800 // This is a simple generator of xfa-based javascript. The function creates
801 // simple javascript statements that are related to XFA logic and the goal is
802 // not to create fully-fleged javascript programs but rather use simple
803 // statements to ensure XFA code is covered.
804 enum XFAJSStatement {
805   kAssignment = 0,
806   kJSMethodCall,
807   kJSObjectCall,
808   kMaxValue = kJSObjectCall
809 };
810 
GenXfaJSScript(FuzzedDataProvider * data_provider)811 std::string GenXfaJSScript(FuzzedDataProvider* data_provider) {
812   std::string xfa_string;
813 
814   size_t num_stmts = data_provider->ConsumeIntegralInRange<size_t>(1, 10);
815   for (size_t i = 0; i < num_stmts; i++) {
816     XFAJSStatement stmt = data_provider->ConsumeEnum<XFAJSStatement>();
817     switch (stmt) {
818       case kAssignment:
819         xfa_string += GenXfaJSAssignment(data_provider);
820         break;
821       case kJSMethodCall:
822         xfa_string += GenXfaJSMethodCall(data_provider);
823         break;
824       case kJSObjectCall:
825         xfa_string += GenXfaName(data_provider);
826         xfa_string += ".";
827         xfa_string += GenXfaJSMethodCall(data_provider);
828         break;
829     }
830     xfa_string += ";\n";
831   }
832   return xfa_string;
833 }
834 
GenXfacript(FuzzedDataProvider * data_provider)835 std::string GenXfacript(FuzzedDataProvider* data_provider) {
836   // Determine if this should be a FormCalc script or Javascript, 50/50 chance
837   // for each.
838   if (data_provider->ConsumeBool()) {
839     return GenXfaFormCalcScript(data_provider);
840   }
841   return GenXfaJSScript(data_provider);
842 }
843 
844 // Will create a single XFA attributes, with both lhs and rhs.
getXfaElemAttributes(FuzzedDataProvider * data_provider)845 std::string getXfaElemAttributes(FuzzedDataProvider* data_provider) {
846   // Generate a set of tags, and a set of values for the tags.
847   return GenXfaTagName(data_provider) + "=" + GenXfaTagValue(data_provider);
848 }
849 
850 // Creates an XFA structure wrapped in <xdp tags.
GenXfaTree(FuzzedDataProvider * data_provider)851 std::string GenXfaTree(FuzzedDataProvider* data_provider) {
852   std::string xfa_string = "<xdp xmlns=\"http://ns.adobe.com/xdp/\">";
853 
854   // One stack iteration
855   int stack_iterations = data_provider->ConsumeIntegralInRange(1, 3);
856   for (int si = 0; si < stack_iterations; si++) {
857     int elem_count = data_provider->ConsumeIntegralInRange(1, 6);
858     std::vector<std::string> xml_stack;
859     xml_stack.reserve(elem_count);
860     for (int i = 0; i < elem_count; i++) {
861       std::string tag = GenXfaTag(data_provider);
862       xfa_string += "<" + tag;
863 
864       // in 30% of cases, add attributes
865       if (data_provider->ConsumeIntegralInRange(1, 100) > 70) {
866         size_t attribute_count = data_provider->ConsumeIntegralInRange(1, 5);
867         for (; 0 < attribute_count; attribute_count--) {
868           xfa_string += " " + getXfaElemAttributes(data_provider);
869         }
870       }
871       xfa_string += ">";
872 
873       // If needed, add a body to the tag
874       if (tag == "script") {
875         xfa_string += GenXfacript(data_provider);
876       }
877 
878       // Push the tag to the stack so we can close it when done
879       xml_stack.push_back(tag);
880     }
881     for (const std::string& tag : pdfium::base::Reversed(xml_stack)) {
882       xfa_string += "</" + tag + ">";
883     }
884   }
885   xfa_string += "</xdp>";
886   return xfa_string;
887 }
888 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)889 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
890   FuzzedDataProvider data_provider(data, size);
891   std::string xfa_string = GenXfaTree(&data_provider);
892 
893   // Add 1 for newline before endstream.
894   std::string xfa_stream_len = std::to_string(xfa_string.size() + 1);
895 
896   // Compose the fuzzer
897   std::string xfa_final_str = std::string(kSimplePdfTemplate);
898   xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len);
899   xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string);
900 
901 #ifdef PDFIUM_FUZZER_DUMP
902   for (size_t i = 0; i < xfa_final_str.size(); i++) {
903     putc(xfa_final_str[i], stdout);
904   }
905 #endif
906 
907   PDFiumXFAFuzzer fuzzer;
908   fuzzer.SetFdp(&data_provider);
909   fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
910   return 0;
911 }
912