Commit 99ec10237ae for php
commit 99ec10237ae814ecc1cbccba945b02e7d2bd57c7
Author: lazerg <lazerg2@gmail.com>
Date: Sat Oct 31 22:23:51 2026 +0500
Fix GH-25092: Double free in Phar::copy() on link and mounted entries
Closes GH-25100.
diff --git a/NEWS b/NEWS
index bd81e07a3f3..a9c4db67e37 100644
--- a/NEWS
+++ b/NEWS
@@ -245,6 +245,8 @@ PHP NEWS
(Lazizbek Ergashev)
. Fixed bug GH-25091 (UAF in phar rename() when source and destination
are equal). (Lazizbek Ergashev)
+ . Fixed bug GH-25092 (Double free in Phar::copy() on link and
+ mounted entries). (Lazizbek Ergashev)
- Phpdbg:
. Fix wrong command names. (ndossche, Weilin Du)
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index e2f36f1c7b2..8b467a9abc8 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -3560,9 +3560,23 @@ PHP_METHOD(Phar, copy)
newentry.filename_len = tmp_len;
newentry.fp_refcount = 0;
+ if (newentry.link) {
+ newentry.link = estrdup(newentry.link);
+ }
+
+ if (newentry.tmp) {
+ newentry.tmp = estrdup(newentry.tmp);
+ }
+
if (oldentry->fp_type != PHAR_FP) {
if (FAILURE == phar_copy_entry_fp(oldentry, &newentry, &error)) {
efree(newentry.filename);
+ if (newentry.link) {
+ efree(newentry.link);
+ }
+ if (newentry.tmp) {
+ efree(newentry.tmp);
+ }
php_stream_close(newentry.fp);
zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error);
efree(error);
diff --git a/ext/phar/tests/gh25092.phpt b/ext/phar/tests/gh25092.phpt
new file mode 100644
index 00000000000..604094d7636
--- /dev/null
+++ b/ext/phar/tests/gh25092.phpt
@@ -0,0 +1,62 @@
+--TEST--
+GH-25092 (Double free in Phar::copy() on link and mounted entries)
+--EXTENSIONS--
+phar
+--INI--
+phar.readonly=0
+--FILE--
+<?php
+function tar_symlink($name, $target) {
+ $h = str_pad($name, 100, "\0");
+ $h .= "0000777\0";
+ $h .= "0000000\0";
+ $h .= "0000000\0";
+ $h .= "00000000000\0";
+ $h .= "00000000000\0";
+ $h .= str_repeat(' ', 8);
+ $h .= '2';
+ $h .= str_pad($target, 100, "\0");
+ $h .= "ustar\0" . "00";
+ $h = str_pad($h, 512, "\0");
+
+ $sum = 0;
+ for ($i = 0; $i < 512; $i++) {
+ $sum += ord($h[$i]);
+ }
+ return substr_replace($h, sprintf("%06o\0 ", $sum), 148, 8);
+}
+
+$tar = __DIR__ . '/gh25092.tar';
+file_put_contents($tar, tar_symlink('l', 'a') . str_repeat("\0", 1024));
+
+$t = new PharData($tar);
+$t->copy('l', 'l2');
+unset($t);
+
+$t = new PharData($tar);
+var_dump(isset($t['l']), isset($t['l2']));
+unset($t);
+
+$phar = __DIR__ . '/gh25092.phar';
+file_put_contents(__DIR__ . '/gh25092.txt', 'hello');
+
+$p = new Phar($phar);
+$p->addFromString('x.txt', 'x');
+$p->setStub('<?php __HALT_COMPILER(); ?>');
+unset($p);
+
+$p = new Phar($phar);
+Phar::mount('phar://' . $phar . '/m.txt', __DIR__ . '/gh25092.txt');
+$p->copy('m.txt', 'c.txt');
+var_dump(isset($p['c.txt']));
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/gh25092.tar');
+@unlink(__DIR__ . '/gh25092.phar');
+@unlink(__DIR__ . '/gh25092.txt');
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)