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)