xref: /aosp_15_r20/external/pdfium/testing/fuzzers/pdfium_fuzzer_helper.cc (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 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 "testing/fuzzers/pdfium_fuzzer_helper.h"
6 
7 #include <assert.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <sstream>
15 #include <string>
16 #include <tuple>
17 #include <utility>
18 
19 #include "public/cpp/fpdf_scopers.h"
20 #include "public/fpdf_dataavail.h"
21 #include "public/fpdf_ext.h"
22 #include "public/fpdf_text.h"
23 #include "third_party/base/check_op.h"
24 #include "third_party/base/containers/span.h"
25 #include "third_party/base/numerics/checked_math.h"
26 
27 namespace {
28 
29 class FuzzerTestLoader {
30  public:
FuzzerTestLoader(pdfium::span<const char> span)31   explicit FuzzerTestLoader(pdfium::span<const char> span) : m_Span(span) {}
32 
GetBlock(void * param,unsigned long pos,unsigned char * pBuf,unsigned long size)33   static int GetBlock(void* param,
34                       unsigned long pos,
35                       unsigned char* pBuf,
36                       unsigned long size) {
37     FuzzerTestLoader* pLoader = static_cast<FuzzerTestLoader*>(param);
38     pdfium::base::CheckedNumeric<size_t> end = pos;
39     end += size;
40     CHECK_LE(end.ValueOrDie(), pLoader->m_Span.size());
41 
42     memcpy(pBuf, &pLoader->m_Span[pos], size);
43     return 1;
44   }
45 
46  private:
47   const pdfium::span<const char> m_Span;
48 };
49 
ExampleAppAlert(IPDF_JSPLATFORM *,FPDF_WIDESTRING,FPDF_WIDESTRING,int,int)50 int ExampleAppAlert(IPDF_JSPLATFORM*,
51                     FPDF_WIDESTRING,
52                     FPDF_WIDESTRING,
53                     int,
54                     int) {
55   return 0;
56 }
57 
ExampleAppResponse(IPDF_JSPLATFORM *,FPDF_WIDESTRING question,FPDF_WIDESTRING title,FPDF_WIDESTRING default_value,FPDF_WIDESTRING label,FPDF_BOOL is_password,void * response,int length)58 int ExampleAppResponse(IPDF_JSPLATFORM*,
59                        FPDF_WIDESTRING question,
60                        FPDF_WIDESTRING title,
61                        FPDF_WIDESTRING default_value,
62                        FPDF_WIDESTRING label,
63                        FPDF_BOOL is_password,
64                        void* response,
65                        int length) {
66   // UTF-16, always LE regardless of platform.
67   uint8_t* ptr = static_cast<uint8_t*>(response);
68   ptr[0] = 'N';
69   ptr[1] = 0;
70   ptr[2] = 'o';
71   ptr[3] = 0;
72   return 4;
73 }
74 
ExampleDocGotoPage(IPDF_JSPLATFORM *,int pageNumber)75 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int pageNumber) {}
76 
ExampleDocMail(IPDF_JSPLATFORM *,void * mailData,int length,FPDF_BOOL UI,FPDF_WIDESTRING To,FPDF_WIDESTRING Subject,FPDF_WIDESTRING CC,FPDF_WIDESTRING BCC,FPDF_WIDESTRING Msg)77 void ExampleDocMail(IPDF_JSPLATFORM*,
78                     void* mailData,
79                     int length,
80                     FPDF_BOOL UI,
81                     FPDF_WIDESTRING To,
82                     FPDF_WIDESTRING Subject,
83                     FPDF_WIDESTRING CC,
84                     FPDF_WIDESTRING BCC,
85                     FPDF_WIDESTRING Msg) {}
86 
Is_Data_Avail(FX_FILEAVAIL * pThis,size_t offset,size_t size)87 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
88   return true;
89 }
90 
Add_Segment(FX_DOWNLOADHINTS * pThis,size_t offset,size_t size)91 void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {}
92 
GetRenderingAndFormFlagFromData(const char * data,size_t len)93 std::pair<int, int> GetRenderingAndFormFlagFromData(const char* data,
94                                                     size_t len) {
95   std::string data_str = std::string(data, len);
96   size_t data_hash = std::hash<std::string>()(data_str);
97 
98   // The largest flag value is 0x4FFF, so just take 16 bits from |data_hash| at
99   // a time.
100   int render_flags = data_hash & 0xffff;
101   int form_flags = (data_hash >> 16) & 0xffff;
102   return std::make_pair(render_flags, form_flags);
103 }
104 
105 }  // namespace
106 
107 PDFiumFuzzerHelper::PDFiumFuzzerHelper() = default;
108 
109 PDFiumFuzzerHelper::~PDFiumFuzzerHelper() = default;
110 
OnFormFillEnvLoaded(FPDF_DOCUMENT doc)111 bool PDFiumFuzzerHelper::OnFormFillEnvLoaded(FPDF_DOCUMENT doc) {
112   return true;
113 }
114 
RenderPdf(const char * data,size_t len)115 void PDFiumFuzzerHelper::RenderPdf(const char* data, size_t len) {
116   int render_flags;
117   int form_flags;
118   std::tie(render_flags, form_flags) =
119       GetRenderingAndFormFlagFromData(data, len);
120 
121   IPDF_JSPLATFORM platform_callbacks;
122   memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
123   platform_callbacks.version = 3;
124   platform_callbacks.app_alert = ExampleAppAlert;
125   platform_callbacks.app_response = ExampleAppResponse;
126   platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
127   platform_callbacks.Doc_mail = ExampleDocMail;
128 
129   FPDF_FORMFILLINFO form_callbacks;
130   memset(&form_callbacks, '\0', sizeof(form_callbacks));
131   form_callbacks.version = GetFormCallbackVersion();
132   form_callbacks.m_pJsPlatform = &platform_callbacks;
133 
134   FuzzerTestLoader loader({data, len});
135   FPDF_FILEACCESS file_access;
136   memset(&file_access, '\0', sizeof(file_access));
137   file_access.m_FileLen = static_cast<unsigned long>(len);
138   file_access.m_GetBlock = FuzzerTestLoader::GetBlock;
139   file_access.m_Param = &loader;
140 
141   FX_FILEAVAIL file_avail;
142   memset(&file_avail, '\0', sizeof(file_avail));
143   file_avail.version = 1;
144   file_avail.IsDataAvail = Is_Data_Avail;
145 
146   FX_DOWNLOADHINTS hints;
147   memset(&hints, '\0', sizeof(hints));
148   hints.version = 1;
149   hints.AddSegment = Add_Segment;
150 
151   ScopedFPDFAvail pdf_avail(FPDFAvail_Create(&file_avail, &file_access));
152 
153   int nRet = PDF_DATA_NOTAVAIL;
154   bool bIsLinearized = false;
155   ScopedFPDFDocument doc;
156   if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
157     doc.reset(FPDFAvail_GetDocument(pdf_avail.get(), nullptr));
158     if (doc) {
159       while (nRet == PDF_DATA_NOTAVAIL)
160         nRet = FPDFAvail_IsDocAvail(pdf_avail.get(), &hints);
161 
162       if (nRet == PDF_DATA_ERROR)
163         return;
164 
165       nRet = FPDFAvail_IsFormAvail(pdf_avail.get(), &hints);
166       if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL)
167         return;
168 
169       bIsLinearized = true;
170     }
171   } else {
172     doc.reset(FPDF_LoadCustomDocument(&file_access, nullptr));
173   }
174 
175   if (!doc)
176     return;
177 
178   (void)FPDF_GetDocPermissions(doc.get());
179 
180   ScopedFPDFFormHandle form(
181       FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
182   if (!OnFormFillEnvLoaded(doc.get()))
183     return;
184 
185   FPDF_SetFormFieldHighlightColor(form.get(), FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
186   FPDF_SetFormFieldHighlightAlpha(form.get(), 100);
187   FORM_DoDocumentJSAction(form.get());
188   FORM_DoDocumentOpenAction(form.get());
189 
190   int page_count = FPDF_GetPageCount(doc.get());
191   for (int i = 0; i < page_count; ++i) {
192     if (bIsLinearized) {
193       nRet = PDF_DATA_NOTAVAIL;
194       while (nRet == PDF_DATA_NOTAVAIL)
195         nRet = FPDFAvail_IsPageAvail(pdf_avail.get(), i, &hints);
196 
197       if (nRet == PDF_DATA_ERROR)
198         return;
199     }
200     RenderPage(doc.get(), form.get(), i, render_flags, form_flags);
201   }
202   OnRenderFinished(doc.get());
203   FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
204 }
205 
RenderPage(FPDF_DOCUMENT doc,FPDF_FORMHANDLE form,int page_index,int render_flags,int form_flags)206 bool PDFiumFuzzerHelper::RenderPage(FPDF_DOCUMENT doc,
207                                     FPDF_FORMHANDLE form,
208                                     int page_index,
209                                     int render_flags,
210                                     int form_flags) {
211   ScopedFPDFPage page(FPDF_LoadPage(doc, page_index));
212   if (!page)
213     return false;
214 
215   ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
216   FORM_OnAfterLoadPage(page.get(), form);
217   FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN);
218 
219   FormActionHandler(form, doc, page.get());
220 
221   const double scale = 1.0;
222   int width = static_cast<int>(FPDF_GetPageWidthF(page.get()) * scale);
223   int height = static_cast<int>(FPDF_GetPageHeightF(page.get()) * scale);
224   ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, 0));
225   if (bitmap) {
226     FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, 0xFFFFFFFF);
227     FPDF_RenderPageBitmap(bitmap.get(), page.get(), 0, 0, width, height, 0,
228                           render_flags);
229     FPDF_FFLDraw(form, bitmap.get(), page.get(), 0, 0, width, height, 0,
230                  form_flags);
231   }
232   FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_CLOSE);
233   FORM_OnBeforeClosePage(page.get(), form);
234   return !!bitmap;
235 }
236