Skip to content

Commit edee622

Browse files
Fixed possible infinite recursion in PdfType::resolve()
1 parent 282e79b commit edee622

File tree

4 files changed

+56
-14
lines changed

4 files changed

+56
-14
lines changed

src/PdfParser/Type/PdfType.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,33 @@ class PdfType
2727
* @param PdfType $value
2828
* @param PdfParser $parser
2929
* @param bool $stopAtIndirectObject
30+
* @param array $ensuredObjectsList A list of all ensured indirect objects to prevent recursion
3031
* @return PdfType
3132
* @throws CrossReferenceException
3233
* @throws PdfParserException
3334
*/
34-
public static function resolve(PdfType $value, PdfParser $parser, $stopAtIndirectObject = false)
35-
{
35+
public static function resolve(
36+
PdfType $value,
37+
PdfParser $parser,
38+
$stopAtIndirectObject = false,
39+
array &$ensuredObjectsList = []
40+
) {
41+
if ($value instanceof PdfIndirectObjectReference) {
42+
$value = $parser->getIndirectObject($value->value);
43+
}
44+
3645
if ($value instanceof PdfIndirectObject) {
3746
if ($stopAtIndirectObject === true) {
3847
return $value;
3948
}
4049

41-
return self::resolve($value->value, $parser, $stopAtIndirectObject);
42-
}
43-
44-
if ($value instanceof PdfIndirectObjectReference) {
45-
return self::resolve($parser->getIndirectObject($value->value), $parser, $stopAtIndirectObject);
50+
if (\in_array($value->objectNumber, $ensuredObjectsList, true)) {
51+
throw new PdfParserException(
52+
\sprintf('Indirect reference recursion detected (%s).', $value->objectNumber)
53+
);
54+
}
55+
$ensuredObjectsList[] = $value->objectNumber;
56+
return self::resolve($value->value, $parser, $stopAtIndirectObject, $ensuredObjectsList);
4657
}
4758

4859
return $value;

src/PdfReader/PdfReader.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function getPage($pageNumber)
147147
foreach ($kids->value as $reference) {
148148
$reference = PdfIndirectObjectReference::ensure($reference);
149149

150-
if (\in_array($reference->value, $alreadyReadKids)) {
150+
if (\in_array($reference->value, $alreadyReadKids, true)) {
151151
throw new PdfReaderException('Recursive pages dictionary detected.');
152152
}
153153
$alreadyReadKids[] = $reference->value;
@@ -224,7 +224,7 @@ protected function readPages($readAll = false)
224224
continue;
225225
}
226226

227-
if (\in_array($reference->value, $alreadyReadKids)) {
227+
if (\in_array($reference->value, $alreadyReadKids, true)) {
228228
throw new PdfReaderException('Recursive pages dictionary detected.');
229229
}
230230
$alreadyReadKids[] = $reference->value;

tests/functional/PdfParser/Type/PdfIndirectObjectTest.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@
55
use PHPUnit\Framework\TestCase;
66
use setasign\Fpdi\PdfParser\PdfParser;
77
use setasign\Fpdi\PdfParser\StreamReader;
8-
use setasign\Fpdi\PdfParser\Type\PdfBoolean;
98
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
109
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
11-
use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
12-
use setasign\Fpdi\PdfParser\Type\PdfNull;
1310
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
1411
use setasign\Fpdi\PdfParser\Type\PdfStream;
15-
use setasign\Fpdi\PdfParser\Type\PdfToken;
1612

1713
class PdfIndirectObjectTest extends TestCase
1814
{
@@ -129,7 +125,6 @@ public function testParse($objectNumberToken, $generationNumberToken, $in, $expe
129125
if ($result->value instanceof PdfStream) {
130126
$this->assertEquals($expectedResult->value->value, $result->value->value);
131127
$this->assertSame($expectedResult->value->getStream(), $result->value->getStream());
132-
133128
} else {
134129
$this->assertEquals($expectedResult, $result);
135130
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PdfParser\Type;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use setasign\Fpdi\PdfParser\PdfParser;
9+
use setasign\Fpdi\PdfParser\PdfParserException;
10+
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
11+
use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
12+
use setasign\Fpdi\PdfParser\Type\PdfType;
13+
14+
class PdfTypeTest extends TestCase
15+
{
16+
public function testResolveWithRecursiveReferences()
17+
{
18+
$parser = (
19+
$this->getMockBuilder(PdfParser::class)
20+
->setMethods(['getCatalog', 'getIndirectObject'])
21+
->disableOriginalConstructor()
22+
->getMock()
23+
);
24+
25+
$object1 = PdfIndirectObject::create(1, 0, PdfIndirectObjectReference::create(2, 0));
26+
$object2 = PdfIndirectObject::create(2, 0, PdfIndirectObjectReference::create(1, 0));
27+
$parser->method('getIndirectObject')->willReturnMap([
28+
[1, false, $object1],
29+
[2, false, $object2],
30+
]);
31+
32+
$this->expectException(PdfParserException::class);
33+
$this->expectExceptionMessage('Indirect reference recursion detected (1).');
34+
PdfType::resolve($object1, $parser);
35+
}
36+
}

0 commit comments

Comments
 (0)