diff --git a/NEWS b/NEWS
index 1cc265f02e8d..f747e7723264 100644
--- a/NEWS
+++ b/NEWS
@@ -97,6 +97,8 @@ PHP NEWS
fails to initialize). (Lazizbek Ergashev)
. Fixed WSDL cache corruption when a soap:header defines headerfaults.
(Ilia Alshanetsky)
+ . Fixed stack overflow when parsing a WSDL with self-referential schema
+ groups or attributeGroups. (Ilia Alshanetsky)
- Standard:
. Fixed a segfault when a stream filter callback unsets StreamBucket::$data
diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c
index a4911c659842..90d175a02535 100644
--- a/ext/soap/php_schema.c
+++ b/ext/soap/php_schema.c
@@ -2161,6 +2161,10 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT
if (ctx->attributeGroups != NULL) {
tmp = (sdlTypePtr)schema_find_by_ref(ctx->attributeGroups, attr->ref);
if (tmp) {
+ if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)tmp) != NULL) {
+ soap_error1(E_ERROR, "Parsing Schema: recursive attributeGroup 'ref' attribute '%s'", attr->ref);
+ }
+ zend_hash_index_add_ptr(&ctx->fixupInProgress, (zend_ulong)tmp, tmp);
if (tmp->attributes) {
zend_hash_internal_pointer_reset(tmp->attributes);
while ((tmp_attr = zend_hash_get_current_data_ptr(tmp->attributes)) != NULL) {
@@ -2196,6 +2200,7 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT
}
}
}
+ zend_hash_index_del(&ctx->fixupInProgress, (zend_ulong)tmp);
}
}
efree(attr->ref);
@@ -2210,6 +2215,9 @@ static void schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model)
sdlTypePtr tmp;
if (ctx->sdl->groups && (tmp = zend_hash_str_find_ptr(ctx->sdl->groups, model->u.group_ref, strlen(model->u.group_ref))) != NULL) {
+ if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)tmp) != NULL) {
+ soap_error1(E_ERROR, "Parsing Schema: recursive group 'ref' attribute '%s'", model->u.group_ref);
+ }
schema_type_fixup(ctx, tmp);
efree(model->u.group_ref);
model->kind = XSD_CONTENT_GROUP;
@@ -2253,6 +2261,8 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type)
sdlTypePtr tmp;
sdlAttributePtr attr;
+ zend_hash_index_add_ptr(&ctx->fixupInProgress, (zend_ulong)type, type);
+
if (type->ref != NULL) {
if (ctx->sdl->elements != NULL) {
tmp = (sdlTypePtr)schema_find_by_ref(ctx->sdl->elements, type->ref);
@@ -2305,6 +2315,7 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type)
}
}
}
+ zend_hash_index_del(&ctx->fixupInProgress, (zend_ulong)type);
}
void schema_pass2(sdlCtx *ctx)
@@ -2313,6 +2324,8 @@ void schema_pass2(sdlCtx *ctx)
sdlAttributePtr attr;
sdlTypePtr type;
+ zend_hash_init(&ctx->fixupInProgress, 0, NULL, NULL, 0);
+
if (ctx->attributes) {
ZEND_HASH_FOREACH_PTR(ctx->attributes, attr) {
schema_attribute_fixup(ctx, attr);
@@ -2346,6 +2359,8 @@ void schema_pass2(sdlCtx *ctx)
zend_hash_destroy(ctx->attributeGroups);
efree(ctx->attributeGroups);
}
+
+ zend_hash_destroy(&ctx->fixupInProgress);
}
void delete_model(zval *zv)
diff --git a/ext/soap/php_sdl.h b/ext/soap/php_sdl.h
index 3df4fbdca015..843b13141cc0 100644
--- a/ext/soap/php_sdl.h
+++ b/ext/soap/php_sdl.h
@@ -73,6 +73,7 @@ typedef struct sdlCtx {
HashTable *attributes; /* array of sdlAttributePtr */
HashTable *attributeGroups; /* array of sdlTypesPtr */
+ HashTable fixupInProgress;
php_stream_context *context;
zval old_header;
} sdlCtx;
diff --git a/ext/soap/tests/schema-selfref-attrgroup.phpt b/ext/soap/tests/schema-selfref-attrgroup.phpt
new file mode 100644
index 000000000000..668826ea6c39
--- /dev/null
+++ b/ext/soap/tests/schema-selfref-attrgroup.phpt
@@ -0,0 +1,44 @@
+--TEST--
+SOAP XML Schema: self-referential attributeGroup fix-up recursion is rejected
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+';
+$file = __DIR__ . '/schema-selfref-attrgroup.wsdl';
+file_put_contents($file, $wsdl);
+try {
+ $c = new SoapClient($file, ['exceptions' => true, 'cache_wsdl' => WSDL_CACHE_NONE]);
+ echo "parsed ok\n";
+} catch (Throwable $e) {
+ echo $e::class, ": ", substr($e->getMessage(), 0, 120), "\n";
+}
+echo "done\n";
+?>
+--CLEAN--
+
+--EXPECTF--
+SoapFault: SOAP-ERROR: Parsing Schema: recursive attributeGroup 'ref' attribute '%s'
+done
diff --git a/ext/soap/tests/schema-selfref-group.phpt b/ext/soap/tests/schema-selfref-group.phpt
new file mode 100644
index 000000000000..be9d7df524f9
--- /dev/null
+++ b/ext/soap/tests/schema-selfref-group.phpt
@@ -0,0 +1,46 @@
+--TEST--
+SOAP XML Schema: self-referential group fix-up recursion is rejected
+--EXTENSIONS--
+soap
+--FILE--
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+';
+$file = __DIR__ . '/schema-selfref-group.wsdl';
+file_put_contents($file, $wsdl);
+try {
+ $c = new SoapClient($file, ['exceptions' => true, 'cache_wsdl' => WSDL_CACHE_NONE]);
+ echo "parsed ok\n";
+} catch (Throwable $e) {
+ echo $e::class, ": ", substr($e->getMessage(), 0, 120), "\n";
+}
+echo "done\n";
+?>
+--CLEAN--
+
+--EXPECTF--
+SoapFault: SOAP-ERROR: Parsing Schema: recursive group 'ref' attribute '%s'
+done