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 "Â",
270 "ÆÁÂÁ",
271 "Amount[*]",
272 "~!@#$%^&*()_+",
273 "&|",
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 """,
301 "Space",
302 "Str",
303 "url",
304 "xhtml",
305 "xml",
306 "XML"",
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