/src/qpdf/fuzz/qpdf_outlines_fuzzer.cc
Line | Count | Source |
1 | | #include <qpdf/Buffer.hh> |
2 | | #include <qpdf/BufferInputSource.hh> |
3 | | #include <qpdf/Pl_DCT.hh> |
4 | | #include <qpdf/Pl_Discard.hh> |
5 | | #include <qpdf/Pl_Flate.hh> |
6 | | #include <qpdf/Pl_PNGFilter.hh> |
7 | | #include <qpdf/Pl_RunLength.hh> |
8 | | #include <qpdf/Pl_TIFFPredictor.hh> |
9 | | #include <qpdf/QPDF.hh> |
10 | | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
11 | | #include <qpdf/QPDFPageObjectHelper.hh> |
12 | | #include <qpdf/QUtil.hh> |
13 | | #include <cstdlib> |
14 | | |
15 | | class FuzzHelper |
16 | | { |
17 | | public: |
18 | | FuzzHelper(unsigned char const* data, size_t size); |
19 | | void run(); |
20 | | |
21 | | private: |
22 | | std::shared_ptr<QPDF> getQpdf(); |
23 | | void testOutlines(); |
24 | | void doChecks(); |
25 | | |
26 | | Buffer input_buffer; |
27 | | Pl_Discard discard; |
28 | | }; |
29 | | |
30 | | FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) : |
31 | | // We do not modify data, so it is safe to remove the const for Buffer |
32 | 298k | input_buffer(const_cast<unsigned char*>(data), size) |
33 | 298k | { |
34 | 298k | } |
35 | | |
36 | | std::shared_ptr<QPDF> |
37 | | FuzzHelper::getQpdf() |
38 | 262k | { |
39 | 262k | auto is = |
40 | 262k | std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer)); |
41 | 262k | auto qpdf = QPDF::create(); |
42 | 262k | qpdf->setMaxWarnings(200); |
43 | 262k | qpdf->processInputSource(is); |
44 | 262k | return qpdf; |
45 | 262k | } |
46 | | |
47 | | void |
48 | | FuzzHelper::testOutlines() |
49 | 38.3k | { |
50 | 38.3k | std::shared_ptr<QPDF> q = getQpdf(); |
51 | 38.3k | std::list<std::vector<QPDFOutlineObjectHelper>> queue; |
52 | 38.3k | auto& odh = QPDFOutlineDocumentHelper::get(*q); |
53 | 38.3k | queue.push_back(odh.getTopLevelOutlines()); |
54 | 55.8k | while (!queue.empty()) { |
55 | 17.5k | for (auto& ol: *(queue.begin())) { |
56 | 14.9k | ol.getDestPage(); |
57 | 14.9k | queue.push_back(ol.getKids()); |
58 | 14.9k | } |
59 | 17.5k | queue.pop_front(); |
60 | 17.5k | } |
61 | 38.3k | } |
62 | | |
63 | | void |
64 | | FuzzHelper::doChecks() |
65 | 282k | { |
66 | | // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during |
67 | | // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before |
68 | | // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally |
69 | | // occur legitimately and therefore must be allowed during normal operations. |
70 | 282k | Pl_DCT::setMemoryLimit(100'000'000); |
71 | 282k | Pl_DCT::setScanLimit(50); |
72 | | |
73 | 282k | Pl_PNGFilter::setMemoryLimit(1'000'000); |
74 | 282k | Pl_RunLength::setMemoryLimit(1'000'000); |
75 | 282k | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
76 | 282k | Pl_Flate::memory_limit(200'000); |
77 | | |
78 | | // Do not decompress corrupt data. This may cause extended runtime within jpeglib without |
79 | | // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts. |
80 | 282k | Pl_DCT::setThrowOnCorruptData(true); |
81 | | |
82 | | // Get as much coverage as possible in parts of the library that |
83 | | // might benefit from fuzzing. |
84 | 282k | std::cerr << "\ninfo: starting testOutlines\n"; |
85 | 282k | testOutlines(); |
86 | 282k | } |
87 | | |
88 | | void |
89 | | FuzzHelper::run() |
90 | 262k | { |
91 | | // The goal here is that you should be able to throw anything at |
92 | | // libqpdf and it will respond without any memory errors and never |
93 | | // do anything worse than throwing a QPDFExc or |
94 | | // std::runtime_error. Throwing any other kind of exception, |
95 | | // segfaulting, or having a memory error (when built with |
96 | | // appropriate sanitizers) will all cause abnormal exit. |
97 | 262k | try { |
98 | 262k | doChecks(); |
99 | 262k | } catch (QPDFExc const& e) { |
100 | 173k | std::cerr << "QPDFExc: " << e.what() << '\n'; |
101 | 173k | } catch (std::runtime_error const& e) { |
102 | 708 | std::cerr << "runtime_error: " << e.what() << '\n'; |
103 | 708 | } |
104 | 262k | } |
105 | | |
106 | | extern "C" int |
107 | | LLVMFuzzerTestOneInput(unsigned char const* data, size_t size) |
108 | 298k | { |
109 | 298k | #ifndef _WIN32 |
110 | | // Used by jpeg library to work around false positives in memory |
111 | | // sanitizer. |
112 | 298k | setenv("JSIMD_FORCENONE", "1", 1); |
113 | 298k | #endif |
114 | 298k | FuzzHelper f(data, size); |
115 | 298k | f.run(); |
116 | 298k | return 0; |
117 | 298k | } |