diff --git a/ChangeLog b/ChangeLog index 3a88135c..17c7a1c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2018-04-15 Jay Berkenbilt + + * Arbitrarily limit the depth of data structures represented by + direct object. This is CVE-2018-9918. Fixes #202. + 2018-03-06 Jay Berkenbilt * 8.0.2: release diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index c178a492..149668eb 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -1487,12 +1487,26 @@ QPDFObjectHandle::parseInternal(PointerHolder input, case QPDFTokenizer::tt_array_open: case QPDFTokenizer::tt_dict_open: - olist_stack.push_back(std::vector()); - state = st_start; - offset_stack.push_back(input->tell()); - state_stack.push_back( - (token.getType() == QPDFTokenizer::tt_array_open) ? - st_array : st_dictionary); + if (olist_stack.size() > 500) + { + QTC::TC("qpdf", "QPDFObjectHandle too deep"); + warn(context, + QPDFExc(qpdf_e_damaged_pdf, input->getName(), + object_description, + input->getLastOffset(), + "ignoring excessively deeply nested data structure")); + object = newNull(); + state = st_top; + } + else + { + olist_stack.push_back(std::vector()); + state = st_start; + offset_stack.push_back(input->tell()); + state_stack.push_back( + (token.getType() == QPDFTokenizer::tt_array_open) ? + st_array : st_dictionary); + } break; case QPDFTokenizer::tt_bool: diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 5374302f..3f055a86 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -335,3 +335,4 @@ QPDFObjectHandle numeric non-numeric 0 QPDFObjectHandle erase array bounds 0 qpdf-c called qpdf_check_pdf 0 QPDF xref loop 0 +QPDFObjectHandle too deep 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 92a9412b..b27b4c82 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -236,6 +236,7 @@ my @bug_tests = ( ["148", "free memory on bad flate", 2], ["149", "xref prev pointer loop", 3], ["150", "integer overflow", 2], + ["202", "even more deeply nested dictionary", 2], ); $n_tests += scalar(@bug_tests); foreach my $d (@bug_tests) diff --git a/qpdf/qtest/qpdf/issue-146.out b/qpdf/qtest/qpdf/issue-146.out index 79bb8118..a275957f 100644 --- a/qpdf/qtest/qpdf/issue-146.out +++ b/qpdf/qtest/qpdf/issue-146.out @@ -1,7 +1,5 @@ WARNING: issue-146.pdf: file is damaged WARNING: issue-146.pdf: can't find startxref WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table -WARNING: issue-146.pdf (trailer, offset 20728): unknown token while reading object; treating as string -WARNING: issue-146.pdf (trailer, offset 20732): unexpected EOF -WARNING: issue-146.pdf (trailer, offset 20732): parse error while reading object +WARNING: issue-146.pdf (trailer, offset 695): ignoring excessively deeply nested data structure issue-146.pdf: unable to find trailer dictionary while recovering damaged file diff --git a/qpdf/qtest/qpdf/issue-202.out b/qpdf/qtest/qpdf/issue-202.out new file mode 100644 index 00000000..edac1f2d --- /dev/null +++ b/qpdf/qtest/qpdf/issue-202.out @@ -0,0 +1,5 @@ +WARNING: issue-202.pdf (trailer, offset 55770): ignoring excessively deeply nested data structure +WARNING: issue-202.pdf: file is damaged +WARNING: issue-202.pdf (offset 54769): expected trailer dictionary +WARNING: issue-202.pdf: Attempting to reconstruct cross-reference table +issue-202.pdf: unable to find trailer dictionary while recovering damaged file diff --git a/qpdf/qtest/qpdf/issue-202.pdf b/qpdf/qtest/qpdf/issue-202.pdf new file mode 100644 index 00000000..193d58a1 Binary files /dev/null and b/qpdf/qtest/qpdf/issue-202.pdf differ