Commit 315092dd98d5230ef0efa18b294d464d0e9f79d0
1 parent
603f2223
Avoid xref reconstruction infinite loop (fixes #100)
This is CVE-2017-9209.
Showing
6 changed files
with
22 additions
and
1 deletions
ChangeLog
| 1 | 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 | 7 | * CVE-2017-9210: Fix infinite loop caused by attempting to unparse |
| 4 | 8 | an object for inclusion in the text of an exception. |
| 5 | 9 | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -1075,6 +1075,7 @@ class QPDF |
| 1075 | 1075 | // copied_stream_data_provider is owned by copied_streams |
| 1076 | 1076 | CopiedStreamDataProvider* copied_stream_data_provider; |
| 1077 | 1077 | std::set<QPDFObjGen> attachment_streams; |
| 1078 | + bool reconstructed_xref; | |
| 1078 | 1079 | |
| 1079 | 1080 | // Linearization data |
| 1080 | 1081 | qpdf_offset_t first_xref_item_offset; // actual value from file | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -93,6 +93,7 @@ QPDF::QPDF() : |
| 93 | 93 | cached_key_generation(0), |
| 94 | 94 | pushed_inherited_attributes_to_pages(false), |
| 95 | 95 | copied_stream_data_provider(0), |
| 96 | + reconstructed_xref(false), | |
| 96 | 97 | first_xref_item_offset(0), |
| 97 | 98 | uncompressed_after_compressed(false) |
| 98 | 99 | { |
| ... | ... | @@ -331,6 +332,15 @@ QPDF::setTrailer(QPDFObjectHandle obj) |
| 331 | 332 | void |
| 332 | 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 | 344 | PCRE obj_re("^\\s*(\\d+)\\s+(\\d+)\\s+obj\\b"); |
| 335 | 345 | PCRE endobj_re("^\\s*endobj\\b"); |
| 336 | 346 | PCRE trailer_re("^\\s*trailer\\b"); | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -206,7 +206,7 @@ $td->runtest("remove page we don't have", |
| 206 | 206 | show_ntests(); |
| 207 | 207 | # ---------- |
| 208 | 208 | $td->notify("--- Miscellaneous Tests ---"); |
| 209 | -$n_tests += 78; | |
| 209 | +$n_tests += 79; | |
| 210 | 210 | |
| 211 | 211 | $td->runtest("qpdf version", |
| 212 | 212 | {$td->COMMAND => "qpdf --version"}, |
| ... | ... | @@ -220,6 +220,7 @@ $td->runtest("C API: qpdf version", |
| 220 | 220 | |
| 221 | 221 | # Files to reproduce various bugs |
| 222 | 222 | foreach my $d ( |
| 223 | + ["100","xref reconstruction loop"], | |
| 223 | 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