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