Skip to content

Commit b2de3cf

Browse files
committed
Fix GH-21986: PharData::getContent() crash on infinite recursion with symlinks.
The entries being unbounded when cycling through them, it blows the stack. close GH-21989
1 parent f5db09d commit b2de3cf

3 files changed

Lines changed: 67 additions & 10 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ PHP NEWS
3131
- OpenSSL:
3232
. Fix compatibility issues with OpenSSL 4.0. (jordikroon, Remi)
3333

34+
- Phar:
35+
. Fixed bug GH-21986 (PharData::getContent() crash on infinite recursion
36+
with symlinks). (David Carlier)
37+
3438
- SOAP:
3539
. Fixed integer overflow when decoding SOAP array indexes. (Weilin Du)
3640

ext/phar/tests/tar/gh21986.phpt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
GH-21986 (PharData::getContent() crash on circular symlink chain in tar)
3+
--EXTENSIONS--
4+
phar
5+
--FILE--
6+
<?php
7+
function tar_symlink($name, $target) {
8+
$h = str_pad($name, 100, "\0");
9+
$h .= "0000777\0";
10+
$h .= "0000000\0";
11+
$h .= "0000000\0";
12+
$h .= "00000000000\0";
13+
$h .= "00000000000\0";
14+
$h .= str_repeat(' ', 8);
15+
$h .= '2';
16+
$h .= str_pad($target, 100, "\0");
17+
$h .= "ustar\0" . "00";
18+
$h = str_pad($h, 512, "\0");
19+
20+
$sum = 0;
21+
for ($i = 0; $i < 512; $i++) {
22+
$sum += ord($h[$i]);
23+
}
24+
return substr_replace($h, sprintf("%06o\0 ", $sum), 148, 8);
25+
}
26+
27+
$tar = __DIR__ . '/gh21986.tar';
28+
file_put_contents(
29+
$tar,
30+
tar_symlink('a.txt', 'b.txt') .
31+
tar_symlink('b.txt', 'a.txt') .
32+
str_repeat("\0", 1024)
33+
);
34+
35+
$phar = new PharData($tar);
36+
var_dump($phar['a.txt']->getContent());
37+
?>
38+
--CLEAN--
39+
<?php
40+
@unlink(__DIR__ . '/gh21986.tar');
41+
?>
42+
--EXPECT--
43+
string(0) ""

ext/phar/util.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,24 +64,34 @@ phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */
6464
{
6565
phar_entry_info *link_entry;
6666
char *link;
67+
uint32_t depth = 0, max_depth;
6768

6869
if (!entry->link) {
6970
return entry;
7071
}
7172

72-
link = phar_get_link_location(entry);
73-
if (NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->link, strlen(entry->link))) ||
74-
NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), link, strlen(link)))) {
75-
if (link != entry->link) {
76-
efree(link);
73+
max_depth = zend_hash_num_elements(&(entry->phar->manifest));
74+
75+
while (entry->link) {
76+
if (UNEXPECTED(++depth > max_depth)) {
77+
return NULL;
7778
}
78-
return phar_get_link_source(link_entry);
79-
} else {
80-
if (link != entry->link) {
81-
efree(link);
79+
link = phar_get_link_location(entry);
80+
81+
if (NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->link, strlen(entry->link))) ||
82+
NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), link, strlen(link)))) {
83+
if (link != entry->link) {
84+
efree(link);
85+
}
86+
entry = link_entry;
87+
} else {
88+
if (link != entry->link) {
89+
efree(link);
90+
}
91+
return NULL;
8292
}
83-
return NULL;
8493
}
94+
return entry;
8595
}
8696
/* }}} */
8797

0 commit comments

Comments
 (0)