[php-src] master: Merge branch 'PHP-8.4' into PHP-8.5

From: Date: Thu, 01 Oct 2026 18:16:26 +0000
Subject: [php-src] master: Merge branch 'PHP-8.4' into PHP-8.5
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-141850@lists.php.net to get a copy of this message
Author: David Carlier (devnexen) Date: 2026-10-01T19:12:52+01:00 Commit: https://github.com/php/php-src/commit/81a83bb33a62838a7f2986d02daf8769afac4ef4 Raw diff: https://github.com/php/php-src/commit/81a83bb33a62838a7f2986d02daf8769afac4ef4.diff Merge branch 'PHP-8.4' into PHP-8.5 * PHP-8.4: ext/soap: to_xml_array() heap use-after-free with illegal iterator keys. # Conflicts: # Zend/zend_API.c Changed paths: A ext/soap/tests/bugs/gh22895.phpt M NEWS M Zend/zend_API.c M ext/soap/php_encoding.c Diff: diff --git a/NEWS b/NEWS index 4749cdf78ceb..1cc0c1891fe5 100644 --- a/NEWS +++ b/NEWS @@ -172,6 +172,10 @@ PHP NEWS . Fixed reconstructing a SimpleXMLElement freeing a child element that another variable still references. (Ilia Alshanetsky) +- SOAP: + . Fixed bug GH-22895 (Heap use-after-free while encoding a Traversable with + an illegal key). (David Carlier) + - SQLite: . Fixed a crash when SQLite3::close() is called from a userland callback. (Ilia Alshanetsky) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5d1f58d61635..99d3125bf2b9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2263,6 +2263,9 @@ ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) / break; case IS_RESOURCE: zend_use_resource_as_offset(key); + if (UNEXPECTED(EG(exception))) { + return FAILURE; + } result = zend_hash_index_update(ht, Z_RES_HANDLE_P(key), value); break; case IS_FALSE: @@ -2274,9 +2277,14 @@ ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) / case IS_LONG: result = zend_hash_index_update(ht, Z_LVAL_P(key), value); break; - case IS_DOUBLE: - result = zend_hash_index_update(ht, zend_dval_to_lval_safe(Z_DVAL_P(key)), value); + case IS_DOUBLE: { + zend_long lval = zend_dval_to_lval_safe(Z_DVAL_P(key)); + if (UNEXPECTED(EG(exception))) { + return FAILURE; + } + result = zend_hash_index_update(ht, lval, value); break; + } case IS_NULL: zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead"); if (UNEXPECTED(EG(exception))) { diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index f34ad7c2a60c..0f33736ef0e7 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2292,13 +2292,15 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod if (EG(exception)) { goto iterator_done; } - array_set_zval_key(Z_ARRVAL(array_copy), &key, val); - zval_ptr_dtor(val); + zend_result status = array_set_zval_key(Z_ARRVAL(array_copy), &key, val); zval_ptr_dtor(&key); + if (status == FAILURE) { + goto iterator_done; + } } else { + Z_TRY_ADDREF_P(val); add_next_index_zval(&array_copy, val); } - Z_TRY_ADDREF_P(val); iter->funcs->move_forward(iter); if (EG(exception)) { diff --git a/ext/soap/tests/bugs/gh22895.phpt b/ext/soap/tests/bugs/gh22895.phpt new file mode 100644 index 000000000000..feed80065a46 --- /dev/null +++ b/ext/soap/tests/bugs/gh22895.phpt @@ -0,0 +1,97 @@ +--TEST-- +GH-22895 (Heap use-after-free while encoding a Traversable with an illegal key) +--CREDITS-- +Amorsec +--EXTENSIONS-- +soap +--FILE-- +<?php +class LocalSoapClient extends SoapClient +{ + public function __doRequest( + $request, + $location, + $action, + $version, + $one_way = false + ): ?string { + return ''; + } +} + +class ArrayKeyIterator implements Iterator +{ + private int $i = 0; + + public function current(): mixed + { + return new stdClass(); + } + + public function key(): mixed + { + return ['illegal', 'key']; + } + + public function next(): void + { + $this->i++; + } + + public function rewind(): void + { + $this->i = 0; + } + + public function valid(): bool + { + return $this->i < 2; + } +} + +$client = new LocalSoapClient(null, [ + 'location' => 'http://127.0.0.1/', + 'uri' => 'urn:audit', + 'trace' => 1, +]); + +$multiple = new MultipleIterator(); +$multiple->attachIterator(new ArrayIterator([0])); + +foreach ([$multiple, new ArrayKeyIterator()] as $iterator) { + try { + $client->__soapCall('audit', [new SoapVar($iterator, SOAP_ENC_ARRAY)]); + } catch (TypeError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +function gen($key) { + yield $key => new stdClass(); + yield 2 => new stdClass(); +} + +set_error_handler(function ($errno, $errstr) { + throw new Exception($errstr); +}); +foreach ([1.5, STDIN, null] as $key) { + try { + $client->__soapCall('audit', [new SoapVar(gen($key), SOAP_ENC_ARRAY)]); + } catch (Exception $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} +restore_error_handler(); + +/* A key the encoder can use must still be serialized, without leaking. */ +$client->__soapCall('audit', [new SoapVar(new ArrayIterator(['a' => 1]), SOAP_ENC_ARRAY)]); +echo $client->__getLastRequest(); +?> +--EXPECTF-- +TypeError: Cannot access offset of type array on array +TypeError: Cannot access offset of type array on array +Exception: Implicit conversion from float 1.5 to int loses precision +Exception: Resource ID#%d used as offset, casting to integer (%d) +Exception: Using null as an array offset is deprecated, use an empty string instead +<?xml version="1.0" encoding="UTF-8"?> +<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:audit" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:audit><param0 SOAP-ENC:arrayType="xsd:int[1]" xsi:type="SOAP-ENC:Array"><item xsi:type="xsd:int">1</item></param0></ns1:audit></SOAP-ENV:Body></SOAP-ENV:Envelope>

« previous php.cvs (#141850) next »