Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ 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; negative occurrence values are now rejected).
(Weilin Du)

- Sockets:
. Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in
Expand Down
5 changes: 5 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ PHP 8.6 UPGRADE NOTES
- mysqli
. Added new constant MYSQLI_OPT_COMPRESS.

- Soap:
. WSDL/XML Schema parsing now rejects out-of-range integer values for
occurrence constraints and integer restriction facets. Negative minOccurs
and maxOccurs values are rejected as well.

========================================
10. New Global Constants
========================================
Expand Down
28 changes: 25 additions & 3 deletions ext/soap/php_schema.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ 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, bool allow_negative)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workflow correct, can be possibly optimised/simplified like this

static int schema_parse_int(const xmlChar *value, const char *name, bool allow_negative)
  {
        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 (type != IS_LONG) {
                errno = 0;
                lval = ZEND_STRTOL(str, NULL, 10);
                if (oflow_info || (errno == ERANGE && lval != 0)) {
                        soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
                }
        }

        if (ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0)) {
                soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
        }

        return (int) lval;
  }

wdyt ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me :)

{
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 (type != IS_LONG) {
errno = 0;
lval = ZEND_STRTOL(str, NULL, 10);
if (oflow_info || (errno == ERANGE && lval != 0)) {
soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name);
}
}

if (ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0)) {
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};
Expand Down Expand Up @@ -854,7 +876,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, true);

return TRUE;
}
Expand Down Expand Up @@ -1016,7 +1038,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", false);
} else {
model->min_occurs = 1;
}
Expand All @@ -1026,7 +1048,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", false);
}
} else {
model->max_occurs = 1;
Expand Down
128 changes: 128 additions & 0 deletions ext/soap/tests/bugs/gh22167.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
--TEST--
GH-22167 (Out-of-range XML Schema integer values in SOAP WSDL)
--EXTENSIONS--
soap
--INI--
soap.wsdl_cache_enabled=0
--FILE--
<?php
function wsdl_with_schema(string $schema): string {
return <<<XML
<?xml version="1.0"?>
<definitions
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://test-uri/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://test-uri/">
<types>
<xsd:schema targetNamespace="http://test-uri/">
$schema
</xsd:schema>
</types>
<message name="m"><part name="p" type="tns:T"/></message>
<portType name="p"><operation name="op"><input message="tns:m"/></operation></portType>
<binding name="b" type="tns:p">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="op">
<soap:operation soapAction="#op"/>
<input>
<soap:body use="encoded"
namespace="http://test-uri/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
</operation>
</binding>
<service name="s"><port name="p" binding="tns:b"><soap:address location="test://"/></port></service>
</definitions>
XML;
}

function occurrence_schema(string $attribute, string $value = "2147483648"): string {
return <<<XML
<xsd:complexType name="T">
<xsd:sequence>
<xsd:element name="x" type="xsd:string" $attribute="$value"/>
</xsd:sequence>
</xsd:complexType>
XML;
}

function restriction_schema(string $facet, string $value = "2147483648"): string {
return <<<XML
<xsd:simpleType name="T">
<xsd:restriction base="xsd:int">
<xsd:$facet value="$value"/>
</xsd:restriction>
</xsd:simpleType>
XML;
}

$cases = [
"minOccurs" => occurrence_schema("minOccurs"),
"maxOccurs" => occurrence_schema("maxOccurs"),
"negative minOccurs" => occurrence_schema("minOccurs", "-1"),
"negative maxOccurs" => occurrence_schema("maxOccurs", "-1"),
"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",
"negative out-of-range numeric-string" => "-2147483649",
"decimal numeric-string" => "2147483648.0",
"exponent numeric-string" => "2147483648e0",
Comment thread
LamentXU123 marked this conversation as resolved.
];

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
negative minOccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range
negative 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
negative out-of-range numeric-string: 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
Loading