From b2de3cf170f81b203dfc2622f2f2dd1572ef95be Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 8 May 2026 22:33:59 +0100 Subject: [PATCH 1/2] 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 --- NEWS | 4 +++ ext/phar/tests/tar/gh21986.phpt | 43 +++++++++++++++++++++++++++++++++ ext/phar/util.c | 30 +++++++++++++++-------- 3 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 ext/phar/tests/tar/gh21986.phpt diff --git a/NEWS b/NEWS index 80567e1ff2b8..0dd41914ed4f 100644 --- a/NEWS +++ b/NEWS @@ -31,6 +31,10 @@ PHP NEWS - OpenSSL: . Fix compatibility issues with OpenSSL 4.0. (jordikroon, Remi) +- Phar: + . Fixed bug GH-21986 (PharData::getContent() crash on infinite recursion + with symlinks). (David Carlier) + - SOAP: . Fixed integer overflow when decoding SOAP array indexes. (Weilin Du) diff --git a/ext/phar/tests/tar/gh21986.phpt b/ext/phar/tests/tar/gh21986.phpt new file mode 100644 index 000000000000..13205d40a65d --- /dev/null +++ b/ext/phar/tests/tar/gh21986.phpt @@ -0,0 +1,43 @@ +--TEST-- +GH-21986 (PharData::getContent() crash on circular symlink chain in tar) +--EXTENSIONS-- +phar +--FILE-- +getContent()); +?> +--CLEAN-- + +--EXPECT-- +string(0) "" diff --git a/ext/phar/util.c b/ext/phar/util.c index fe177f964443..e2d1076921f2 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -64,24 +64,34 @@ phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */ { phar_entry_info *link_entry; char *link; + uint32_t depth = 0, max_depth; if (!entry->link) { return entry; } - link = phar_get_link_location(entry); - if (NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->link, strlen(entry->link))) || - NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), link, strlen(link)))) { - if (link != entry->link) { - efree(link); + max_depth = zend_hash_num_elements(&(entry->phar->manifest)); + + while (entry->link) { + if (UNEXPECTED(++depth > max_depth)) { + return NULL; } - return phar_get_link_source(link_entry); - } else { - if (link != entry->link) { - efree(link); + link = phar_get_link_location(entry); + + if (NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->link, strlen(entry->link))) || + NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), link, strlen(link)))) { + if (link != entry->link) { + efree(link); + } + entry = link_entry; + } else { + if (link != entry->link) { + efree(link); + } + return NULL; } - return NULL; } + return entry; } /* }}} */ From f51c8d548d5004de895fffa6e12a8ef83f07cce8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 19 May 2026 23:01:23 +0100 Subject: [PATCH 2/2] ext/phar: fix for GH-21986 for master --- ext/phar/util.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ext/phar/util.c b/ext/phar/util.c index 829abc5dfea3..e95b3ab7574b 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -82,14 +82,10 @@ phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */ if (NULL != (link_entry = zend_hash_find_ptr(&(entry->phar->manifest), entry->symlink)) || NULL != (link_entry = zend_hash_find_ptr(&(entry->phar->manifest), link))) { - if (link != entry->symlink) { - zend_string_release(link); - } + zend_string_release(link); entry = link_entry; } else { - if (link != entry->symlink) { - zend_string_release(link); - } + zend_string_release(link); return NULL; } }