aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2017-07-25 16:21:27 +0200
committerJay Berkenbilt <ejb@ql.org>2017-07-26 12:24:07 +0200
commit315092dd98d5230ef0efa18b294d464d0e9f79d0 (patch)
treec0031a373dd87c04d2d34f2fbcd7602b344c4ac2
parent603f222365252f1a1e20303b3dbe52466be3053b (diff)
downloadqpdf-315092dd98d5230ef0efa18b294d464d0e9f79d0.tar.zst
Avoid xref reconstruction infinite loop (fixes #100)
This is CVE-2017-9209.
-rw-r--r--ChangeLog4
-rw-r--r--include/qpdf/QPDF.hh1
-rw-r--r--libqpdf/QPDF.cc10
-rw-r--r--qpdf/qtest/qpdf.test3
-rw-r--r--qpdf/qtest/qpdf/issue-100.out5
-rw-r--r--qpdf/qtest/qpdf/issue-100.pdfbin0 -> 1145 bytes
6 files changed, 22 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 32bafad6..ce0ce1e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2017-07-26 Jay Berkenbilt <ejb@ql.org>
+ * CVE-2017-9209: Fix infinite loop caused by attempting to
+ reconstruct the xref table while already in the process of
+ reconstructing the xref table.
+
* CVE-2017-9210: Fix infinite loop caused by attempting to unparse
an object for inclusion in the text of an exception.
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 88acf6c5..f7a31edf 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -1075,6 +1075,7 @@ class QPDF
// copied_stream_data_provider is owned by copied_streams
CopiedStreamDataProvider* copied_stream_data_provider;
std::set<QPDFObjGen> attachment_streams;
+ bool reconstructed_xref;
// Linearization data
qpdf_offset_t first_xref_item_offset; // actual value from file
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index b8a1601c..a50c87ad 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -93,6 +93,7 @@ QPDF::QPDF() :
cached_key_generation(0),
pushed_inherited_attributes_to_pages(false),
copied_stream_data_provider(0),
+ reconstructed_xref(false),
first_xref_item_offset(0),
uncompressed_after_compressed(false)
{
@@ -331,6 +332,15 @@ QPDF::setTrailer(QPDFObjectHandle obj)
void
QPDF::reconstruct_xref(QPDFExc& e)
{
+ if (this->reconstructed_xref)
+ {
+ // Avoid xref reconstruction infinite loops
+ QTC::TC("qpdf", "QPDF caught recursive xref reconstruction");
+ throw e;
+ }
+
+ this->reconstructed_xref = true;
+
PCRE obj_re("^\\s*(\\d+)\\s+(\\d+)\\s+obj\\b");
PCRE endobj_re("^\\s*endobj\\b");
PCRE trailer_re("^\\s*trailer\\b");
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index e0b2609a..dd8dad30 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -206,7 +206,7 @@ $td->runtest("remove page we don't have",
show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
-$n_tests += 78;
+$n_tests += 79;
$td->runtest("qpdf version",
{$td->COMMAND => "qpdf --version"},
@@ -220,6 +220,7 @@ $td->runtest("C API: qpdf version",
# Files to reproduce various bugs
foreach my $d (
+ ["100","xref reconstruction loop"],
["101", "resolve for exception text"],
)
{
diff --git a/qpdf/qtest/qpdf/issue-100.out b/qpdf/qtest/qpdf/issue-100.out
new file mode 100644
index 00000000..37bd3207
--- /dev/null
+++ b/qpdf/qtest/qpdf/issue-100.out
@@ -0,0 +1,5 @@
+WARNING: issue-100.pdf: file is damaged
+WARNING: issue-100.pdf (file position 736): xref not found
+WARNING: issue-100.pdf: Attempting to reconstruct cross-reference table
+WARNING: issue-100.pdf (object 5 0, file position 489): attempting to recover stream length
+issue-100.pdf (object 6 0, file position 59): expected n n obj
diff --git a/qpdf/qtest/qpdf/issue-100.pdf b/qpdf/qtest/qpdf/issue-100.pdf
new file mode 100644
index 00000000..fc418b3b
--- /dev/null
+++ b/qpdf/qtest/qpdf/issue-100.pdf
Binary files differ