Commit 40f601dfa7657b503df23b6f03d37d5ba925cb75

Authored by m-holger
1 parent 33b639a0

Refine QPDFParser error handling

#1349 introduced a limit on the maximum size of arrays and dictionaries
contained in objects that generate errors during parsing, and #1354
reduced that limit to 5000 objects. However, the limit was only imposed
once a further error was encountered.

Stop adding objects to containers once the limit is reached.

Fixes oss-fuzz issue 398060137
fuzz/CMakeLists.txt
... ... @@ -155,6 +155,7 @@ set(CORPUS_OTHER
155 155 391974927.fuzz
156 156 394129398.fuzz
157 157 394463491.fuzz
  158 + 398060137.fuzz
158 159 )
159 160  
160 161 set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)
... ...
fuzz/qpdf_extra/398060137.fuzz 0 → 100644
No preview for this file type
fuzz/qtest/fuzz.test
... ... @@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz');
11 11  
12 12 my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS";
13 13  
14   -my $n_qpdf_files = 92; # increment when adding new files
  14 +my $n_qpdf_files = 93; # increment when adding new files
15 15  
16 16 my @fuzzers = (
17 17 ['ascii85' => 1],
... ...
libqpdf/QPDFParser.cc
... ... @@ -215,6 +215,11 @@ QPDFParser::parseRemainder(bool content_stream)
215 215 continue;
216 216  
217 217 case QPDFTokenizer::tt_array_close:
  218 + if (bad_count && !max_bad_count) {
  219 + // Trigger warning.
  220 + (void)tooManyBadTokens();
  221 + return {QPDF_Null::create()};
  222 + }
218 223 if (frame->state == st_array) {
219 224 auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100);
220 225 setDescription(object, frame->offset - 1);
... ... @@ -239,6 +244,11 @@ QPDFParser::parseRemainder(bool content_stream)
239 244 continue;
240 245  
241 246 case QPDFTokenizer::tt_dict_close:
  247 + if (bad_count && !max_bad_count) {
  248 + // Trigger warning.
  249 + (void)tooManyBadTokens();
  250 + return {QPDF_Null::create()};
  251 + }
242 252 if (frame->state <= st_dictionary_value) {
243 253 // Attempt to recover more or less gracefully from invalid dictionaries.
244 254 auto& dict = frame->dict;
... ... @@ -419,6 +429,12 @@ template &lt;typename T, typename... Args&gt;
419 429 void
420 430 QPDFParser::addScalar(Args&&... args)
421 431 {
  432 + if (bad_count && (frame->olist.size() > 5'000 || frame->dict.size() > 5'000)) {
  433 + // Stop adding scalars. We are going to abort when the close token or a bad token is
  434 + // encountered.
  435 + max_bad_count = 0;
  436 + return;
  437 + }
422 438 auto obj = T::create(args...);
423 439 obj->setDescription(context, description, input.getLastOffset());
424 440 add(std::move(obj));
... ...
libqpdf/qpdf/QPDFParser.hh
... ... @@ -83,7 +83,8 @@ class QPDFParser
83 83  
84 84 std::vector<StackFrame> stack;
85 85 StackFrame* frame;
86   - // Number of recent bad tokens.
  86 + // Number of recent bad tokens. This will always be > 0 once a bad token has been encountered as
  87 + // it only gets incremented or reset when a bad token is encountered.
87 88 int bad_count{0};
88 89 // Number of bad tokens (remaining) before giving up.
89 90 int max_bad_count{15};
... ...