Commit d8d73679e71942c4d53e6f8826aa5b51eaa57137

Authored by m-holger
1 parent c1377176

Split qpdf_fuzzer into six separate fuzzers

fuzz/CMakeLists.txt
@@ -3,6 +3,11 @@ @@ -3,6 +3,11 @@
3 3
4 set(FUZZERS 4 set(FUZZERS
5 qpdf_fuzzer 5 qpdf_fuzzer
  6 + qpdf_crypt_fuzzer
  7 + qpdf_crypt_insecure_fuzzer
  8 + qpdf_lin_fuzzer
  9 + qpdf_pages_fuzzer
  10 + qpdf_outlines_fuzzer
6 ascii85_fuzzer 11 ascii85_fuzzer
7 dct_fuzzer 12 dct_fuzzer
8 flate_fuzzer 13 flate_fuzzer
@@ -174,7 +179,7 @@ add_test( @@ -174,7 +179,7 @@ add_test(
174 if(OSS_FUZZ) 179 if(OSS_FUZZ)
175 list(APPEND SEED_CORPUS_ZIPS) 180 list(APPEND SEED_CORPUS_ZIPS)
176 foreach(F ${FUZZERS}) 181 foreach(F ${FUZZERS})
177 - if(F STREQUAL qpdf_fuzzer) 182 + if((F STRGREATER qpdf_) AND (F STRLESS qpdg))
178 set(SEED_DIR ${CORPUS_DIR}) 183 set(SEED_DIR ${CORPUS_DIR})
179 else() 184 else()
180 set(SEED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${F}_seed_corpus) 185 set(SEED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${F}_seed_corpus)
fuzz/qpdf_crypt_fuzzer.cc 0 → 100644
  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_TIFFPredictor.hh>
  8 +#include <qpdf/QPDF.hh>
  9 +#include <qpdf/QPDFPageObjectHelper.hh>
  10 +#include <qpdf/QPDFWriter.hh>
  11 +#include <qpdf/QUtil.hh>
  12 +
  13 +#include <cstdlib>
  14 +
  15 +class DiscardContents: public QPDFObjectHandle::ParserCallbacks
  16 +{
  17 + public:
  18 + ~DiscardContents() override = default;
  19 + void
  20 + handleObject(QPDFObjectHandle) override
  21 + {
  22 + }
  23 + void
  24 + handleEOF() override
  25 + {
  26 + }
  27 +};
  28 +
  29 +class FuzzHelper
  30 +{
  31 + public:
  32 + FuzzHelper(unsigned char const* data, size_t size);
  33 + void run();
  34 +
  35 + private:
  36 + std::shared_ptr<QPDF> getQpdf();
  37 + std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>);
  38 + void doWrite(std::shared_ptr<QPDFWriter> w);
  39 + void testWrite();
  40 + void doChecks();
  41 +
  42 + Buffer input_buffer;
  43 + Pl_Discard discard;
  44 +};
  45 +
  46 +FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
  47 + // We do not modify data, so it is safe to remove the const for Buffer
  48 + input_buffer(const_cast<unsigned char*>(data), size)
  49 +{
  50 +}
  51 +
  52 +std::shared_ptr<QPDF>
  53 +FuzzHelper::getQpdf()
  54 +{
  55 + auto is =
  56 + std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
  57 + auto qpdf = QPDF::create();
  58 + qpdf->setMaxWarnings(200);
  59 + qpdf->processInputSource(is);
  60 + return qpdf;
  61 +}
  62 +
  63 +std::shared_ptr<QPDFWriter>
  64 +FuzzHelper::getWriter(std::shared_ptr<QPDF> qpdf)
  65 +{
  66 + auto w = std::make_shared<QPDFWriter>(*qpdf);
  67 + w->setOutputPipeline(&this->discard);
  68 + w->setDecodeLevel(qpdf_dl_all);
  69 + return w;
  70 +}
  71 +
  72 +void
  73 +FuzzHelper::doWrite(std::shared_ptr<QPDFWriter> w)
  74 +{
  75 + try {
  76 + w->write();
  77 + } catch (QPDFExc const& e) {
  78 + std::cerr << e.what() << std::endl;
  79 + } catch (std::runtime_error const& e) {
  80 + std::cerr << e.what() << std::endl;
  81 + }
  82 +}
  83 +
  84 +void
  85 +FuzzHelper::testWrite()
  86 +{
  87 + // Write in various ways to exercise QPDFWriter
  88 +
  89 + std::shared_ptr<QPDF> q;
  90 + std::shared_ptr<QPDFWriter> w;
  91 +
  92 + q = getQpdf();
  93 + w = getWriter(q);
  94 + w->setStaticID(true);
  95 + w->setLinearization(true);
  96 + w->setR6EncryptionParameters("u", "o", true, true, true, true, true, true, qpdf_r3p_full, true);
  97 + doWrite(w);
  98 +}
  99 +
  100 +void
  101 +FuzzHelper::doChecks()
  102 +{
  103 + // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
  104 + // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
  105 + // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
  106 + // occur legitimately and therefore must be allowed during normal operations.
  107 + Pl_DCT::setMemoryLimit(100'000'000);
  108 + Pl_DCT::setScanLimit(50);
  109 +
  110 + Pl_PNGFilter::setMemoryLimit(1'000'000);
  111 + Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  112 + Pl_Flate::setMemoryLimit(1'000'000);
  113 +
  114 + // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
  115 + // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
  116 + Pl_DCT::setThrowOnCorruptData(true);
  117 +
  118 + // Get as much coverage as possible in parts of the library that
  119 + // might benefit from fuzzing.
  120 + std::cerr << "\ninfo: starting testWrite\n";
  121 + testWrite();
  122 +}
  123 +
  124 +void
  125 +FuzzHelper::run()
  126 +{
  127 + // The goal here is that you should be able to throw anything at
  128 + // libqpdf and it will respond without any memory errors and never
  129 + // do anything worse than throwing a QPDFExc or
  130 + // std::runtime_error. Throwing any other kind of exception,
  131 + // segfaulting, or having a memory error (when built with
  132 + // appropriate sanitizers) will all cause abnormal exit.
  133 + try {
  134 + doChecks();
  135 + } catch (QPDFExc const& e) {
  136 + std::cerr << "QPDFExc: " << e.what() << std::endl;
  137 + } catch (std::runtime_error const& e) {
  138 + std::cerr << "runtime_error: " << e.what() << std::endl;
  139 + }
  140 +}
  141 +
  142 +extern "C" int
  143 +LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
  144 +{
  145 +#ifndef _WIN32
  146 + // Used by jpeg library to work around false positives in memory
  147 + // sanitizer.
  148 + setenv("JSIMD_FORCENONE", "1", 1);
  149 +#endif
  150 + FuzzHelper f(data, size);
  151 + f.run();
  152 + return 0;
  153 +}
fuzz/qpdf_crypt_fuzzer.options 0 → 100644
  1 +[libfuzzer]
  2 +dict = pdf.dict
fuzz/qpdf_crypt_insecure_fuzzer.cc 0 → 100644
  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_TIFFPredictor.hh>
  8 +#include <qpdf/QPDF.hh>
  9 +#include <qpdf/QPDFPageObjectHelper.hh>
  10 +#include <qpdf/QPDFWriter.hh>
  11 +#include <qpdf/QUtil.hh>
  12 +#include <cstdlib>
  13 +
  14 +class DiscardContents: public QPDFObjectHandle::ParserCallbacks
  15 +{
  16 + public:
  17 + ~DiscardContents() override = default;
  18 + void
  19 + handleObject(QPDFObjectHandle) override
  20 + {
  21 + }
  22 + void
  23 + handleEOF() override
  24 + {
  25 + }
  26 +};
  27 +
  28 +class FuzzHelper
  29 +{
  30 + public:
  31 + FuzzHelper(unsigned char const* data, size_t size);
  32 + void run();
  33 +
  34 + private:
  35 + std::shared_ptr<QPDF> getQpdf();
  36 + std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>);
  37 + void doWrite(std::shared_ptr<QPDFWriter> w);
  38 + void testWrite();
  39 + void doChecks();
  40 +
  41 + Buffer input_buffer;
  42 + Pl_Discard discard;
  43 +};
  44 +
  45 +FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
  46 + // We do not modify data, so it is safe to remove the const for Buffer
  47 + input_buffer(const_cast<unsigned char*>(data), size)
  48 +{
  49 +}
  50 +
  51 +std::shared_ptr<QPDF>
  52 +FuzzHelper::getQpdf()
  53 +{
  54 + auto is =
  55 + std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
  56 + auto qpdf = QPDF::create();
  57 + qpdf->setMaxWarnings(200);
  58 + qpdf->processInputSource(is);
  59 + return qpdf;
  60 +}
  61 +
  62 +std::shared_ptr<QPDFWriter>
  63 +FuzzHelper::getWriter(std::shared_ptr<QPDF> qpdf)
  64 +{
  65 + auto w = std::make_shared<QPDFWriter>(*qpdf);
  66 + w->setOutputPipeline(&this->discard);
  67 + w->setDecodeLevel(qpdf_dl_all);
  68 + return w;
  69 +}
  70 +
  71 +void
  72 +FuzzHelper::doWrite(std::shared_ptr<QPDFWriter> w)
  73 +{
  74 + try {
  75 + w->write();
  76 + } catch (QPDFExc const& e) {
  77 + std::cerr << e.what() << std::endl;
  78 + } catch (std::runtime_error const& e) {
  79 + std::cerr << e.what() << std::endl;
  80 + }
  81 +}
  82 +
  83 +void
  84 +FuzzHelper::testWrite()
  85 +{
  86 + // Write in various ways to exercise QPDFWriter
  87 +
  88 + std::shared_ptr<QPDF> q;
  89 + std::shared_ptr<QPDFWriter> w;
  90 +
  91 + q = getQpdf();
  92 + w = getWriter(q);
  93 + w->setStaticID(true);
  94 + w->setObjectStreamMode(qpdf_o_disable);
  95 + w->setR3EncryptionParametersInsecure(
  96 + "u", "o", true, true, true, true, true, true, qpdf_r3p_full);
  97 + doWrite(w);
  98 +}
  99 +
  100 +void
  101 +FuzzHelper::doChecks()
  102 +{
  103 + // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
  104 + // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
  105 + // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
  106 + // occur legitimately and therefore must be allowed during normal operations.
  107 + Pl_DCT::setMemoryLimit(100'000'000);
  108 + Pl_DCT::setScanLimit(50);
  109 +
  110 + Pl_PNGFilter::setMemoryLimit(1'000'000);
  111 + Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  112 + Pl_Flate::setMemoryLimit(1'000'000);
  113 +
  114 + // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
  115 + // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
  116 + Pl_DCT::setThrowOnCorruptData(true);
  117 +
  118 + // Get as much coverage as possible in parts of the library that
  119 + // might benefit from fuzzing.
  120 + std::cerr << "\ninfo: starting testWrite\n";
  121 + testWrite();
  122 +}
  123 +
  124 +void
  125 +FuzzHelper::run()
  126 +{
  127 + // The goal here is that you should be able to throw anything at
  128 + // libqpdf and it will respond without any memory errors and never
  129 + // do anything worse than throwing a QPDFExc or
  130 + // std::runtime_error. Throwing any other kind of exception,
  131 + // segfaulting, or having a memory error (when built with
  132 + // appropriate sanitizers) will all cause abnormal exit.
  133 + try {
  134 + doChecks();
  135 + } catch (QPDFExc const& e) {
  136 + std::cerr << "QPDFExc: " << e.what() << std::endl;
  137 + } catch (std::runtime_error const& e) {
  138 + std::cerr << "runtime_error: " << e.what() << std::endl;
  139 + }
  140 +}
  141 +
  142 +extern "C" int
  143 +LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
  144 +{
  145 +#ifndef _WIN32
  146 + // Used by jpeg library to work around false positives in memory
  147 + // sanitizer.
  148 + setenv("JSIMD_FORCENONE", "1", 1);
  149 +#endif
  150 + FuzzHelper f(data, size);
  151 + f.run();
  152 + return 0;
  153 +}
fuzz/qpdf_crypt_insecure_fuzzer.options 0 → 100644
  1 +[libfuzzer]
  2 +dict = pdf.dict
fuzz/qpdf_fuzzer.cc
@@ -6,10 +6,6 @@ @@ -6,10 +6,6 @@
6 #include <qpdf/Pl_PNGFilter.hh> 6 #include <qpdf/Pl_PNGFilter.hh>
7 #include <qpdf/Pl_TIFFPredictor.hh> 7 #include <qpdf/Pl_TIFFPredictor.hh>
8 #include <qpdf/QPDF.hh> 8 #include <qpdf/QPDF.hh>
9 -#include <qpdf/QPDFAcroFormDocumentHelper.hh>  
10 -#include <qpdf/QPDFOutlineDocumentHelper.hh>  
11 -#include <qpdf/QPDFPageDocumentHelper.hh>  
12 -#include <qpdf/QPDFPageLabelDocumentHelper.hh>  
13 #include <qpdf/QPDFPageObjectHelper.hh> 9 #include <qpdf/QPDFPageObjectHelper.hh>
14 #include <qpdf/QPDFWriter.hh> 10 #include <qpdf/QPDFWriter.hh>
15 #include <qpdf/QUtil.hh> 11 #include <qpdf/QUtil.hh>
@@ -40,8 +36,6 @@ class FuzzHelper @@ -40,8 +36,6 @@ class FuzzHelper
40 std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>); 36 std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>);
41 void doWrite(std::shared_ptr<QPDFWriter> w); 37 void doWrite(std::shared_ptr<QPDFWriter> w);
42 void testWrite(); 38 void testWrite();
43 - void testPages();  
44 - void testOutlines();  
45 void doChecks(); 39 void doChecks();
46 40
47 Buffer input_buffer; 41 Buffer input_buffer;
@@ -106,71 +100,6 @@ FuzzHelper::testWrite() @@ -106,71 +100,6 @@ FuzzHelper::testWrite()
106 w->setLinearization(true); 100 w->setLinearization(true);
107 w->setR6EncryptionParameters("u", "o", true, true, true, true, true, true, qpdf_r3p_full, true); 101 w->setR6EncryptionParameters("u", "o", true, true, true, true, true, true, qpdf_r3p_full, true);
108 doWrite(w); 102 doWrite(w);
109 -  
110 - q = getQpdf();  
111 - w = getWriter(q);  
112 - w->setStaticID(true);  
113 - w->setObjectStreamMode(qpdf_o_disable);  
114 - w->setR3EncryptionParametersInsecure(  
115 - "u", "o", true, true, true, true, true, true, qpdf_r3p_full);  
116 - doWrite(w);  
117 -  
118 - q = getQpdf();  
119 - w = getWriter(q);  
120 - w->setDeterministicID(true);  
121 - w->setObjectStreamMode(qpdf_o_generate);  
122 - w->setLinearization(true);  
123 - doWrite(w);  
124 -}  
125 -  
126 -void  
127 -FuzzHelper::testPages()  
128 -{  
129 - // Parse all content streams, and exercise some helpers that  
130 - // operate on pages.  
131 - std::shared_ptr<QPDF> q = getQpdf();  
132 - QPDFPageDocumentHelper pdh(*q);  
133 - QPDFPageLabelDocumentHelper pldh(*q);  
134 - QPDFOutlineDocumentHelper odh(*q);  
135 - QPDFAcroFormDocumentHelper afdh(*q);  
136 - afdh.generateAppearancesIfNeeded();  
137 - pdh.flattenAnnotations();  
138 - DiscardContents discard_contents;  
139 - int pageno = 0;  
140 - for (auto& page: pdh.getAllPages()) {  
141 - ++pageno;  
142 - try {  
143 - page.coalesceContentStreams();  
144 - page.parseContents(&discard_contents);  
145 - page.getImages();  
146 - pldh.getLabelForPage(pageno);  
147 - QPDFObjectHandle page_obj(page.getObjectHandle());  
148 - page_obj.getJSON(JSON::LATEST, true).unparse();  
149 - odh.getOutlinesForPage(page_obj.getObjGen());  
150 -  
151 - for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) {  
152 - afdh.getFieldForAnnotation(aoh);  
153 - }  
154 - } catch (QPDFExc& e) {  
155 - std::cerr << "page " << pageno << ": " << e.what() << std::endl;  
156 - }  
157 - }  
158 -}  
159 -  
160 -void  
161 -FuzzHelper::testOutlines()  
162 -{  
163 - std::shared_ptr<QPDF> q = getQpdf();  
164 - std::list<std::vector<QPDFOutlineObjectHelper>> queue;  
165 - QPDFOutlineDocumentHelper odh(*q);  
166 - queue.push_back(odh.getTopLevelOutlines());  
167 - while (!queue.empty()) {  
168 - for (auto& ol: *(queue.begin())) {  
169 - ol.getDestPage();  
170 - queue.push_back(ol.getKids());  
171 - }  
172 - queue.pop_front();  
173 - }  
174 } 103 }
175 104
176 void 105 void
@@ -195,10 +124,6 @@ FuzzHelper::doChecks() @@ -195,10 +124,6 @@ FuzzHelper::doChecks()
195 // might benefit from fuzzing. 124 // might benefit from fuzzing.
196 std::cerr << "\ninfo: starting testWrite\n"; 125 std::cerr << "\ninfo: starting testWrite\n";
197 testWrite(); 126 testWrite();
198 - std::cerr << "\ninfo: starting testPages\n";  
199 - testPages();  
200 - std::cerr << "\ninfo: starting testOutlines\n";  
201 - testOutlines();  
202 } 127 }
203 128
204 void 129 void
fuzz/qpdf_lin_fuzzer.cc 0 → 100644
  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_TIFFPredictor.hh>
  8 +#include <qpdf/QPDF.hh>
  9 +#include <qpdf/QPDFPageObjectHelper.hh>
  10 +#include <qpdf/QPDFWriter.hh>
  11 +#include <qpdf/QUtil.hh>
  12 +#include <cstdlib>
  13 +
  14 +class DiscardContents: public QPDFObjectHandle::ParserCallbacks
  15 +{
  16 + public:
  17 + ~DiscardContents() override = default;
  18 + void
  19 + handleObject(QPDFObjectHandle) override
  20 + {
  21 + }
  22 + void
  23 + handleEOF() override
  24 + {
  25 + }
  26 +};
  27 +
  28 +class FuzzHelper
  29 +{
  30 + public:
  31 + FuzzHelper(unsigned char const* data, size_t size);
  32 + void run();
  33 +
  34 + private:
  35 + std::shared_ptr<QPDF> getQpdf();
  36 + std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>);
  37 + void doWrite(std::shared_ptr<QPDFWriter> w);
  38 + void testWrite();
  39 + void doChecks();
  40 +
  41 + Buffer input_buffer;
  42 + Pl_Discard discard;
  43 +};
  44 +
  45 +FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
  46 + // We do not modify data, so it is safe to remove the const for Buffer
  47 + input_buffer(const_cast<unsigned char*>(data), size)
  48 +{
  49 +}
  50 +
  51 +std::shared_ptr<QPDF>
  52 +FuzzHelper::getQpdf()
  53 +{
  54 + auto is =
  55 + std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
  56 + auto qpdf = QPDF::create();
  57 + qpdf->setMaxWarnings(200);
  58 + qpdf->processInputSource(is);
  59 + return qpdf;
  60 +}
  61 +
  62 +std::shared_ptr<QPDFWriter>
  63 +FuzzHelper::getWriter(std::shared_ptr<QPDF> qpdf)
  64 +{
  65 + auto w = std::make_shared<QPDFWriter>(*qpdf);
  66 + w->setOutputPipeline(&this->discard);
  67 + w->setDecodeLevel(qpdf_dl_all);
  68 + return w;
  69 +}
  70 +
  71 +void
  72 +FuzzHelper::doWrite(std::shared_ptr<QPDFWriter> w)
  73 +{
  74 + try {
  75 + w->write();
  76 + } catch (QPDFExc const& e) {
  77 + std::cerr << e.what() << std::endl;
  78 + } catch (std::runtime_error const& e) {
  79 + std::cerr << e.what() << std::endl;
  80 + }
  81 +}
  82 +
  83 +void
  84 +FuzzHelper::testWrite()
  85 +{
  86 + // Write in various ways to exercise QPDFWriter
  87 +
  88 + std::shared_ptr<QPDF> q;
  89 + std::shared_ptr<QPDFWriter> w;
  90 +
  91 + q = getQpdf();
  92 + w = getWriter(q);
  93 + w->setDeterministicID(true);
  94 + w->setObjectStreamMode(qpdf_o_generate);
  95 + w->setLinearization(true);
  96 + doWrite(w);
  97 +}
  98 +
  99 +void
  100 +FuzzHelper::doChecks()
  101 +{
  102 + // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
  103 + // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
  104 + // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
  105 + // occur legitimately and therefore must be allowed during normal operations.
  106 + Pl_DCT::setMemoryLimit(100'000'000);
  107 + Pl_DCT::setScanLimit(50);
  108 +
  109 + Pl_PNGFilter::setMemoryLimit(1'000'000);
  110 + Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  111 + Pl_Flate::setMemoryLimit(1'000'000);
  112 +
  113 + // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
  114 + // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
  115 + Pl_DCT::setThrowOnCorruptData(true);
  116 +
  117 + // Get as much coverage as possible in parts of the library that
  118 + // might benefit from fuzzing.
  119 + std::cerr << "\ninfo: starting testWrite\n";
  120 + testWrite();
  121 +}
  122 +
  123 +void
  124 +FuzzHelper::run()
  125 +{
  126 + // The goal here is that you should be able to throw anything at
  127 + // libqpdf and it will respond without any memory errors and never
  128 + // do anything worse than throwing a QPDFExc or
  129 + // std::runtime_error. Throwing any other kind of exception,
  130 + // segfaulting, or having a memory error (when built with
  131 + // appropriate sanitizers) will all cause abnormal exit.
  132 + try {
  133 + doChecks();
  134 + } catch (QPDFExc const& e) {
  135 + std::cerr << "QPDFExc: " << e.what() << std::endl;
  136 + } catch (std::runtime_error const& e) {
  137 + std::cerr << "runtime_error: " << e.what() << std::endl;
  138 + }
  139 +}
  140 +
  141 +extern "C" int
  142 +LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
  143 +{
  144 +#ifndef _WIN32
  145 + // Used by jpeg library to work around false positives in memory
  146 + // sanitizer.
  147 + setenv("JSIMD_FORCENONE", "1", 1);
  148 +#endif
  149 + FuzzHelper f(data, size);
  150 + f.run();
  151 + return 0;
  152 +}
fuzz/qpdf_lin_fuzzer.options 0 → 100644
  1 +[libfuzzer]
  2 +dict = pdf.dict
fuzz/qpdf_outlines_fuzzer.cc 0 → 100644
  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_TIFFPredictor.hh>
  8 +#include <qpdf/QPDF.hh>
  9 +#include <qpdf/QPDFOutlineDocumentHelper.hh>
  10 +#include <qpdf/QPDFPageObjectHelper.hh>
  11 +#include <qpdf/QUtil.hh>
  12 +#include <cstdlib>
  13 +
  14 +class DiscardContents: public QPDFObjectHandle::ParserCallbacks
  15 +{
  16 + public:
  17 + ~DiscardContents() override = default;
  18 + void
  19 + handleObject(QPDFObjectHandle) override
  20 + {
  21 + }
  22 + void
  23 + handleEOF() override
  24 + {
  25 + }
  26 +};
  27 +
  28 +class FuzzHelper
  29 +{
  30 + public:
  31 + FuzzHelper(unsigned char const* data, size_t size);
  32 + void run();
  33 +
  34 + private:
  35 + std::shared_ptr<QPDF> getQpdf();
  36 + void testOutlines();
  37 + void doChecks();
  38 +
  39 + Buffer input_buffer;
  40 + Pl_Discard discard;
  41 +};
  42 +
  43 +FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
  44 + // We do not modify data, so it is safe to remove the const for Buffer
  45 + input_buffer(const_cast<unsigned char*>(data), size)
  46 +{
  47 +}
  48 +
  49 +std::shared_ptr<QPDF>
  50 +FuzzHelper::getQpdf()
  51 +{
  52 + auto is =
  53 + std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
  54 + auto qpdf = QPDF::create();
  55 + qpdf->setMaxWarnings(200);
  56 + qpdf->processInputSource(is);
  57 + return qpdf;
  58 +}
  59 +
  60 +void
  61 +FuzzHelper::testOutlines()
  62 +{
  63 + std::shared_ptr<QPDF> q = getQpdf();
  64 + std::list<std::vector<QPDFOutlineObjectHelper>> queue;
  65 + QPDFOutlineDocumentHelper odh(*q);
  66 + queue.push_back(odh.getTopLevelOutlines());
  67 + while (!queue.empty()) {
  68 + for (auto& ol: *(queue.begin())) {
  69 + ol.getDestPage();
  70 + queue.push_back(ol.getKids());
  71 + }
  72 + queue.pop_front();
  73 + }
  74 +}
  75 +
  76 +void
  77 +FuzzHelper::doChecks()
  78 +{
  79 + // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
  80 + // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
  81 + // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
  82 + // occur legitimately and therefore must be allowed during normal operations.
  83 + Pl_DCT::setMemoryLimit(100'000'000);
  84 + Pl_DCT::setScanLimit(50);
  85 +
  86 + Pl_PNGFilter::setMemoryLimit(1'000'000);
  87 + Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  88 + Pl_Flate::setMemoryLimit(1'000'000);
  89 +
  90 + // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
  91 + // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
  92 + Pl_DCT::setThrowOnCorruptData(true);
  93 +
  94 + // Get as much coverage as possible in parts of the library that
  95 + // might benefit from fuzzing.
  96 + std::cerr << "\ninfo: starting testOutlines\n";
  97 + testOutlines();
  98 +}
  99 +
  100 +void
  101 +FuzzHelper::run()
  102 +{
  103 + // The goal here is that you should be able to throw anything at
  104 + // libqpdf and it will respond without any memory errors and never
  105 + // do anything worse than throwing a QPDFExc or
  106 + // std::runtime_error. Throwing any other kind of exception,
  107 + // segfaulting, or having a memory error (when built with
  108 + // appropriate sanitizers) will all cause abnormal exit.
  109 + try {
  110 + doChecks();
  111 + } catch (QPDFExc const& e) {
  112 + std::cerr << "QPDFExc: " << e.what() << std::endl;
  113 + } catch (std::runtime_error const& e) {
  114 + std::cerr << "runtime_error: " << e.what() << std::endl;
  115 + }
  116 +}
  117 +
  118 +extern "C" int
  119 +LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
  120 +{
  121 +#ifndef _WIN32
  122 + // Used by jpeg library to work around false positives in memory
  123 + // sanitizer.
  124 + setenv("JSIMD_FORCENONE", "1", 1);
  125 +#endif
  126 + FuzzHelper f(data, size);
  127 + f.run();
  128 + return 0;
  129 +}
fuzz/qpdf_outlines_fuzzer.options 0 → 100644
  1 +[libfuzzer]
  2 +dict = pdf.dict
fuzz/qpdf_pages_fuzzer.cc 0 → 100644
  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_TIFFPredictor.hh>
  8 +#include <qpdf/QPDF.hh>
  9 +#include <qpdf/QPDFAcroFormDocumentHelper.hh>
  10 +#include <qpdf/QPDFOutlineDocumentHelper.hh>
  11 +#include <qpdf/QPDFPageDocumentHelper.hh>
  12 +#include <qpdf/QPDFPageLabelDocumentHelper.hh>
  13 +#include <qpdf/QPDFPageObjectHelper.hh>
  14 +#include <qpdf/QUtil.hh>
  15 +#include <cstdlib>
  16 +
  17 +class DiscardContents: public QPDFObjectHandle::ParserCallbacks
  18 +{
  19 + public:
  20 + ~DiscardContents() override = default;
  21 + void
  22 + handleObject(QPDFObjectHandle) override
  23 + {
  24 + }
  25 + void
  26 + handleEOF() override
  27 + {
  28 + }
  29 +};
  30 +
  31 +class FuzzHelper
  32 +{
  33 + public:
  34 + FuzzHelper(unsigned char const* data, size_t size);
  35 + void run();
  36 +
  37 + private:
  38 + std::shared_ptr<QPDF> getQpdf();
  39 + void testPages();
  40 + void doChecks();
  41 +
  42 + Buffer input_buffer;
  43 + Pl_Discard discard;
  44 +};
  45 +
  46 +FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
  47 + // We do not modify data, so it is safe to remove the const for Buffer
  48 + input_buffer(const_cast<unsigned char*>(data), size)
  49 +{
  50 +}
  51 +
  52 +std::shared_ptr<QPDF>
  53 +FuzzHelper::getQpdf()
  54 +{
  55 + auto is =
  56 + std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
  57 + auto qpdf = QPDF::create();
  58 + qpdf->setMaxWarnings(200);
  59 + qpdf->processInputSource(is);
  60 + return qpdf;
  61 +}
  62 +
  63 +void
  64 +FuzzHelper::testPages()
  65 +{
  66 + // Parse all content streams, and exercise some helpers that
  67 + // operate on pages.
  68 + std::shared_ptr<QPDF> q = getQpdf();
  69 + QPDFPageDocumentHelper pdh(*q);
  70 + QPDFPageLabelDocumentHelper pldh(*q);
  71 + QPDFOutlineDocumentHelper odh(*q);
  72 + QPDFAcroFormDocumentHelper afdh(*q);
  73 + afdh.generateAppearancesIfNeeded();
  74 + pdh.flattenAnnotations();
  75 + DiscardContents discard_contents;
  76 + int pageno = 0;
  77 + for (auto& page: pdh.getAllPages()) {
  78 + ++pageno;
  79 + try {
  80 + page.coalesceContentStreams();
  81 + page.parseContents(&discard_contents);
  82 + page.getImages();
  83 + pldh.getLabelForPage(pageno);
  84 + QPDFObjectHandle page_obj(page.getObjectHandle());
  85 + page_obj.getJSON(JSON::LATEST, true).unparse();
  86 + odh.getOutlinesForPage(page_obj.getObjGen());
  87 +
  88 + for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) {
  89 + afdh.getFieldForAnnotation(aoh);
  90 + }
  91 + } catch (QPDFExc& e) {
  92 + std::cerr << "page " << pageno << ": " << e.what() << std::endl;
  93 + }
  94 + }
  95 +}
  96 +
  97 +void
  98 +FuzzHelper::doChecks()
  99 +{
  100 + // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
  101 + // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
  102 + // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
  103 + // occur legitimately and therefore must be allowed during normal operations.
  104 + Pl_DCT::setMemoryLimit(100'000'000);
  105 + Pl_DCT::setScanLimit(50);
  106 +
  107 + Pl_PNGFilter::setMemoryLimit(1'000'000);
  108 + Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  109 + Pl_Flate::setMemoryLimit(1'000'000);
  110 +
  111 + // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
  112 + // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
  113 + Pl_DCT::setThrowOnCorruptData(true);
  114 +
  115 + // Get as much coverage as possible in parts of the library that
  116 + // might benefit from fuzzing.
  117 + std::cerr << "\ninfo: starting testPages\n";
  118 + testPages();
  119 +}
  120 +
  121 +void
  122 +FuzzHelper::run()
  123 +{
  124 + // The goal here is that you should be able to throw anything at
  125 + // libqpdf and it will respond without any memory errors and never
  126 + // do anything worse than throwing a QPDFExc or
  127 + // std::runtime_error. Throwing any other kind of exception,
  128 + // segfaulting, or having a memory error (when built with
  129 + // appropriate sanitizers) will all cause abnormal exit.
  130 + try {
  131 + doChecks();
  132 + } catch (QPDFExc const& e) {
  133 + std::cerr << "QPDFExc: " << e.what() << std::endl;
  134 + } catch (std::runtime_error const& e) {
  135 + std::cerr << "runtime_error: " << e.what() << std::endl;
  136 + }
  137 +}
  138 +
  139 +extern "C" int
  140 +LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
  141 +{
  142 +#ifndef _WIN32
  143 + // Used by jpeg library to work around false positives in memory
  144 + // sanitizer.
  145 + setenv("JSIMD_FORCENONE", "1", 1);
  146 +#endif
  147 + FuzzHelper f(data, size);
  148 + f.run();
  149 + return 0;
  150 +}
fuzz/qpdf_pages_fuzzer.options 0 → 100644
  1 +[libfuzzer]
  2 +dict = pdf.dict
fuzz/qtest/fuzz.test
@@ -11,6 +11,8 @@ my $td = new TestDriver(&#39;fuzz&#39;); @@ -11,6 +11,8 @@ my $td = new TestDriver(&#39;fuzz&#39;);
11 11
12 my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS"; 12 my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS";
13 13
  14 +my $n_qpdf_files = 77; # increment when adding new files
  15 +
14 my @fuzzers = ( 16 my @fuzzers = (
15 ['ascii85' => 1], 17 ['ascii85' => 1],
16 ['dct' => 4], 18 ['dct' => 4],
@@ -21,7 +23,12 @@ my @fuzzers = ( @@ -21,7 +23,12 @@ my @fuzzers = (
21 ['pngpredictor' => 1], 23 ['pngpredictor' => 1],
22 ['runlength' => 6], 24 ['runlength' => 6],
23 ['tiffpredictor' => 2], 25 ['tiffpredictor' => 2],
24 - ['qpdf' => 77], # increment when adding new files 26 + ['qpdf' => $n_qpdf_files],
  27 + ['qpdf_crypt' => $n_qpdf_files],
  28 + ['qpdf_crypt_insecure' => $n_qpdf_files],
  29 + ['qpdf_lin' => $n_qpdf_files],
  30 + ['qpdf_pages' => $n_qpdf_files],
  31 + ['qpdf_outlines' => $n_qpdf_files],
25 ); 32 );
26 33
27 my $n_tests = 0; 34 my $n_tests = 0;