Commit f5cac93ac63559ddc0aec1b1c61c9e2503c7b8e0
Committed by
GitHub
Merge pull request #1349 from m-holger/fuzz
Fix bugs found during fuzzing
Showing
17 changed files
with
44 additions
and
4 deletions
fuzz/CMakeLists.txt
| @@ -147,10 +147,13 @@ set(CORPUS_OTHER | @@ -147,10 +147,13 @@ set(CORPUS_OTHER | ||
| 147 | 369662293.fuzz | 147 | 369662293.fuzz |
| 148 | 369662293a.fuzz | 148 | 369662293a.fuzz |
| 149 | 376305073.fuzz | 149 | 376305073.fuzz |
| 150 | + 376305073a.fuzz | ||
| 150 | 377977949.fuzz | 151 | 377977949.fuzz |
| 152 | + 388571629.fuzz | ||
| 151 | 389339260.fuzz | 153 | 389339260.fuzz |
| 152 | 389974979.fuzz | 154 | 389974979.fuzz |
| 153 | 391974927.fuzz | 155 | 391974927.fuzz |
| 156 | + 394129398.fuzz | ||
| 154 | ) | 157 | ) |
| 155 | 158 | ||
| 156 | set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) | 159 | set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) |
fuzz/qpdf_crypt_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFPageObjectHelper.hh> | 10 | #include <qpdf/QPDFPageObjectHelper.hh> |
| @@ -108,6 +109,7 @@ FuzzHelper::doChecks() | @@ -108,6 +109,7 @@ FuzzHelper::doChecks() | ||
| 108 | Pl_DCT::setScanLimit(50); | 109 | Pl_DCT::setScanLimit(50); |
| 109 | 110 | ||
| 110 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 111 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 112 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 111 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 113 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 112 | Pl_Flate::setMemoryLimit(200'000); | 114 | Pl_Flate::setMemoryLimit(200'000); |
| 113 | 115 |
fuzz/qpdf_crypt_insecure_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFPageObjectHelper.hh> | 10 | #include <qpdf/QPDFPageObjectHelper.hh> |
| @@ -108,6 +109,7 @@ FuzzHelper::doChecks() | @@ -108,6 +109,7 @@ FuzzHelper::doChecks() | ||
| 108 | Pl_DCT::setScanLimit(50); | 109 | Pl_DCT::setScanLimit(50); |
| 109 | 110 | ||
| 110 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 111 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 112 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 111 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 113 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 112 | Pl_Flate::setMemoryLimit(200'000); | 114 | Pl_Flate::setMemoryLimit(200'000); |
| 113 | 115 |
fuzz/qpdf_extra/376305073a.fuzz
0 โ 100644
No preview for this file type
fuzz/qpdf_extra/388571629.fuzz
0 โ 100644
No preview for this file type
fuzz/qpdf_extra/394129398.fuzz
0 โ 100644
No preview for this file type
fuzz/qpdf_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFPageObjectHelper.hh> | 10 | #include <qpdf/QPDFPageObjectHelper.hh> |
| @@ -106,6 +107,7 @@ FuzzHelper::doChecks() | @@ -106,6 +107,7 @@ FuzzHelper::doChecks() | ||
| 106 | Pl_DCT::setScanLimit(50); | 107 | Pl_DCT::setScanLimit(50); |
| 107 | 108 | ||
| 108 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 109 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 110 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 109 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 111 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 110 | Pl_Flate::setMemoryLimit(200'000); | 112 | Pl_Flate::setMemoryLimit(200'000); |
| 111 | 113 |
fuzz/qpdf_lin_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFPageObjectHelper.hh> | 10 | #include <qpdf/QPDFPageObjectHelper.hh> |
| @@ -107,6 +108,7 @@ FuzzHelper::doChecks() | @@ -107,6 +108,7 @@ FuzzHelper::doChecks() | ||
| 107 | Pl_DCT::setScanLimit(50); | 108 | Pl_DCT::setScanLimit(50); |
| 108 | 109 | ||
| 109 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 110 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 111 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 110 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 112 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 111 | Pl_Flate::setMemoryLimit(200'000); | 113 | Pl_Flate::setMemoryLimit(200'000); |
| 112 | 114 |
fuzz/qpdf_outlines_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFOutlineDocumentHelper.hh> | 10 | #include <qpdf/QPDFOutlineDocumentHelper.hh> |
| @@ -84,6 +85,7 @@ FuzzHelper::doChecks() | @@ -84,6 +85,7 @@ FuzzHelper::doChecks() | ||
| 84 | Pl_DCT::setScanLimit(50); | 85 | Pl_DCT::setScanLimit(50); |
| 85 | 86 | ||
| 86 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 87 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 88 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 87 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 89 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 88 | Pl_Flate::setMemoryLimit(200'000); | 90 | Pl_Flate::setMemoryLimit(200'000); |
| 89 | 91 |
fuzz/qpdf_pages_fuzzer.cc
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | #include <qpdf/Pl_Discard.hh> | 4 | #include <qpdf/Pl_Discard.hh> |
| 5 | #include <qpdf/Pl_Flate.hh> | 5 | #include <qpdf/Pl_Flate.hh> |
| 6 | #include <qpdf/Pl_PNGFilter.hh> | 6 | #include <qpdf/Pl_PNGFilter.hh> |
| 7 | +#include <qpdf/Pl_RunLength.hh> | ||
| 7 | #include <qpdf/Pl_TIFFPredictor.hh> | 8 | #include <qpdf/Pl_TIFFPredictor.hh> |
| 8 | #include <qpdf/QPDF.hh> | 9 | #include <qpdf/QPDF.hh> |
| 9 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> | 10 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| @@ -105,6 +106,7 @@ FuzzHelper::doChecks() | @@ -105,6 +106,7 @@ FuzzHelper::doChecks() | ||
| 105 | Pl_DCT::setScanLimit(50); | 106 | Pl_DCT::setScanLimit(50); |
| 106 | 107 | ||
| 107 | Pl_PNGFilter::setMemoryLimit(1'000'000); | 108 | Pl_PNGFilter::setMemoryLimit(1'000'000); |
| 109 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 108 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); | 110 | Pl_TIFFPredictor::setMemoryLimit(1'000'000); |
| 109 | Pl_Flate::setMemoryLimit(200'000); | 111 | Pl_Flate::setMemoryLimit(200'000); |
| 110 | 112 |
fuzz/qtest/fuzz.test
| @@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz'); | @@ -11,7 +11,7 @@ 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 = 88; # increment when adding new files | 14 | +my $n_qpdf_files = 91; # increment when adding new files |
| 15 | 15 | ||
| 16 | my @fuzzers = ( | 16 | my @fuzzers = ( |
| 17 | ['ascii85' => 1], | 17 | ['ascii85' => 1], |
fuzz/runlength_fuzzer.cc
| @@ -25,6 +25,7 @@ FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) : | @@ -25,6 +25,7 @@ FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) : | ||
| 25 | void | 25 | void |
| 26 | FuzzHelper::doChecks() | 26 | FuzzHelper::doChecks() |
| 27 | { | 27 | { |
| 28 | + Pl_RunLength::setMemoryLimit(1'000'000); | ||
| 28 | Pl_Discard discard; | 29 | Pl_Discard discard; |
| 29 | Pl_RunLength p("decode", &discard, Pl_RunLength::a_decode); | 30 | Pl_RunLength p("decode", &discard, Pl_RunLength::a_decode); |
| 30 | p.write(const_cast<unsigned char*>(data), size); | 31 | p.write(const_cast<unsigned char*>(data), size); |
include/qpdf/Pl_Flate.hh
| @@ -46,7 +46,7 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline | @@ -46,7 +46,7 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline | ||
| 46 | ~Pl_Flate() override; | 46 | ~Pl_Flate() override; |
| 47 | 47 | ||
| 48 | // Limit the memory used. | 48 | // Limit the memory used. |
| 49 | - // NB This is a static option affecting all Pl_PNGFilter instances. | 49 | + // NB This is a static option affecting all Pl_Flate instances. |
| 50 | QPDF_DLL | 50 | QPDF_DLL |
| 51 | static void setMemoryLimit(unsigned long long limit); | 51 | static void setMemoryLimit(unsigned long long limit); |
| 52 | 52 |
include/qpdf/Pl_RunLength.hh
| @@ -32,6 +32,11 @@ class QPDF_DLL_CLASS Pl_RunLength: public Pipeline | @@ -32,6 +32,11 @@ class QPDF_DLL_CLASS Pl_RunLength: public Pipeline | ||
| 32 | QPDF_DLL | 32 | QPDF_DLL |
| 33 | ~Pl_RunLength() override; | 33 | ~Pl_RunLength() override; |
| 34 | 34 | ||
| 35 | + // Limit the memory used. | ||
| 36 | + // NB This is a static option affecting all Pl_RunLength instances. | ||
| 37 | + QPDF_DLL | ||
| 38 | + static void setMemoryLimit(unsigned long long limit); | ||
| 39 | + | ||
| 35 | QPDF_DLL | 40 | QPDF_DLL |
| 36 | void write(unsigned char const* data, size_t len) override; | 41 | void write(unsigned char const* data, size_t len) override; |
| 37 | QPDF_DLL | 42 | QPDF_DLL |
libqpdf/Pl_RunLength.cc
| @@ -3,6 +3,11 @@ | @@ -3,6 +3,11 @@ | ||
| 3 | #include <qpdf/QTC.hh> | 3 | #include <qpdf/QTC.hh> |
| 4 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 5 | 5 | ||
| 6 | +namespace | ||
| 7 | +{ | ||
| 8 | + unsigned long long memory_limit{0}; | ||
| 9 | +} // namespace | ||
| 10 | + | ||
| 6 | Pl_RunLength::Members::Members(action_e action) : | 11 | Pl_RunLength::Members::Members(action_e action) : |
| 7 | action(action) | 12 | action(action) |
| 8 | { | 13 | { |
| @@ -17,6 +22,12 @@ Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e acti | @@ -17,6 +22,12 @@ Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e acti | ||
| 17 | } | 22 | } |
| 18 | } | 23 | } |
| 19 | 24 | ||
| 25 | +void | ||
| 26 | +Pl_RunLength::setMemoryLimit(unsigned long long limit) | ||
| 27 | +{ | ||
| 28 | + memory_limit = limit; | ||
| 29 | +} | ||
| 30 | + | ||
| 20 | Pl_RunLength::~Pl_RunLength() // NOLINT (modernize-use-equals-default) | 31 | Pl_RunLength::~Pl_RunLength() // NOLINT (modernize-use-equals-default) |
| 21 | { | 32 | { |
| 22 | // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer | 33 | // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer |
| @@ -67,6 +78,9 @@ Pl_RunLength::encode(unsigned char const* data, size_t len) | @@ -67,6 +78,9 @@ Pl_RunLength::encode(unsigned char const* data, size_t len) | ||
| 67 | void | 78 | void |
| 68 | Pl_RunLength::decode(unsigned char const* data, size_t len) | 79 | Pl_RunLength::decode(unsigned char const* data, size_t len) |
| 69 | { | 80 | { |
| 81 | + if (memory_limit && (len + m->out.size()) > memory_limit) { | ||
| 82 | + throw std::runtime_error("Pl_RunLength memory limit exceeded"); | ||
| 83 | + } | ||
| 70 | m->out.reserve(len); | 84 | m->out.reserve(len); |
| 71 | for (size_t i = 0; i < len; ++i) { | 85 | for (size_t i = 0; i < len; ++i) { |
| 72 | unsigned char const& ch = data[i]; | 86 | unsigned char const& ch = data[i]; |
libqpdf/QPDFParser.cc
| @@ -470,6 +470,11 @@ bool | @@ -470,6 +470,11 @@ bool | ||
| 470 | QPDFParser::tooManyBadTokens() | 470 | QPDFParser::tooManyBadTokens() |
| 471 | { | 471 | { |
| 472 | if (--max_bad_count > 0 && good_count > 4) { | 472 | if (--max_bad_count > 0 && good_count > 4) { |
| 473 | + if (frame->olist.size() > 100'000 || frame->dict.size() > 100'000) { | ||
| 474 | + warn("encountered errors while parsing an array or dictionary with more than 100000 " | ||
| 475 | + "elements; giving up on reading object"); | ||
| 476 | + return true; | ||
| 477 | + } | ||
| 473 | good_count = 0; | 478 | good_count = 0; |
| 474 | bad_count = 1; | 479 | bad_count = 1; |
| 475 | return false; | 480 | return false; |
libqpdf/QPDFWriter.cc
| @@ -1877,8 +1877,8 @@ QPDFWriter::generateID() | @@ -1877,8 +1877,8 @@ QPDFWriter::generateID() | ||
| 1877 | if (m->deterministic_id_data.empty()) { | 1877 | if (m->deterministic_id_data.empty()) { |
| 1878 | QTC::TC("qpdf", "QPDFWriter deterministic with no data"); | 1878 | QTC::TC("qpdf", "QPDFWriter deterministic with no data"); |
| 1879 | throw std::runtime_error("INTERNAL ERROR: QPDFWriter::generateID has no data for " | 1879 | throw std::runtime_error("INTERNAL ERROR: QPDFWriter::generateID has no data for " |
| 1880 | - "deterministic ID. This may happen if deterministic ID and " | ||
| 1881 | - "file encryption are requested together."); | 1880 | + "deterministic ID. This may happen if deterministic ID " |
| 1881 | + "and file encryption are requested together."); | ||
| 1882 | } | 1882 | } |
| 1883 | seed += m->deterministic_id_data; | 1883 | seed += m->deterministic_id_data; |
| 1884 | } else { | 1884 | } else { |