Commit 0c4e3b6295b for php

commit 0c4e3b6295bce9a6ed1b08dc30ad4b2f94b86b47
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Sat Oct 3 09:54:37 2026 -0400

    ext/standard: Keep IPTC headers local to each call

    Give each iptcembed() invocation its own APP13 header so concurrent calls
    and reentrant output handlers cannot overwrite its pending segment length.

    Closes GH-24100

diff --git a/NEWS b/NEWS
index 931bdc47686..dacb602662f 100644
--- a/NEWS
+++ b/NEWS
@@ -201,6 +201,8 @@ PHP                                                                        NEWS
 - Standard:
   . Fixed sha1_file() returning a digest for incomplete data after a stream
     read failure. (Ilia Alshanetsky)
+  . Fixed iptcembed() corrupting JPEG headers when called recursively from an
+    output handler. (Ilia Alshanetsky)
   . Fixed three Windows-only proc_open() defects: an uninitialized
     PROCESS_INFORMATION, an indeterminate comspec pointer after a failed
     lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)
diff --git a/ext/standard/iptc.c b/ext/standard/iptc.c
index 0f46ecd19bc..85f54249dd5 100644
--- a/ext/standard/iptc.c
+++ b/ext/standard/iptc.c
@@ -175,11 +175,10 @@ static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf, c
 }
 /* }}} */

-static char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
-
 /* {{{ Embed binary IPTC data into a JPEG image. */
 PHP_FUNCTION(iptcembed)
 {
+	char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0\0";
 	char *iptcdata, *jpeg_file;
 	size_t iptcdata_len, jpeg_file_len;
 	zend_long spool = 0;
diff --git a/ext/standard/tests/image/iptcembed_reentrant.phpt b/ext/standard/tests/image/iptcembed_reentrant.phpt
new file mode 100644
index 00000000000..758e50378b0
--- /dev/null
+++ b/ext/standard/tests/image/iptcembed_reentrant.phpt
@@ -0,0 +1,34 @@
+--TEST--
+iptcembed() keeps APP13 headers local during reentrant output handling
+--FILE--
+<?php
+$file = __DIR__ . '/iptcembed_reentrant.jpg';
+file_put_contents($file, "\xff\xd8\xff\xe0\x00\x02\xff\xda\x00\x02");
+$output = '';
+$nested = false;
+ob_start(function ($chunk) use (&$output, &$nested, $file) {
+    $output .= $chunk;
+    if (!$nested && str_ends_with($output, "\xff\xed")) {
+        $nested = true;
+        iptcembed(str_repeat('B', 256), $file, 0);
+    }
+    return '';
+}, 1);
+iptcembed('AA', $file, 2);
+ob_end_flush();
+$start = strpos($output, "\xff\xed");
+echo "Nested call: ";
+var_dump($nested);
+echo "APP13 length: ";
+var_dump(unpack('n', substr($output, $start + 2, 2))[1]);
+echo "Outer payload: ";
+var_dump(substr($output, $start + 30, 2));
+?>
+--CLEAN--
+<?php
+unlink(__DIR__ . '/iptcembed_reentrant.jpg');
+?>
+--EXPECT--
+Nested call: bool(true)
+APP13 length: int(30)
+Outer payload: string(2) "AA"