Skip to content

Commit 0439e91

Browse files
committed
ext/soap: to_xml_array() heap use-after-free with illegal iterator keys.
Fix #22895 The return value of array_set_zval_key() was ignored, so when the key is not a legal array offset, e.g. MultipleIterator::key() returning an array, it threw and took no reference. The unconditional zval_ptr_dtor() then dropped the iterator's only reference to the borrowed value and the following Z_TRY_ADDREF_P() read freed memory. Let array_set_zval_key() own its reference the way spl_iterator_to_array_apply() does, and stop iterating on failure.
1 parent 68d605f commit 0439e91

2 files changed

Lines changed: 82 additions & 3 deletions

File tree

ext/soap/php_encoding.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,13 +2312,15 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod
23122312
if (EG(exception)) {
23132313
goto iterator_done;
23142314
}
2315-
array_set_zval_key(Z_ARRVAL(array_copy), &key, val);
2316-
zval_ptr_dtor(val);
2315+
zend_result status = array_set_zval_key(Z_ARRVAL(array_copy), &key, val);
23172316
zval_ptr_dtor(&key);
2317+
if (status == FAILURE) {
2318+
goto iterator_done;
2319+
}
23182320
} else {
2321+
Z_TRY_ADDREF_P(val);
23192322
add_next_index_zval(&array_copy, val);
23202323
}
2321-
Z_TRY_ADDREF_P(val);
23222324

23232325
iter->funcs->move_forward(iter);
23242326
if (EG(exception)) {

ext/soap/tests/bugs/gh22895.phpt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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->getMessage(), PHP_EOL;
66+
}
67+
}
68+
69+
/* A key the encoder can use must still be serialized, without leaking. */
70+
$client->__soapCall('audit', [new SoapVar(new ArrayIterator(['a' => 1]), SOAP_ENC_ARRAY)]);
71+
echo $client->__getLastRequest();
72+
?>
73+
--EXPECT--
74+
Cannot access offset of type array on array
75+
Cannot access offset of type array on array
76+
<?xml version="1.0" encoding="UTF-8"?>
77+
<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)