Skip to content

Commit b205b6f

Browse files
committed
Merge branch 'PHP-8.6'
* PHP-8.6: ext/soap: to_xml_array() heap use-after-free with illegal iterator keys.
2 parents a0efab2 + 58f680c commit b205b6f

3 files changed

Lines changed: 112 additions & 5 deletions

File tree

‎Zend/zend_API.c‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2309,6 +2309,9 @@ ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) /
23092309
break;
23102310
case IS_RESOURCE:
23112311
zend_use_resource_as_offset(key);
2312+
if (UNEXPECTED(EG(exception))) {
2313+
return FAILURE;
2314+
}
23122315
result = zend_hash_index_update(ht, Z_RES_HANDLE_P(key), value);
23132316
break;
23142317
case IS_FALSE:
@@ -2320,9 +2323,14 @@ ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) /
23202323
case IS_LONG:
23212324
result = zend_hash_index_update(ht, Z_LVAL_P(key), value);
23222325
break;
2323-
case IS_DOUBLE:
2324-
result = zend_hash_index_update(ht, zend_dval_to_lval_safe(Z_DVAL_P(key)), value);
2326+
case IS_DOUBLE: {
2327+
zend_long lval = zend_dval_to_lval_safe(Z_DVAL_P(key));
2328+
if (UNEXPECTED(EG(exception))) {
2329+
return FAILURE;
2330+
}
2331+
result = zend_hash_index_update(ht, lval, value);
23252332
break;
2333+
}
23262334
case IS_NULL:
23272335
zend_error(E_DEPRECATED, "Using null as an array offset is deprecated, use an empty string instead");
23282336
if (UNEXPECTED(EG(exception))) {

‎ext/soap/php_encoding.c‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,13 +2302,15 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod
23022302
if (EG(exception)) {
23032303
goto iterator_done;
23042304
}
2305-
array_set_zval_key(Z_ARRVAL(array_copy), &key, val);
2306-
zval_ptr_dtor(val);
2305+
zend_result status = array_set_zval_key(Z_ARRVAL(array_copy), &key, val);
23072306
zval_ptr_dtor(&key);
2307+
if (status == FAILURE) {
2308+
goto iterator_done;
2309+
}
23082310
} else {
2311+
Z_TRY_ADDREF_P(val);
23092312
add_next_index_zval(&array_copy, val);
23102313
}
2311-
Z_TRY_ADDREF_P(val);
23122314

23132315
iter->funcs->move_forward(iter);
23142316
if (EG(exception)) {

‎ext/soap/tests/bugs/gh22895.phpt‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
--TEST--
2+
GH-22895 (Heap use-after-free while encoding a Traversable with an illegal key)
3+
--CREDITS--
4+
Amorsec
5+
--EXTENSIONS--
6+
soap
7+
--FILE--
8+
<?php
9+
class LocalSoapClient extends SoapClient
10+
{
11+
public function __doRequest(
12+
$request,
13+
$location,
14+
$action,
15+
$version,
16+
$one_way = false
17+
): ?string {
18+
return '';
19+
}
20+
}
21+
22+
class ArrayKeyIterator implements Iterator
23+
{
24+
private int $i = 0;
25+
26+
public function current(): mixed
27+
{
28+
return new stdClass();
29+
}
30+
31+
public function key(): mixed
32+
{
33+
return ['illegal', 'key'];
34+
}
35+
36+
public function next(): void
37+
{
38+
$this->i++;
39+
}
40+
41+
public function rewind(): void
42+
{
43+
$this->i = 0;
44+
}
45+
46+
public function valid(): bool
47+
{
48+
return $this->i < 2;
49+
}
50+
}
51+
52+
$client = new LocalSoapClient(null, [
53+
'location' => 'http://127.0.0.1/',
54+
'uri' => 'urn:audit',
55+
'trace' => 1,
56+
]);
57+
58+
$multiple = new MultipleIterator();
59+
$multiple->attachIterator(new ArrayIterator([0]));
60+
61+
foreach ([$multiple, new ArrayKeyIterator()] as $iterator) {
62+
try {
63+
$client->__soapCall('audit', [new SoapVar($iterator, SOAP_ENC_ARRAY)]);
64+
} catch (TypeError $e) {
65+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
66+
}
67+
}
68+
69+
function gen($key) {
70+
yield $key => new stdClass();
71+
yield 2 => new stdClass();
72+
}
73+
74+
set_error_handler(function ($errno, $errstr) {
75+
throw new Exception($errstr);
76+
});
77+
foreach ([1.5, STDIN, null] as $key) {
78+
try {
79+
$client->__soapCall('audit', [new SoapVar(gen($key), SOAP_ENC_ARRAY)]);
80+
} catch (Exception $e) {
81+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
82+
}
83+
}
84+
restore_error_handler();
85+
86+
/* A key the encoder can use must still be serialized, without leaking. */
87+
$client->__soapCall('audit', [new SoapVar(new ArrayIterator(['a' => 1]), SOAP_ENC_ARRAY)]);
88+
echo $client->__getLastRequest();
89+
?>
90+
--EXPECTF--
91+
TypeError: Cannot access offset of type array on array
92+
TypeError: Cannot access offset of type array on array
93+
Exception: Implicit conversion from float 1.5 to int loses precision
94+
Exception: Resource ID#%d used as offset, casting to integer (%d)
95+
Exception: Using null as an array offset is deprecated, use an empty string instead
96+
<?xml version="1.0" encoding="UTF-8"?>
97+
<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>

0 commit comments

Comments
 (0)