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,6 +657,31 @@ class QPDF
657 }; 657 };
658 friend class Warner; 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 // Pipe class is restricted to QPDF_Stream 685 // Pipe class is restricted to QPDF_Stream
661 class Pipe 686 class Pipe
662 { 687 {
@@ -816,6 +841,7 @@ class QPDF @@ -816,6 +841,7 @@ class QPDF
816 friend class ResolveRecorder; 841 friend class ResolveRecorder;
817 842
818 void parse(char const* password); 843 void parse(char const* password);
  844 + void inParse(bool);
819 void warn(QPDFExc const& e); 845 void warn(QPDFExc const& e);
820 void setTrailer(QPDFObjectHandle obj); 846 void setTrailer(QPDFObjectHandle obj);
821 void read_xref(qpdf_offset_t offset); 847 void read_xref(qpdf_offset_t offset);
@@ -1352,6 +1378,7 @@ class QPDF @@ -1352,6 +1378,7 @@ class QPDF
1352 bool reconstructed_xref; 1378 bool reconstructed_xref;
1353 bool fixed_dangling_refs; 1379 bool fixed_dangling_refs;
1354 bool immediate_copy_from; 1380 bool immediate_copy_from;
  1381 + bool in_parse;
1355 1382
1356 // Linearization data 1383 // Linearization data
1357 qpdf_offset_t first_xref_item_offset; // actual value from file 1384 qpdf_offset_t first_xref_item_offset; // actual value from file
libqpdf/QPDF.cc
@@ -150,6 +150,7 @@ QPDF::Members::Members() : @@ -150,6 +150,7 @@ QPDF::Members::Members() :
150 reconstructed_xref(false), 150 reconstructed_xref(false),
151 fixed_dangling_refs(false), 151 fixed_dangling_refs(false),
152 immediate_copy_from(false), 152 immediate_copy_from(false),
  153 + in_parse(false),
153 first_xref_item_offset(0), 154 first_xref_item_offset(0),
154 uncompressed_after_compressed(false) 155 uncompressed_after_compressed(false)
155 { 156 {
@@ -417,6 +418,20 @@ QPDF::parse(char const* password) @@ -417,6 +418,20 @@ QPDF::parse(char const* password)
417 } 418 }
418 419
419 void 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 QPDF::warn(QPDFExc const& e) 435 QPDF::warn(QPDFExc const& e)
421 { 436 {
422 this->m->warnings.push_back(e); 437 this->m->warnings.push_back(e);
libqpdf/QPDFObjectHandle.cc
@@ -1714,7 +1714,11 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input, @@ -1714,7 +1714,11 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
1714 // This method must take care not to resolve any objects. Don't 1714 // This method must take care not to resolve any objects. Don't
1715 // check the type of any object without first ensuring that it is 1715 // check the type of any object without first ensuring that it is
1716 // a direct object. Otherwise, doing so may have the side effect 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 empty = false; 1723 empty = false;
1720 1724