diff --git a/NEWS b/NEWS
index 23212414d361..9629191f19af 100644
--- a/NEWS
+++ b/NEWS
@@ -176,6 +176,8 @@ PHP NEWS
represented as a string anymore but a int. (David Carlier)
. Fixed bug GH-21421 (SoapClient typemap property breaks engine assumptions).
(ndossche)
+ . Fixed bug GH-22167 (Out-of-range XML Schema integer values were silently
+ accepted during WSDL parsing). (Weilin Du)
- Sockets:
. Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in
diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c
index 73d6691af262..5f5b176d26c5 100644
--- a/ext/soap/php_schema.c
+++ b/ext/soap/php_schema.c
@@ -53,6 +53,30 @@ static bool node_is_equal_xsd(xmlNodePtr node, const char *name)
return node_is_equal_ex_one_of(node, name, ns);
}
+static int schema_parse_int(const xmlChar *value, const char *name)
+{
+ const char *str = (const char *) value;
+ zend_long lval = 0;
+ int oflow_info = 0;
+ uint8_t type = is_numeric_string_ex(str, strlen(str), &lval, NULL, true, &oflow_info, NULL);
+
+ if (oflow_info > 0 || (type == IS_LONG && ZEND_LONG_INT_OVFL(lval))) {
+ soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
+ }
+
+ if (type == IS_LONG) {
+ return (int) lval;
+ }
+
+ errno = 0;
+ lval = ZEND_STRTOL(str, NULL, 10);
+ if ((errno == ERANGE && lval > 0) || ZEND_LONG_INT_OVFL(lval)) {
+ soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
+ }
+
+ return (int) lval;
+}
+
static encodePtr create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlChar *ns, const xmlChar *type)
{
smart_str nscat = {0};
@@ -854,7 +878,7 @@ static int schema_restriction_var_int(xmlNodePtr val, sdlRestrictionIntPtr *valp
soap_error0(E_ERROR, "Parsing Schema: missing restriction value");
}
- (*valptr)->value = atoi((char*)value->children->content);
+ (*valptr)->value = schema_parse_int(value->children->content, (const char *) val->name);
return TRUE;
}
@@ -1016,7 +1040,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model)
xmlAttrPtr attr = get_attribute(node->properties, "minOccurs");
if (attr) {
- model->min_occurs = atoi((char*)attr->children->content);
+ model->min_occurs = schema_parse_int(attr->children->content, "minOccurs");
} else {
model->min_occurs = 1;
}
@@ -1026,7 +1050,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model)
if (!strncmp((char*)attr->children->content, "unbounded", sizeof("unbounded"))) {
model->max_occurs = -1;
} else {
- model->max_occurs = atoi((char*)attr->children->content);
+ model->max_occurs = schema_parse_int(attr->children->content, "maxOccurs");
}
} else {
model->max_occurs = 1;
diff --git a/ext/soap/tests/bugs/gh22167.phpt b/ext/soap/tests/bugs/gh22167.phpt
new file mode 100644
index 000000000000..6fe9aef5475a
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22167.phpt
@@ -0,0 +1,122 @@
+--TEST--
+GH-22167 (Out-of-range XML Schema integer values in SOAP WSDL)
+--EXTENSIONS--
+soap
+--INI--
+soap.wsdl_cache_enabled=0
+--FILE--
+
+
+
+
+ $schema
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+XML;
+}
+
+function occurrence_schema(string $attribute, string $value = "2147483648"): string {
+ return <<
+
+
+
+
+XML;
+}
+
+function restriction_schema(string $facet, string $value = "2147483648"): string {
+ return <<
+
+
+
+
+XML;
+}
+
+$cases = [
+ "minOccurs" => occurrence_schema("minOccurs"),
+ "maxOccurs" => occurrence_schema("maxOccurs"),
+ "minExclusive" => restriction_schema("minExclusive"),
+ "minInclusive" => restriction_schema("minInclusive"),
+ "maxExclusive" => restriction_schema("maxExclusive"),
+ "maxInclusive" => restriction_schema("maxInclusive"),
+ "totalDigits" => restriction_schema("totalDigits"),
+ "fractionDigits" => restriction_schema("fractionDigits"),
+ "length" => restriction_schema("length"),
+ "minLength" => restriction_schema("minLength"),
+ "maxLength" => restriction_schema("maxLength"),
+];
+
+$numeric_string_cases = [
+ "leading whitespace numeric-string" => " 2147483648",
+ "leading plus numeric-string" => "+2147483648",
+ "leading zero numeric-string" => "00000000002147483648",
+ "leading numeric-string with trailing data" => "2147483648abc",
+ "decimal numeric-string" => "2147483648.0",
+ "exponent numeric-string" => "2147483648e0",
+];
+
+foreach ($numeric_string_cases as $name => $value) {
+ $cases[$name] = occurrence_schema("maxOccurs", $value);
+}
+
+$cases["fractional numeric-string within int range"] = occurrence_schema("maxOccurs", "3.141");
+
+foreach ($cases as $name => $schema) {
+ $file = tempnam(sys_get_temp_dir(), "wsdl");
+ file_put_contents($file, wsdl_with_schema($schema));
+
+ try {
+ new SoapClient($file, ["cache_wsdl" => WSDL_CACHE_NONE]);
+ echo "$name: parsed\n";
+ } catch (SoapFault $e) {
+ echo "$name: {$e->getMessage()}\n";
+ } finally {
+ unlink($file);
+ }
+}
+?>
+--EXPECT--
+minOccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range
+maxOccurs: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+minExclusive: SOAP-ERROR: Parsing Schema: minExclusive value is out of range
+minInclusive: SOAP-ERROR: Parsing Schema: minInclusive value is out of range
+maxExclusive: SOAP-ERROR: Parsing Schema: maxExclusive value is out of range
+maxInclusive: SOAP-ERROR: Parsing Schema: maxInclusive value is out of range
+totalDigits: SOAP-ERROR: Parsing Schema: totalDigits value is out of range
+fractionDigits: SOAP-ERROR: Parsing Schema: fractionDigits value is out of range
+length: SOAP-ERROR: Parsing Schema: length value is out of range
+minLength: SOAP-ERROR: Parsing Schema: minLength value is out of range
+maxLength: SOAP-ERROR: Parsing Schema: maxLength value is out of range
+leading whitespace numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+leading plus numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+leading zero numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+leading numeric-string with trailing data: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+decimal numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+exponent numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range
+fractional numeric-string within int range: parsed