diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c
index 17ece4fe770f8..423714545ae30 100644
--- a/ext/soap/php_schema.c
+++ b/ext/soap/php_schema.c
@@ -92,6 +92,13 @@ static encodePtr get_create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlCh
return enc;
}
+/* Necessary for some error paths to avoid leaking persistent memory. */
+static void requestify_string(xmlChar **str) {
+ xmlChar *copy = (xmlChar *) estrdup((const char *) *str);
+ xmlFree(*str);
+ *str = copy;
+}
+
static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlAttrPtr tns, int import) {
if (location != NULL &&
!zend_hash_str_exists(&ctx->docs, (char*)location, xmlStrlen(location))) {
@@ -104,22 +111,35 @@ static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlA
sdl_restore_uri_credentials(ctx);
if (doc == NULL) {
+ requestify_string(&location);
soap_error1(E_ERROR, "Parsing Schema: can't import schema from '%s'", location);
}
schema = get_node(doc->children, "schema");
if (schema == NULL) {
+ requestify_string(&location);
xmlFreeDoc(doc);
soap_error1(E_ERROR, "Parsing Schema: can't import schema from '%s'", location);
}
new_tns = get_attribute(schema->properties, "targetNamespace");
if (import) {
if (ns != NULL && (new_tns == NULL || xmlStrcmp(ns->children->content, new_tns->children->content) != 0)) {
- xmlFreeDoc(doc);
- soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s'", location, ns->children->content);
+ requestify_string(&location);
+ if (new_tns == NULL) {
+ xmlFreeDoc(doc);
+ soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', missing 'targetNamespace', expected '%s'", location, ns->children->content);
+ } else {
+ /* Have to make a copy to avoid a UAF after freeing `doc` */
+ const char *target_ns_copy = estrdup((const char *) new_tns->children->content);
+ xmlFreeDoc(doc);
+ soap_error3(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected '%s'", location, target_ns_copy, ns->children->content);
+ }
}
if (ns == NULL && new_tns != NULL) {
+ requestify_string(&location);
+ /* Have to make a copy to avoid a UAF after freeing `doc` */
+ const char *target_ns_copy = estrdup((const char *) new_tns->children->content);
xmlFreeDoc(doc);
- soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s'", location, new_tns->children->content);
+ soap_error2(E_ERROR, "Parsing Schema: can't import schema from '%s', unexpected 'targetNamespace'='%s', expected no 'targetNamespace'", location, target_ns_copy);
}
} else {
new_tns = get_attribute(schema->properties, "targetNamespace");
@@ -128,6 +148,7 @@ static void schema_load_file(sdlCtx *ctx, xmlAttrPtr ns, xmlChar *location, xmlA
xmlSetProp(schema, BAD_CAST("targetNamespace"), tns->children->content);
}
} else if (tns != NULL && xmlStrcmp(tns->children->content, new_tns->children->content) != 0) {
+ requestify_string(&location);
xmlFreeDoc(doc);
soap_error1(E_ERROR, "Parsing Schema: can't include schema from '%s', different 'targetNamespace'", location);
}
diff --git a/ext/soap/tests/bugs/bug62900.phpt b/ext/soap/tests/bugs/bug62900.phpt
new file mode 100644
index 0000000000000..c78afda5304af
--- /dev/null
+++ b/ext/soap/tests/bugs/bug62900.phpt
@@ -0,0 +1,95 @@
+--TEST--
+Bug #62900 (Wrong namespace on xsd import error message)
+--EXTENSIONS--
+soap
+--INI--
+soap.wsdl_cache_enabled=0
+--FILE--
+
+
+
+
+
+
+
+XML;
+
+$wsdl_without_ns = <<
+
+
+
+
+
+
+XML;
+
+$xsd_with_wrong_ns = <<
+
+XML;
+
+$xsd_without_ns = <<
+
+XML;
+
+$combinations = [
+ [$wsdl_with_ns, $xsd_with_wrong_ns],
+ [$wsdl_with_ns, $xsd_without_ns],
+ [$wsdl_without_ns, $xsd_with_wrong_ns],
+ [$wsdl_without_ns, $xsd_without_ns],
+];
+
+chdir(__DIR__);
+
+$args = ["-d", "display_startup_errors=0", "-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
+if (php_ini_loaded_file()) {
+ // Necessary such that it works from a development directory in which case extension_dir might not be the real extension dir
+ $args[] = "-c";
+ $args[] = php_ini_loaded_file();
+}
+
+foreach ($combinations as list($wsdl, $xsd)) {
+ file_put_contents(__DIR__."/bug62900.wsdl", $wsdl);
+ file_put_contents(__DIR__."/bug62900.xsd", $xsd);
+
+ $proc = proc_open([PHP_BINARY, ...$args, __DIR__.'/bug62900_run'], [1 => ["pipe", "w"], 2 => ["pipe", "w"]], $pipes);
+ echo stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ proc_close($proc);
+}
+
+?>
+--CLEAN--
+
+--EXPECTF--
+Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing Schema: can't import schema from '%sbug62900.xsd', unexpected 'targetNamespace'='http://www.w3.org/XML/1998/namespacex', expected 'http://www.w3.org/XML/1998/namespace' in %s:%d
+Stack trace:
+#0 %s(%d): SoapClient->__construct(%s)
+#1 {main}
+ thrown in %s on line %d
+
+Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing Schema: can't import schema from '%sbug62900.xsd', missing 'targetNamespace', expected 'http://www.w3.org/XML/1998/namespace' in %s:%d
+Stack trace:
+#0 %s(%d): SoapClient->__construct(%s)
+#1 {main}
+ thrown in %s on line %d
+
+Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing Schema: can't import schema from '%sbug62900.xsd', unexpected 'targetNamespace'='http://www.w3.org/XML/1998/namespacex', expected no 'targetNamespace' in %s:%d
+Stack trace:
+#0 %s(%d): SoapClient->__construct(%s)
+#1 {main}
+ thrown in %s on line %d
+
+Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't bind to service in %s:%d
+Stack trace:
+#0 %s(%d): SoapClient->__construct(%s)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/soap/tests/bugs/bug62900_run b/ext/soap/tests/bugs/bug62900_run
new file mode 100644
index 0000000000000..0b7f7a0f33a98
--- /dev/null
+++ b/ext/soap/tests/bugs/bug62900_run
@@ -0,0 +1,2 @@
+