Blob Blame History Raw
From b4d6cf6836ce025ba1811b7bbec52680c7204223 Mon Sep 17 00:00:00 2001
From: Jay Berkenbilt <ejb@ql.org>
Date: Sun, 15 Apr 2018 16:11:22 -0400
Subject: [PATCH] Limit depth of nesting in direct objects (fixes #202)

This fixes CVE-2018-9918.
---
 ChangeLog                     |   5 +++++
 libqpdf/QPDFObjectHandle.cc   |  26 ++++++++++++++++++++------
 qpdf/qpdf.testcov             |   1 +
 qpdf/qtest/qpdf.test          |   1 +
 qpdf/qtest/qpdf/issue-146.out |   4 +---
 qpdf/qtest/qpdf/issue-202.out |   5 +++++
 4 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3a88135..17c7a1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2018-04-15  Jay Berkenbilt  <ejb@ql.org>
+
+	* Arbitrarily limit the depth of data structures represented by
+	direct object. This is CVE-2018-9918. Fixes #202.
+
 2018-03-06  Jay Berkenbilt  <ejb@ql.org>
 
 	* 8.0.2: release
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index c178a49..149668e 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -1487,12 +1487,26 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
 
 	  case QPDFTokenizer::tt_array_open:
 	  case QPDFTokenizer::tt_dict_open:
-            olist_stack.push_back(std::vector<QPDFObjectHandle>());
-            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<QPDFObjectHandle>());
+                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 5374302..3f055a8 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/issue-146.out b/qpdf/qtest/qpdf/issue-146.out
index 79bb811..a275957 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
-- 
2.14.3