php/php-cve2026-7260.patch
Remi Collet 1a03b9047c - Fix leak on double DatePeriod::__construct() call
- Fixed SQL injection via E'...' backslash breakout CVE-2026-17543
- Fixed GHSA-vc5h-9ppw-p5f3 Crash via recursive symlinks  CVE-2026-7260

Resolves: RHEL-223940
2026-08-04 11:29:11 +02:00

103 lines
3.1 KiB
Diff

From 8.2.33, without binary diffs
From 92458605f7f88697973e1183b46296d4c9bf9c46 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Sun, 3 May 2026 19:26:31 +0200
Subject: [PATCH 3/5] Fix GHSA-vc5h-9ppw-p5f3: phar circular symlink crash
Prevents infinite recursion in phar_get_link_source.
(cherry picked from commit 2e0fa0a44441d74bf8cc4e1ce1c8af9cd4209f52)
(cherry picked from commit c84ffef248fddba29bde47847bd4cd06761e0aab)
---
.../tests/tar/files/circular_symlinks.tar | Bin 0 -> 10240 bytes
.../tar/files/circular_symlinks_long.tar | Bin 0 -> 215040 bytes
.../tests/tar/files/circular_symlinks_rho.tar | Bin 0 -> 10240 bytes
.../ghsa-vc5h-9ppw-p5f3-symlink-circular.phpt | 27 ++++++++++
ext/phar/util.c | 51 ++++++++++++++----
5 files changed, 67 insertions(+), 11 deletions(-)
create mode 100644 ext/phar/tests/tar/files/circular_symlinks.tar
create mode 100644 ext/phar/tests/tar/files/circular_symlinks_long.tar
create mode 100644 ext/phar/tests/tar/files/circular_symlinks_rho.tar
create mode 100644 ext/phar/tests/tar/ghsa-vc5h-9ppw-p5f3-symlink-circular.phpt
diff --git a/ext/phar/util.c b/ext/phar/util.c
index 59362bbc72..d49ea9bfc5 100644
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@ -57,30 +57,59 @@ static char *phar_get_link_location(phar_entry_info *entry) /* {{{ */
}
/* }}} */
-phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */
+static phar_entry_info *phar_follow_one_link(phar_entry_info *entry)
{
phar_entry_info *link_entry;
char *link;
- 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);
}
- return phar_get_link_source(link_entry);
- } else {
- if (link != entry->link) {
- efree(link);
+ return link_entry;
+ }
+
+ if (link != entry->link) {
+ efree(link);
+ }
+ return NULL;
+}
+
+phar_entry_info *phar_get_link_source(phar_entry_info *entry)
+{
+ phar_entry_info *slow, *fast;
+
+ if (!entry->link) {
+ return entry;
+ }
+
+ /*
+ * Use Floyd's cycle detection algorithm to follow the symlink chain without unbounded
+ * recursion. Each entry has at most one outgoing link, so if a cycle exists the fast pointer
+ * will eventually meet the slow one. Otherwise the fast pointer reaches the end first.
+ */
+ slow = fast = entry;
+ while (1) {
+ fast = phar_follow_one_link(fast);
+ if (!fast || !fast->link) {
+ return fast;
+ }
+ fast = phar_follow_one_link(fast);
+ if (!fast || !fast->link) {
+ return fast;
+ }
+
+ /* no need to check slow as it's always behind */
+ slow = phar_follow_one_link(slow);
+
+ if (slow == fast) {
+ /* circular symlink chain */
+ return NULL;
}
- return NULL;
}
}
-/* }}} */
/* retrieve a phar_entry_info's current file pointer for reading contents */
php_stream *phar_get_efp(phar_entry_info *entry, int follow_links) /* {{{ */
--
2.55.0