diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index 4eedbffc5a4b..d0911c863d89 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -380,6 +380,16 @@ static void soap_add_xml_ref(zval *data, xmlNodePtr node)
}
}
+static void soap_add_xml_ref_holder(zval *data, xmlNodePtr node)
+{
+ if (SOAP_GLOBAL(ref_map)) {
+ zval holder;
+
+ array_init(&holder);
+ zend_hash_index_update(SOAP_GLOBAL(ref_map), (zend_ulong)node, &holder);
+ }
+}
+
static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xmlNodePtr parent, int check_class_map)
{
xmlNodePtr node = NULL;
@@ -2536,6 +2546,9 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data)
ZVAL_NULL(ret);
FIND_XML_NULL(data, ret);
+ if (soap_check_xml_ref(ret, data)) {
+ return ret;
+ }
if (data &&
(attr = get_attribute(data->properties,"arrayType")) &&
@@ -2672,6 +2685,7 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data)
}
array_init(ret);
+ soap_add_xml_ref_holder(ret, data);
trav = data->children;
while (trav) {
if (trav->type == XML_ELEMENT_NODE) {
@@ -2729,6 +2743,7 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data)
}
efree(dims);
efree(pos);
+ soap_add_xml_ref(ret, data);
return ret;
}
@@ -2798,9 +2813,13 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data)
ZVAL_NULL(ret);
FIND_XML_NULL(data, ret);
+ if (soap_check_xml_ref(ret, data)) {
+ return ret;
+ }
if (data && data->children) {
array_init(ret);
+ soap_add_xml_ref_holder(ret, data);
trav = data->children;
trav = data->children;
@@ -2830,6 +2849,7 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data)
zval_ptr_dtor(&key);
}
ENDFOREACH(trav);
+ soap_add_xml_ref(ret, data);
} else {
ZVAL_NULL(ret);
}
diff --git a/ext/soap/tests/bugs/gh22984_1.phpt b/ext/soap/tests/bugs/gh22984_1.phpt
new file mode 100644
index 000000000000..4257555e086c
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22984_1.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-22984 (Native stack overflow while decoding a cyclic SOAP 1.1 href reference)
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+
+
+
+
+
+XML;
+ }
+}
+
+$client = new TestSoapClient(null, ['location' => 'test://', 'uri' => 'http://testuri.org', 'soap_version' => SOAP_1_1]);
+var_dump($client->__soapCall('probe', []));
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(0) {
+ }
+}
diff --git a/ext/soap/tests/bugs/gh22984_2.phpt b/ext/soap/tests/bugs/gh22984_2.phpt
new file mode 100644
index 000000000000..07fd190cf34a
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22984_2.phpt
@@ -0,0 +1,47 @@
+--TEST--
+GH-22984 (Native stack overflow while decoding a cyclic SOAP 1.2 enc:ref reference)
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+
+
+
+
+
+
+
+
+XML;
+ }
+}
+
+$client = new TestSoapClient(null, ['location' => 'test://', 'uri' => 'http://testuri.org', 'soap_version' => SOAP_1_2]);
+var_dump($client->__soapCall('probe', []));
+?>
+--EXPECT--
+array(2) {
+ ["ret"]=>
+ array(1) {
+ [0]=>
+ array(1) {
+ [0]=>
+ array(0) {
+ }
+ }
+ }
+ ["node"]=>
+ array(1) {
+ [0]=>
+ array(0) {
+ }
+ }
+}
diff --git a/ext/soap/tests/bugs/gh22984_3.phpt b/ext/soap/tests/bugs/gh22984_3.phpt
new file mode 100644
index 000000000000..ebe0eb3d9d38
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22984_3.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GH-22984 (Native stack overflow while decoding a cyclic reference inside an apache Map)
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+ -
+ self
+
+
+
+
+
+
+XML;
+ }
+}
+
+$client = new TestSoapClient(null, ['location' => 'test://', 'uri' => 'http://testuri.org', 'soap_version' => SOAP_1_1]);
+var_dump($client->__soapCall('probe', []));
+?>
+--EXPECT--
+array(1) {
+ ["self"]=>
+ array(0) {
+ }
+}
diff --git a/ext/soap/tests/bugs/gh22984_4.phpt b/ext/soap/tests/bugs/gh22984_4.phpt
new file mode 100644
index 000000000000..41e2d3b11182
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22984_4.phpt
@@ -0,0 +1,40 @@
+--TEST--
+GH-22984 (Native stack overflow while a SoapServer decodes a cyclic href reference)
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+
+
+
+
+
+XML;
+
+$server = new SoapServer(null, ['uri' => 'http://testuri.org']);
+$server->addFunction('probe');
+$server->handle($request);
+var_dump($decoded);
+?>
+--EXPECT--
+
+1
+array(1) {
+ [0]=>
+ array(0) {
+ }
+}