Commit 315092dd98d5230ef0efa18b294d464d0e9f79d0

Authored by Jay Berkenbilt
1 parent 603f2223

Avoid xref reconstruction infinite loop (fixes #100)

This is CVE-2017-9209.
ChangeLog
1 2017-07-26 Jay Berkenbilt <ejb@ql.org> 1 2017-07-26 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * CVE-2017-9209: Fix infinite loop caused by attempting to
  4 + reconstruct the xref table while already in the process of
  5 + reconstructing the xref table.
  6 +
3 * CVE-2017-9210: Fix infinite loop caused by attempting to unparse 7 * CVE-2017-9210: Fix infinite loop caused by attempting to unparse
4 an object for inclusion in the text of an exception. 8 an object for inclusion in the text of an exception.
5 9
include/qpdf/QPDF.hh
@@ -1075,6 +1075,7 @@ class QPDF @@ -1075,6 +1075,7 @@ class QPDF
1075 // copied_stream_data_provider is owned by copied_streams 1075 // copied_stream_data_provider is owned by copied_streams
1076 CopiedStreamDataProvider* copied_stream_data_provider; 1076 CopiedStreamDataProvider* copied_stream_data_provider;
1077 std::set<QPDFObjGen> attachment_streams; 1077 std::set<QPDFObjGen> attachment_streams;
  1078 + bool reconstructed_xref;
1078 1079
1079 // Linearization data 1080 // Linearization data
1080 qpdf_offset_t first_xref_item_offset; // actual value from file 1081 qpdf_offset_t first_xref_item_offset; // actual value from file
libqpdf/QPDF.cc
@@ -93,6 +93,7 @@ QPDF::QPDF() : @@ -93,6 +93,7 @@ QPDF::QPDF() :
93 cached_key_generation(0), 93 cached_key_generation(0),
94 pushed_inherited_attributes_to_pages(false), 94 pushed_inherited_attributes_to_pages(false),
95 copied_stream_data_provider(0), 95 copied_stream_data_provider(0),
  96 + reconstructed_xref(false),
96 first_xref_item_offset(0), 97 first_xref_item_offset(0),
97 uncompressed_after_compressed(false) 98 uncompressed_after_compressed(false)
98 { 99 {
@@ -331,6 +332,15 @@ QPDF::setTrailer(QPDFObjectHandle obj) @@ -331,6 +332,15 @@ QPDF::setTrailer(QPDFObjectHandle obj)
331 void 332 void
332 QPDF::reconstruct_xref(QPDFExc& e) 333 QPDF::reconstruct_xref(QPDFExc& e)
333 { 334 {
  335 + if (this->reconstructed_xref)
  336 + {
  337 + // Avoid xref reconstruction infinite loops
  338 + QTC::TC("qpdf", "QPDF caught recursive xref reconstruction");
  339 + throw e;
  340 + }
  341 +
  342 + this->reconstructed_xref = true;
  343 +
334 PCRE obj_re("^\\s*(\\d+)\\s+(\\d+)\\s+obj\\b"); 344 PCRE obj_re("^\\s*(\\d+)\\s+(\\d+)\\s+obj\\b");
335 PCRE endobj_re("^\\s*endobj\\b"); 345 PCRE endobj_re("^\\s*endobj\\b");
336 PCRE trailer_re("^\\s*trailer\\b"); 346 PCRE trailer_re("^\\s*trailer\\b");
qpdf/qtest/qpdf.test
@@ -206,7 +206,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;, @@ -206,7 +206,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;,
206 show_ntests(); 206 show_ntests();
207 # ---------- 207 # ----------
208 $td->notify("--- Miscellaneous Tests ---"); 208 $td->notify("--- Miscellaneous Tests ---");
209 -$n_tests += 78; 209 +$n_tests += 79;
210 210
211 $td->runtest("qpdf version", 211 $td->runtest("qpdf version",
212 {$td->COMMAND => "qpdf --version"}, 212 {$td->COMMAND => "qpdf --version"},
@@ -220,6 +220,7 @@ $td-&gt;runtest(&quot;C API: qpdf version&quot;, @@ -220,6 +220,7 @@ $td-&gt;runtest(&quot;C API: qpdf version&quot;,
220 220
221 # Files to reproduce various bugs 221 # Files to reproduce various bugs
222 foreach my $d ( 222 foreach my $d (
  223 + ["100","xref reconstruction loop"],
223 ["101", "resolve for exception text"], 224 ["101", "resolve for exception text"],
224 ) 225 )
225 { 226 {
qpdf/qtest/qpdf/issue-100.out 0 → 100644
  1 +WARNING: issue-100.pdf: file is damaged
  2 +WARNING: issue-100.pdf (file position 736): xref not found
  3 +WARNING: issue-100.pdf: Attempting to reconstruct cross-reference table
  4 +WARNING: issue-100.pdf (object 5 0, file position 489): attempting to recover stream length
  5 +issue-100.pdf (object 6 0, file position 59): expected n n obj
qpdf/qtest/qpdf/issue-100.pdf 0 → 100644
No preview for this file type