[php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6
| From: | David Carlier | Date: | Thu, 01 Oct 2026 18:16:26 +0000 |
| Subject: | [php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141851@lists.php.net to get a copy of this message | ||
Author: David Carlier (devnexen)
Date: 2026-10-01T19:13:18+01:00
Commit: https://github.com/php/php-src/commit/58f680c81e490bf45cbfb6dd42a17ed430cf8f41
Raw diff: https://github.com/php/php-src/commit/58f680c81e490bf45cbfb6dd42a17ed430cf8f41.diff
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
ext/soap: to_xml_array() heap use-after-free with illegal iterator keys.
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 47044f8d3e07..0277ee871e22 100644
--- a/NEWS
+++ b/NEWS
@@ -108,6 +108,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)
+
- Tidy:
. Fixed a use-after-free when a tidyNode is used after its document is
reparsed. (Ilia Alshanetsky)
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 0665a65b5dae..739bc9d240ea 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2310,6 +2310,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:
@@ -2321,9 +2324,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 646dc688abfb..73589025ccc6 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -2302,13 +2302,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>