diff -up qpdf-6.0.0/include/qpdf/QPDF.hh.detect-recursions qpdf-6.0.0/include/qpdf/QPDF.hh --- qpdf-6.0.0/include/qpdf/QPDF.hh.detect-recursions 2015-11-10 18:48:52.000000000 +0100 +++ qpdf-6.0.0/include/qpdf/QPDF.hh 2017-08-02 08:41:17.500831407 +0200 @@ -603,6 +603,25 @@ class QPDF int gen; }; + class ResolveRecorder + { + public: + ResolveRecorder(QPDF* qpdf, QPDFObjGen const& og) : + qpdf(qpdf), + og(og) + { + qpdf->resolving.insert(og); + } + virtual ~ResolveRecorder() + { + this->qpdf->resolving.erase(og); + } + private: + QPDF* qpdf; + QPDFObjGen og; + }; + friend class ResolveRecorder; + void parse(char const* password); void warn(QPDFExc const& e); void setTrailer(QPDFObjectHandle obj); @@ -1065,6 +1084,7 @@ class QPDF std::map xref_table; std::set deleted_objects; std::map obj_cache; + std::set resolving; QPDFObjectHandle trailer; std::vector all_pages; std::map pageobj_to_pages_pos; diff -up qpdf-6.0.0/libqpdf/QPDF.cc.detect-recursions qpdf-6.0.0/libqpdf/QPDF.cc --- qpdf-6.0.0/libqpdf/QPDF.cc.detect-recursions 2015-11-10 18:48:52.000000000 +0100 +++ qpdf-6.0.0/libqpdf/QPDF.cc 2017-08-02 08:42:19.070393817 +0200 @@ -1453,6 +1453,20 @@ QPDF::resolve(int objid, int generation) // to insert things into the object cache that don't actually // exist in the file. QPDFObjGen og(objid, generation); + if (this->resolving.count(og)) + { + // This can happen if an object references itself directly or + // indirectly in some key that has to be resolved during + // object parsing, such as stream length. + warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "", this->file->getLastOffset(), + "loop detected resolving object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation))); + return new QPDF_Null; + } + ResolveRecorder rr(this, og); + if (! this->obj_cache.count(og)) { if (! this->xref_table.count(og))