Commit 7f3b05ddb4bd9ff7e2da5fea17646b5fc99b45ba
Committed by
GitHub
Merge pull request #1279 from m-holger/fuzz
Split qpdf_fuzzer into six separate fuzzers
Showing
13 changed files
with
761 additions
and
77 deletions
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
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
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
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
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
fuzz/qtest/fuzz.test
| @@ -11,6 +11,8 @@ my $td = new TestDriver('fuzz'); | @@ -11,6 +11,8 @@ my $td = new TestDriver('fuzz'); | ||
| 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; |