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