Commit 40f601dfa7657b503df23b6f03d37d5ba925cb75
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
Showing
5 changed files
with
20 additions
and
2 deletions
fuzz/CMakeLists.txt
| @@ -155,6 +155,7 @@ set(CORPUS_OTHER | @@ -155,6 +155,7 @@ set(CORPUS_OTHER | ||
| 155 | 391974927.fuzz | 155 | 391974927.fuzz |
| 156 | 394129398.fuzz | 156 | 394129398.fuzz |
| 157 | 394463491.fuzz | 157 | 394463491.fuzz |
| 158 | + 398060137.fuzz | ||
| 158 | ) | 159 | ) |
| 159 | 160 | ||
| 160 | set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) | 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,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 = 92; # increment when adding new files | 14 | +my $n_qpdf_files = 93; # increment when adding new files |
| 15 | 15 | ||
| 16 | my @fuzzers = ( | 16 | my @fuzzers = ( |
| 17 | ['ascii85' => 1], | 17 | ['ascii85' => 1], |
libqpdf/QPDFParser.cc
| @@ -215,6 +215,11 @@ QPDFParser::parseRemainder(bool content_stream) | @@ -215,6 +215,11 @@ QPDFParser::parseRemainder(bool content_stream) | ||
| 215 | continue; | 215 | continue; |
| 216 | 216 | ||
| 217 | case QPDFTokenizer::tt_array_close: | 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 | if (frame->state == st_array) { | 223 | if (frame->state == st_array) { |
| 219 | auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100); | 224 | auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100); |
| 220 | setDescription(object, frame->offset - 1); | 225 | setDescription(object, frame->offset - 1); |
| @@ -239,6 +244,11 @@ QPDFParser::parseRemainder(bool content_stream) | @@ -239,6 +244,11 @@ QPDFParser::parseRemainder(bool content_stream) | ||
| 239 | continue; | 244 | continue; |
| 240 | 245 | ||
| 241 | case QPDFTokenizer::tt_dict_close: | 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 | if (frame->state <= st_dictionary_value) { | 252 | if (frame->state <= st_dictionary_value) { |
| 243 | // Attempt to recover more or less gracefully from invalid dictionaries. | 253 | // Attempt to recover more or less gracefully from invalid dictionaries. |
| 244 | auto& dict = frame->dict; | 254 | auto& dict = frame->dict; |
| @@ -419,6 +429,12 @@ template <typename T, typename... Args> | @@ -419,6 +429,12 @@ template <typename T, typename... Args> | ||
| 419 | void | 429 | void |
| 420 | QPDFParser::addScalar(Args&&... args) | 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 | auto obj = T::create(args...); | 438 | auto obj = T::create(args...); |
| 423 | obj->setDescription(context, description, input.getLastOffset()); | 439 | obj->setDescription(context, description, input.getLastOffset()); |
| 424 | add(std::move(obj)); | 440 | add(std::move(obj)); |
libqpdf/qpdf/QPDFParser.hh
| @@ -83,7 +83,8 @@ class QPDFParser | @@ -83,7 +83,8 @@ class QPDFParser | ||
| 83 | 83 | ||
| 84 | std::vector<StackFrame> stack; | 84 | std::vector<StackFrame> stack; |
| 85 | StackFrame* frame; | 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 | int bad_count{0}; | 88 | int bad_count{0}; |
| 88 | // Number of bad tokens (remaining) before giving up. | 89 | // Number of bad tokens (remaining) before giving up. |
| 89 | int max_bad_count{15}; | 90 | int max_bad_count{15}; |