Skip to content

Commit 2913f76

Browse files
committed
Fix OOB access on a SOAP array with a dimensionless arraySize
calc_dimension_12() returned 0 for an arraySize holding neither a digit nor a '*', so to_zval_array() read pos[0] and to_xml_array() read and wrote dims[0] on a zero-element allocation. Treat such a value as one dimension of unspecified size, like a missing attribute. Fixes GH-22887
1 parent 68d605f commit 2913f76

3 files changed

Lines changed: 202 additions & 1 deletion

File tree

ext/soap/php_encoding.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,8 @@ static int calc_dimension_12(const char* str)
20732073
}
20742074
str++;
20752075
}
2076-
return i;
2076+
/* arraySize without dimension => one-dimension of unspecified size */
2077+
return i > 0 ? i : 1;
20772078
}
20782079

20792080
static void soap_array_position_add_digit(int *position, int digit)

ext/soap/tests/gh22887.phpt

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
--TEST--
2+
GH-22887 (heap out-of-bounds read while decoding an array with an empty arraySize)
3+
--EXTENSIONS--
4+
soap
5+
--FILE--
6+
<?php
7+
class TestSoapClient extends SoapClient {
8+
public string $response = '';
9+
public string $request = '';
10+
11+
public function __doRequest(string $request, string $location, string $action, int $version, bool $oneWay = false): ?string {
12+
$this->request = $request;
13+
return $this->response;
14+
}
15+
}
16+
17+
function soap_response(int $version, string $attributes): string {
18+
$envelope = $version === SOAP_1_2
19+
? 'http://www.w3.org/2003/05/soap-envelope'
20+
: 'http://schemas.xmlsoap.org/soap/envelope/';
21+
$encoding = $version === SOAP_1_2
22+
? 'http://www.w3.org/2003/05/soap-encoding'
23+
: 'http://schemas.xmlsoap.org/soap/encoding/';
24+
25+
return <<<XML
26+
<?xml version="1.0" encoding="UTF-8"?>
27+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="$envelope"
28+
xmlns:SOAP-ENC="$encoding"
29+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
30+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
31+
xmlns:ns1="http://example.org/">
32+
<SOAP-ENV:Body>
33+
<ns1:testResponse SOAP-ENV:encodingStyle="$encoding">
34+
<return $attributes>
35+
<item xsi:type="xsd:string">first</item>
36+
<item xsi:type="xsd:string">second</item>
37+
</return>
38+
</ns1:testResponse>
39+
</SOAP-ENV:Body>
40+
</SOAP-ENV:Envelope>
41+
XML;
42+
}
43+
44+
/* Decoding: an arraySize holding no dimension at all must not be trusted as a
45+
* zero-dimensional array. */
46+
foreach ([SOAP_1_1 => '1.1', SOAP_1_2 => '1.2'] as $version => $label) {
47+
foreach ([
48+
'SOAP-ENC:arraySize=""',
49+
'SOAP-ENC:arraySize=" "',
50+
'SOAP-ENC:arraySize="abc"',
51+
'SOAP-ENC:itemType="xsd:string" SOAP-ENC:arraySize=""',
52+
'SOAP-ENC:arraySize="2"',
53+
] as $attributes) {
54+
$client = new TestSoapClient(null, [
55+
'location' => 'test://',
56+
'uri' => 'http://example.org/',
57+
'soap_version' => $version,
58+
]);
59+
$client->response = soap_response($version, $attributes);
60+
61+
echo "SOAP $label, $attributes:", PHP_EOL;
62+
var_dump($client->test());
63+
}
64+
}
65+
66+
/* Encoding: same thing for an arraySize coming from a WSDL. */
67+
$client = new TestSoapClient(__DIR__ . '/gh22887.wsdl', [
68+
'cache_wsdl' => WSDL_CACHE_NONE,
69+
'soap_version' => SOAP_1_2,
70+
]);
71+
$client->test(['first', 'second']);
72+
73+
$request = new DOMDocument();
74+
$request->loadXML($client->request);
75+
$value = $request->getElementsByTagName('value')->item(0);
76+
echo 'arraySize: ', var_export($value->getAttribute('enc:arraySize'), true), PHP_EOL;
77+
echo 'items: ', $value->getElementsByTagName('item')->length, PHP_EOL;
78+
?>
79+
--EXPECT--
80+
SOAP 1.1, SOAP-ENC:arraySize="":
81+
array(2) {
82+
[0]=>
83+
string(5) "first"
84+
[1]=>
85+
string(6) "second"
86+
}
87+
SOAP 1.1, SOAP-ENC:arraySize=" ":
88+
array(2) {
89+
[0]=>
90+
string(5) "first"
91+
[1]=>
92+
string(6) "second"
93+
}
94+
SOAP 1.1, SOAP-ENC:arraySize="abc":
95+
array(2) {
96+
[0]=>
97+
string(5) "first"
98+
[1]=>
99+
string(6) "second"
100+
}
101+
SOAP 1.1, SOAP-ENC:itemType="xsd:string" SOAP-ENC:arraySize="":
102+
array(2) {
103+
[0]=>
104+
string(5) "first"
105+
[1]=>
106+
string(6) "second"
107+
}
108+
SOAP 1.1, SOAP-ENC:arraySize="2":
109+
array(2) {
110+
[0]=>
111+
string(5) "first"
112+
[1]=>
113+
string(6) "second"
114+
}
115+
SOAP 1.2, SOAP-ENC:arraySize="":
116+
array(2) {
117+
[0]=>
118+
string(5) "first"
119+
[1]=>
120+
string(6) "second"
121+
}
122+
SOAP 1.2, SOAP-ENC:arraySize=" ":
123+
array(2) {
124+
[0]=>
125+
string(5) "first"
126+
[1]=>
127+
string(6) "second"
128+
}
129+
SOAP 1.2, SOAP-ENC:arraySize="abc":
130+
array(2) {
131+
[0]=>
132+
string(5) "first"
133+
[1]=>
134+
string(6) "second"
135+
}
136+
SOAP 1.2, SOAP-ENC:itemType="xsd:string" SOAP-ENC:arraySize="":
137+
array(2) {
138+
[0]=>
139+
string(5) "first"
140+
[1]=>
141+
string(6) "second"
142+
}
143+
SOAP 1.2, SOAP-ENC:arraySize="2":
144+
array(2) {
145+
[0]=>
146+
string(5) "first"
147+
[1]=>
148+
string(6) "second"
149+
}
150+
arraySize: '2'
151+
items: 2

ext/soap/tests/gh22887.wsdl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" name="gh22887" targetNamespace="urn:gh22887"
3+
xmlns:tns="urn:gh22887"
4+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
5+
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
6+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
7+
xmlns:soap-enc="http://www.w3.org/2003/05/soap-encoding">
8+
<types>
9+
<xsd:schema targetNamespace="urn:gh22887">
10+
<xsd:complexType name="ArrayOfString">
11+
<xsd:complexContent>
12+
<xsd:restriction base="soap-enc:Array">
13+
<xsd:attribute ref="soap-enc:itemType" wsdl:itemType="xsd:string"/>
14+
<xsd:attribute ref="soap-enc:arraySize" wsdl:arraySize=""/>
15+
</xsd:restriction>
16+
</xsd:complexContent>
17+
</xsd:complexType>
18+
</xsd:schema>
19+
</types>
20+
<message name="testRequest">
21+
<part name="value" type="tns:ArrayOfString"/>
22+
</message>
23+
<message name="testResponse">
24+
<part name="return" type="xsd:string"/>
25+
</message>
26+
<portType name="testPortType">
27+
<operation name="test">
28+
<input message="tns:testRequest"/>
29+
<output message="tns:testResponse"/>
30+
</operation>
31+
</portType>
32+
<binding name="testBinding" type="tns:testPortType">
33+
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
34+
<operation name="test">
35+
<soap:operation soapAction="urn:gh22887#test"/>
36+
<input>
37+
<soap:body use="encoded" namespace="urn:gh22887" encodingStyle="http://www.w3.org/2003/05/soap-encoding"/>
38+
</input>
39+
<output>
40+
<soap:body use="encoded" namespace="urn:gh22887" encodingStyle="http://www.w3.org/2003/05/soap-encoding"/>
41+
</output>
42+
</operation>
43+
</binding>
44+
<service name="testService">
45+
<port name="testPort" binding="tns:testBinding">
46+
<soap:address location="test://"/>
47+
</port>
48+
</service>
49+
</definitions>

0 commit comments

Comments
 (0)