Skip to content

Commit 5acf743

Browse files
committed
Fix GH-22167: reject out-of-range SOAP schema integers
1 parent 673cb6d commit 5acf743

3 files changed

Lines changed: 150 additions & 3 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ PHP NEWS
1515
IntlCalendar::equals(), ::before(), ::after(), and ::isEquivalentTo().
1616
(Weilin Du)
1717

18+
- SOAP:
19+
. Fixed bug GH-22167 (Out-of-range XML Schema integer values were silently
20+
accepted during WSDL parsing). (Weilin Du)
21+
1822
- Zlib:
1923
. Fixed memory leak if deflate initialization fails and there is a dict.
2024
(ndossche)

ext/soap/php_schema.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ static int schema_restriction_var_char(xmlNodePtr val, sdlRestrictionCharPtr *va
4343

4444
static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type);
4545

46+
static int schema_parse_int(const xmlChar *value, const char *name)
47+
{
48+
const char *str = (const char *) value;
49+
zend_long lval = 0;
50+
int oflow_info = 0;
51+
uint8_t type = is_numeric_string_ex(str, strlen(str), &lval, NULL, true, &oflow_info, NULL);
52+
53+
if (oflow_info > 0 || (type == IS_LONG && ZEND_LONG_INT_OVFL(lval))) {
54+
soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
55+
}
56+
57+
if (type == IS_LONG) {
58+
return (int) lval;
59+
}
60+
61+
errno = 0;
62+
lval = ZEND_STRTOL(str, NULL, 10);
63+
if ((errno == ERANGE && lval > 0) || ZEND_LONG_INT_OVFL(lval)) {
64+
soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
65+
}
66+
67+
return (int) lval;
68+
}
69+
4670
static encodePtr create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlChar *ns, const xmlChar *type)
4771
{
4872
smart_str nscat = {0};
@@ -844,7 +868,7 @@ static int schema_restriction_var_int(xmlNodePtr val, sdlRestrictionIntPtr *valp
844868
soap_error0(E_ERROR, "Parsing Schema: missing restriction value");
845869
}
846870

847-
(*valptr)->value = atoi((char*)value->children->content);
871+
(*valptr)->value = schema_parse_int(value->children->content, (const char *) val->name);
848872

849873
return TRUE;
850874
}
@@ -1006,7 +1030,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model)
10061030
xmlAttrPtr attr = get_attribute(node->properties, "minOccurs");
10071031

10081032
if (attr) {
1009-
model->min_occurs = atoi((char*)attr->children->content);
1033+
model->min_occurs = schema_parse_int(attr->children->content, "minOccurs");
10101034
} else {
10111035
model->min_occurs = 1;
10121036
}
@@ -1016,7 +1040,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model)
10161040
if (!strncmp((char*)attr->children->content, "unbounded", sizeof("unbounded"))) {
10171041
model->max_occurs = -1;
10181042
} else {
1019-
model->max_occurs = atoi((char*)attr->children->content);
1043+
model->max_occurs = schema_parse_int(attr->children->content, "maxOccurs");
10201044
}
10211045
} else {
10221046
model->max_occurs = 1;

ext/soap/tests/bugs/gh22167.phpt

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
--TEST--
2+
GH-22167 (Out-of-range XML Schema integer values in SOAP WSDL)
3+
--EXTENSIONS--
4+
soap
5+
--INI--
6+
soap.wsdl_cache_enabled=0
7+
--FILE--
8+
<?php
9+
function wsdl_with_schema(string $schema): string {
10+
return <<<XML
11+
<?xml version="1.0"?>
12+
<definitions
13+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
14+
xmlns:tns="http://test-uri/"
15+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
16+
xmlns="http://schemas.xmlsoap.org/wsdl/"
17+
targetNamespace="http://test-uri/">
18+
<types>
19+
<xsd:schema targetNamespace="http://test-uri/">
20+
$schema
21+
</xsd:schema>
22+
</types>
23+
<message name="m"><part name="p" type="tns:T"/></message>
24+
<portType name="p"><operation name="op"><input message="tns:m"/></operation></portType>
25+
<binding name="b" type="tns:p">
26+
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
27+
<operation name="op">
28+
<soap:operation soapAction="#op"/>
29+
<input>
30+
<soap:body use="encoded"
31+
namespace="http://test-uri/"
32+
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
33+
</input>
34+
</operation>
35+
</binding>
36+
<service name="s"><port name="p" binding="tns:b"><soap:address location="test://"/></port></service>
37+
</definitions>
38+
XML;
39+
}
40+
41+
function occurrence_schema(string $attribute, string $value = "2147483648"): string {
42+
return <<<XML
43+
<xsd:complexType name="T">
44+
<xsd:sequence>
45+
<xsd:element name="x" type="xsd:string" $attribute="$value"/>
46+
</xsd:sequence>
47+
</xsd:complexType>
48+
XML;
49+
}
50+
51+
function restriction_schema(string $facet, string $value = "2147483648"): string {
52+
return <<<XML
53+
<xsd:simpleType name="T">
54+
<xsd:restriction base="xsd:int">
55+
<xsd:$facet value="$value"/>
56+
</xsd:restriction>
57+
</xsd:simpleType>
58+
XML;
59+
}
60+
61+
$cases = [
62+
"minOccurs" => occurrence_schema("minOccurs"),
63+
"maxOccurs" => occurrence_schema("maxOccurs"),
64+
"minExclusive" => restriction_schema("minExclusive"),
65+
"minInclusive" => restriction_schema("minInclusive"),
66+
"maxExclusive" => restriction_schema("maxExclusive"),
67+
"maxInclusive" => restriction_schema("maxInclusive"),
68+
"totalDigits" => restriction_schema("totalDigits"),
69+
"fractionDigits" => restriction_schema("fractionDigits"),
70+
"length" => restriction_schema("length"),
71+
"minLength" => restriction_schema("minLength"),
72+
"maxLength" => restriction_schema("maxLength"),
73+
];
74+
75+
$numeric_string_cases = [
76+
"leading whitespace numeric-string" => " 2147483648",
77+
"leading plus numeric-string" => "+2147483648",
78+
"leading zero numeric-string" => "00000000002147483648",
79+
"leading numeric-string with trailing data" => "2147483648abc",
80+
"decimal numeric-string" => "2147483648.0",
81+
"exponent numeric-string" => "2147483648e0",
82+
];
83+
84+
foreach ($numeric_string_cases as $name => $value) {
85+
$cases[$name] = occurrence_schema("maxOccurs", $value);
86+
}
87+
88+
foreach ($cases as $name => $schema) {
89+
$file = tempnam(sys_get_temp_dir(), "wsdl");
90+
file_put_contents($file, wsdl_with_schema($schema));
91+
92+
try {
93+
new SoapClient($file, ["cache_wsdl" => WSDL_CACHE_NONE]);
94+
echo "$name: parsed\n";
95+
} catch (SoapFault $e) {
96+
echo "$name: {$e->getMessage()}\n";
97+
} finally {
98+
unlink($file);
99+
}
100+
}
101+
?>
102+
--EXPECT--
103+
minOccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range
104+
maxOccurs: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
105+
minExclusive: SOAP-ERROR: Parsing Schema: minExclusive value is out of range
106+
minInclusive: SOAP-ERROR: Parsing Schema: minInclusive value is out of range
107+
maxExclusive: SOAP-ERROR: Parsing Schema: maxExclusive value is out of range
108+
maxInclusive: SOAP-ERROR: Parsing Schema: maxInclusive value is out of range
109+
totalDigits: SOAP-ERROR: Parsing Schema: totalDigits value is out of range
110+
fractionDigits: SOAP-ERROR: Parsing Schema: fractionDigits value is out of range
111+
length: SOAP-ERROR: Parsing Schema: length value is out of range
112+
minLength: SOAP-ERROR: Parsing Schema: minLength value is out of range
113+
maxLength: SOAP-ERROR: Parsing Schema: maxLength value is out of range
114+
leading whitespace numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
115+
leading plus numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
116+
leading zero numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
117+
leading numeric-string with trailing data: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
118+
decimal numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
119+
exponent numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range

0 commit comments

Comments
 (0)