/src/qpdf/fuzz/qpdf_pages_fuzzer.cc
Line | Count | Source |
1 | | #include <qpdf/Buffer.hh> |
2 | | #include <qpdf/BufferInputSource.hh> |
3 | | #include <qpdf/Pl_Discard.hh> |
4 | | #include <qpdf/QPDF.hh> |
5 | | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
6 | | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
7 | | #include <qpdf/QPDFPageDocumentHelper.hh> |
8 | | #include <qpdf/QPDFPageLabelDocumentHelper.hh> |
9 | | #include <qpdf/QPDFPageObjectHelper.hh> |
10 | | #include <qpdf/global.hh> |
11 | | |
12 | | #include <chrono> |
13 | | #include <iostream> |
14 | | |
15 | | class FuzzHelper |
16 | | { |
17 | | public: |
18 | | FuzzHelper(unsigned char const* data, size_t size) : |
19 | | // We do not modify data, so it is safe to remove the const for Buffer |
20 | 273k | input_buffer(const_cast<unsigned char*>(data), size) |
21 | 273k | { |
22 | 273k | } |
23 | | |
24 | | void |
25 | | run() |
26 | 43.1k | { |
27 | 43.1k | qpdf::global::options::fuzz_mode(true); |
28 | | // The goal here is that you should be able to throw anything at libqpdf and it will respond |
29 | | // without any memory errors and never do anything worse than throwing a QPDFExc or |
30 | | // std::runtime_error. Throwing any other kind of exception, segfaulting, or having a memory |
31 | | // error (when built with appropriate sanitizers) will all cause abnormal exit. |
32 | 43.1k | try { |
33 | 43.1k | info(""); |
34 | | // Write in various ways to exercise QPDFWriter |
35 | | |
36 | 43.1k | auto is = std::make_shared<BufferInputSource>("fuzz input", &input_buffer); |
37 | 43.1k | QPDF qpdf; |
38 | 43.1k | qpdf.processInputSource(is); |
39 | 43.1k | info("processInputSource done"); |
40 | 43.1k | QPDFPageDocumentHelper pdh(qpdf); |
41 | 43.1k | QPDFPageLabelDocumentHelper pldh(qpdf); |
42 | 43.1k | QPDFOutlineDocumentHelper odh(qpdf); |
43 | 43.1k | QPDFAcroFormDocumentHelper afdh(qpdf); |
44 | 43.1k | afdh.generateAppearancesIfNeeded(); |
45 | 43.1k | info("generateAppearancesIfNeeded done"); |
46 | 43.1k | pdh.flattenAnnotations(); |
47 | 43.1k | info("flattenAnnotations done"); |
48 | 43.1k | int pageno = 0; |
49 | 43.1k | for (auto& page: pdh.getAllPages()) { |
50 | 37.3k | ++pageno; |
51 | 37.3k | try { |
52 | 37.3k | info("start page", pageno); |
53 | 37.3k | page.coalesceContentStreams(); |
54 | 37.3k | info("coalesceContentStreams done"); |
55 | 37.3k | page.parseContents(nullptr); |
56 | 37.3k | info("parseContents done"); |
57 | 37.3k | page.getImages(); |
58 | 37.3k | info("getImages done"); |
59 | 37.3k | pldh.getLabelForPage(pageno); |
60 | 37.3k | info("getLabelForPage done"); |
61 | 37.3k | QPDFObjectHandle page_obj(page.getObjectHandle()); |
62 | 37.3k | page_obj.getJSON(JSON::LATEST, true).unparse(); |
63 | 37.3k | info("getJSON done"); |
64 | 37.3k | odh.getOutlinesForPage(page_obj); |
65 | 37.3k | info("getOutlinesForPage done"); |
66 | | |
67 | 37.3k | for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) { |
68 | 3.23k | afdh.getFieldForAnnotation(aoh); |
69 | 3.23k | } |
70 | 37.3k | } catch (QPDFExc& e) { |
71 | 5.64k | std::cerr << "page " << pageno << ": " << e.what() << '\n'; |
72 | 5.64k | } |
73 | 37.3k | } |
74 | 43.1k | } catch (std::runtime_error const& e) { |
75 | 28.2k | std::cerr << "runtime_error: " << e.what() << '\n'; |
76 | 28.2k | } |
77 | 43.1k | } |
78 | | |
79 | | private: |
80 | | void |
81 | | info(std::string const& msg, int pageno = 0) const |
82 | 330k | { |
83 | 330k | const std::chrono::duration<double> elapsed{std::chrono::steady_clock::now() - start}; |
84 | | |
85 | 330k | std::cerr << elapsed.count() << " info - " << msg; |
86 | 330k | if (pageno > 0) { |
87 | 37.3k | std::cerr << " page " << pageno; |
88 | 37.3k | } |
89 | 330k | std::cerr << '\n'; |
90 | 330k | } |
91 | | |
92 | | Buffer input_buffer; |
93 | | Pl_Discard discard; |
94 | | const std::chrono::time_point<std::chrono::steady_clock> start; |
95 | | }; |
96 | | |
97 | | extern "C" int |
98 | | LLVMFuzzerTestOneInput(unsigned char const* data, size_t size) |
99 | 273k | { |
100 | 273k | #ifndef _WIN32 |
101 | | // Used by jpeg library to work around false positives in memory sanitizer. |
102 | 273k | setenv("JSIMD_FORCENONE", "1", 1); |
103 | 273k | #endif |
104 | 273k | FuzzHelper f(data, size); |
105 | 273k | f.run(); |
106 | 273k | return 0; |
107 | 273k | } |