Commit 225cd9dac27d685833156dfc249838cda11cd2ef
1 parent
ae5bd710
Protect against coding error of re-entrant parsing
Showing
3 changed files
with
47 additions
and
1 deletions
include/qpdf/QPDF.hh
| ... | ... | @@ -657,6 +657,31 @@ class QPDF |
| 657 | 657 | }; |
| 658 | 658 | friend class Warner; |
| 659 | 659 | |
| 660 | + // ParseGuard class allows QPDFObjectHandle to detect re-entrant | |
| 661 | + // resolution | |
| 662 | + class ParseGuard | |
| 663 | + { | |
| 664 | + friend class QPDFObjectHandle; | |
| 665 | + private: | |
| 666 | + ParseGuard(QPDF* qpdf) : | |
| 667 | + qpdf(qpdf) | |
| 668 | + { | |
| 669 | + if (qpdf) | |
| 670 | + { | |
| 671 | + qpdf->inParse(true); | |
| 672 | + } | |
| 673 | + } | |
| 674 | + ~ParseGuard() | |
| 675 | + { | |
| 676 | + if (qpdf) | |
| 677 | + { | |
| 678 | + qpdf->inParse(false); | |
| 679 | + } | |
| 680 | + } | |
| 681 | + QPDF* qpdf; | |
| 682 | + }; | |
| 683 | + friend class ParseGuard; | |
| 684 | + | |
| 660 | 685 | // Pipe class is restricted to QPDF_Stream |
| 661 | 686 | class Pipe |
| 662 | 687 | { |
| ... | ... | @@ -816,6 +841,7 @@ class QPDF |
| 816 | 841 | friend class ResolveRecorder; |
| 817 | 842 | |
| 818 | 843 | void parse(char const* password); |
| 844 | + void inParse(bool); | |
| 819 | 845 | void warn(QPDFExc const& e); |
| 820 | 846 | void setTrailer(QPDFObjectHandle obj); |
| 821 | 847 | void read_xref(qpdf_offset_t offset); |
| ... | ... | @@ -1352,6 +1378,7 @@ class QPDF |
| 1352 | 1378 | bool reconstructed_xref; |
| 1353 | 1379 | bool fixed_dangling_refs; |
| 1354 | 1380 | bool immediate_copy_from; |
| 1381 | + bool in_parse; | |
| 1355 | 1382 | |
| 1356 | 1383 | // Linearization data |
| 1357 | 1384 | qpdf_offset_t first_xref_item_offset; // actual value from file | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -150,6 +150,7 @@ QPDF::Members::Members() : |
| 150 | 150 | reconstructed_xref(false), |
| 151 | 151 | fixed_dangling_refs(false), |
| 152 | 152 | immediate_copy_from(false), |
| 153 | + in_parse(false), | |
| 153 | 154 | first_xref_item_offset(0), |
| 154 | 155 | uncompressed_after_compressed(false) |
| 155 | 156 | { |
| ... | ... | @@ -417,6 +418,20 @@ QPDF::parse(char const* password) |
| 417 | 418 | } |
| 418 | 419 | |
| 419 | 420 | void |
| 421 | +QPDF::inParse(bool v) | |
| 422 | +{ | |
| 423 | + if (this->m->in_parse == v) | |
| 424 | + { | |
| 425 | + // This happens of QPDFObjectHandle::parseInternal tries to | |
| 426 | + // resolve an indirect object while it is parsing. | |
| 427 | + throw std::logic_error( | |
| 428 | + "QPDF: re-entrant parsing detected. This is a qpdf bug." | |
| 429 | + " Please report at https://github.com/qpdf/qpdf/issues."); | |
| 430 | + } | |
| 431 | + this->m->in_parse = v; | |
| 432 | +} | |
| 433 | + | |
| 434 | +void | |
| 420 | 435 | QPDF::warn(QPDFExc const& e) |
| 421 | 436 | { |
| 422 | 437 | this->m->warnings.push_back(e); | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -1714,7 +1714,11 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input, |
| 1714 | 1714 | // This method must take care not to resolve any objects. Don't |
| 1715 | 1715 | // check the type of any object without first ensuring that it is |
| 1716 | 1716 | // a direct object. Otherwise, doing so may have the side effect |
| 1717 | - // of reading the object and changing the file pointer. | |
| 1717 | + // of reading the object and changing the file pointer. If you do | |
| 1718 | + // this, it will cause a logic error to be thrown from | |
| 1719 | + // QPDF::inParse(). | |
| 1720 | + | |
| 1721 | + QPDF::ParseGuard pg(context); | |
| 1718 | 1722 | |
| 1719 | 1723 | empty = false; |
| 1720 | 1724 | ... | ... |