Commit 225cd9dac27d685833156dfc249838cda11cd2ef

Authored by Jay Berkenbilt
1 parent ae5bd710

Protect against coding error of re-entrant parsing

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  
... ...