diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 33dc2ed..0b24bc8 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -155,6 +155,7 @@ set(CORPUS_OTHER 391974927.fuzz 394129398.fuzz 394463491.fuzz + 398060137.fuzz ) set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) diff --git a/fuzz/qpdf_extra/398060137.fuzz b/fuzz/qpdf_extra/398060137.fuzz new file mode 100644 index 0000000..24fc734 --- /dev/null +++ b/fuzz/qpdf_extra/398060137.fuzz diff --git a/fuzz/qtest/fuzz.test b/fuzz/qtest/fuzz.test index eb943c5..6bde52e 100644 --- a/fuzz/qtest/fuzz.test +++ b/fuzz/qtest/fuzz.test @@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz'); my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS"; -my $n_qpdf_files = 92; # increment when adding new files +my $n_qpdf_files = 93; # increment when adding new files my @fuzzers = ( ['ascii85' => 1], diff --git a/libqpdf/QPDFParser.cc b/libqpdf/QPDFParser.cc index 529cbd9..4e9de63 100644 --- a/libqpdf/QPDFParser.cc +++ b/libqpdf/QPDFParser.cc @@ -215,6 +215,11 @@ QPDFParser::parseRemainder(bool content_stream) continue; case QPDFTokenizer::tt_array_close: + if (bad_count && !max_bad_count) { + // Trigger warning. + (void)tooManyBadTokens(); + return {QPDF_Null::create()}; + } if (frame->state == st_array) { auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100); setDescription(object, frame->offset - 1); @@ -239,6 +244,11 @@ QPDFParser::parseRemainder(bool content_stream) continue; case QPDFTokenizer::tt_dict_close: + if (bad_count && !max_bad_count) { + // Trigger warning. + (void)tooManyBadTokens(); + return {QPDF_Null::create()}; + } if (frame->state <= st_dictionary_value) { // Attempt to recover more or less gracefully from invalid dictionaries. auto& dict = frame->dict; @@ -419,6 +429,12 @@ template void QPDFParser::addScalar(Args&&... args) { + if (bad_count && (frame->olist.size() > 5'000 || frame->dict.size() > 5'000)) { + // Stop adding scalars. We are going to abort when the close token or a bad token is + // encountered. + max_bad_count = 0; + return; + } auto obj = T::create(args...); obj->setDescription(context, description, input.getLastOffset()); add(std::move(obj)); diff --git a/libqpdf/qpdf/QPDFParser.hh b/libqpdf/qpdf/QPDFParser.hh index 4519d0f..8a3dadd 100644 --- a/libqpdf/qpdf/QPDFParser.hh +++ b/libqpdf/qpdf/QPDFParser.hh @@ -83,7 +83,8 @@ class QPDFParser std::vector stack; StackFrame* frame; - // Number of recent bad tokens. + // Number of recent bad tokens. This will always be > 0 once a bad token has been encountered as + // it only gets incremented or reset when a bad token is encountered. int bad_count{0}; // Number of bad tokens (remaining) before giving up. int max_bad_count{15};