From 53218b1a32ecf3b663089e50f7305ec8c288a7ef Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:54:14 +0200 Subject: [PATCH] Mitigate #51561: SoapServer with a extented class and using sessions, lost the setPersistence() The problem is that in the testcase, the session is started before the parent class is loaded. This causes an incomplete class in the session storage. Then in the soap code the check `Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce` fails because it is the incomplete class. It is a silent failure. We cannot fix this easily. But we should let the user know something is wrong, because it leaves them confused otherwise. So emit an error to let them know and suggest a fix. Closes GH-12540. --- NEWS | 2 ++ ext/soap/soap.c | 11 +++++-- ext/soap/tests/bugs/bug51561.inc | 2 ++ ext/soap/tests/bugs/bug51561.phpt | 49 +++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 ext/soap/tests/bugs/bug51561.inc create mode 100644 ext/soap/tests/bugs/bug51561.phpt diff --git a/NEWS b/NEWS index d8bfcec3fce..24e80ca94ac 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,8 @@ SimpleXML: SOAP: . Add support for clark notation for namespaces in class map. (lxShaDoWxl) + . Mitigate #51561 (SoapServer with a extented class and using sessions, + lost the setPersistence()). (nielsdos) Standard: . Implement GH-12188 (Indication for the int size in phpinfo()). (timwolla) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 17ed373b439..d28c77ca9bb 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -26,6 +26,7 @@ #include "soap_arginfo.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "ext/standard/php_incomplete_class.h" static int le_sdl = 0; @@ -1330,9 +1331,13 @@ PHP_METHOD(SoapServer, handle) ZVAL_DEREF(session_vars); if (Z_TYPE_P(session_vars) == IS_ARRAY && (tmp_soap_p = zend_hash_str_find(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1)) != NULL && - Z_TYPE_P(tmp_soap_p) == IS_OBJECT && - Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce) { - soap_obj = tmp_soap_p; + Z_TYPE_P(tmp_soap_p) == IS_OBJECT) { + if (EXPECTED(Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce)) { + soap_obj = tmp_soap_p; + } else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) { + /* See #51561, communicate limitation to user */ + soap_server_fault("Server", "SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue.", NULL, NULL, NULL); + } } } #endif diff --git a/ext/soap/tests/bugs/bug51561.inc b/ext/soap/tests/bugs/bug51561.inc new file mode 100644 index 00000000000..27778d07c5e --- /dev/null +++ b/ext/soap/tests/bugs/bug51561.inc @@ -0,0 +1,2 @@ + +--FILE-- +value = $param; } + public function getValue() { return $this->value; } + } + $server = new SoapServer(null, array('uri' => "blablabla.com",'encoding' => "ISO-8859-1",'soap_version' => SOAP_1_2)); + $server->setClass("Server"); + $server->setPersistence(SOAP_PERSISTENCE_SESSION); + $server->handle(); + PHP; + +php_cli_server_start($code, null, $args); + +$cli = new SoapClient(null, array('location' => "http://".PHP_CLI_SERVER_ADDRESS, 'uri' => "blablabla.com",'encoding' => "ISO-8859-1",'soap_version' => SOAP_1_2)); +$cli->setValue(100); +$response = $cli->getValue(); +echo "Get = ".$response; + +?> +--EXPECTF-- +Fatal error: Uncaught SoapFault exception: [env:Receiver] SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue. in %s:%d +Stack trace: +#0 %s(%d): SoapClient->__call('getValue', Array) +#1 {main} + thrown in %s on line %d