CVE-2015-9252 (Other, published 2018-02-13T19:29Z)
Description: value: An issue was discovered in QPDF before 7.0.0. Endless recursion causes stack exhaustion in QPDFTokenizer::resolveLiteral() in QPDFTokenizer.cc, related to the QPDF::resolve function in QPDF.cc.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-399 \u2014 Resource Management Errors
Weaknesses in this category are related to improper management of system resources.
Repository: https://github.com/qpdf/qpdf
Files changed:
ChangeLog
include/qpdf/QPDF.hh
libqpdf/QPDF.cc
qpdf/qpdf.testcov
qpdf/qtest/qpdf.test
qpdf/qtest/qpdf/issue-51.out
qpdf/qtest/qpdf/issue-51.pdf
Commit message: Detect recursion loops resolving objects (fixes #51)

During parsing of an object, sometimes parts of the object have to be
resolved. An example is stream lengths. If such an object directly or
indirectly points to the object being parsed, it can cause an infinite
loop. Guard against all cases of re-entrant resolution of objects.


Vulnerable code:
$n_tests += 81;

Fixed code:
	* Detect infinite loops while resolving objects. This could happen
	if something inside an object that had to be resolved during
	parsing, such as a stream length, recursively referenced the
	object being resolved.

    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;

    std::set<QPDFObjGen> resolving;
    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.
	QTC::TC("qpdf", "QPDF recursion loop in resolve");
	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);

QPDF recursion loop in resolve 0
$n_tests += 82;
    ["51", "resolve loop"],
WARNING: issue-51.pdf: reported number of objects (0) inconsistent with actual number of objects (9)
WARNING: issue-51.pdf (object 7 0, file position 553): expected endobj
WARNING: issue-51.pdf (object 1 0, file position 359): expected endobj
WARNING: issue-51.pdf (file position 70): loop detected resolving object 2 0
WARNING: issue-51.pdf (object 2 0, file position 71): attempting to recover stream length
issue-51.pdf (object 2 0, file position 71): unable to recover stream data
%PDF-100000000000002 0 obj
<</Length 2 0 R/000000/00000000000>>
stream
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 0 obj
<</0000000000000000 0 0 R/000000000 0 0 R/00000000[00000000000]/00000<</0/00000000000000000000000000000000>>/00000000 2 0 R>>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 0 obj
<</0000/0000000/00000 0 0 R
/0000000000[1 0 R 0000 null null 0]
/0000(00000)
>>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000xref
0 9
0000000000 00000 f 
0000000200 00000 n 
0000000009 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000000 00000 n 
0000000400 00000 n 
0000000000 00000 n 
trailer<</Size 0/Root 7 0 R>>startxref
740
%%EOF