From 8c90abd30858aeec2fad68861a5ccdaf20911d68 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 1/2] 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. --- ext/soap/soap.c | 11 +++++-- ext/soap/tests/bugs/bug51561.phpt | 49 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 ext/soap/tests/bugs/bug51561.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 17ed373b4398b..d28c77ca9bb68 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.phpt b/ext/soap/tests/bugs/bug51561.phpt new file mode 100644 index 0000000000000..211d9112838c4 --- /dev/null +++ b/ext/soap/tests/bugs/bug51561.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #51561 (SoapServer with a extended class and using sessions, lost the setPersistence()) +--EXTENSIONS-- +soap +--SKIPIF-- + +--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 From a55bff17b1466c53c0ea86ad27d1bfff9ea9c9ea Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 27 Oct 2023 21:30:52 +0200 Subject: [PATCH 2/2] Forgot to add file --- ext/soap/tests/bugs/bug51561.inc | 2 ++ ext/soap/tests/bugs/bug51561.phpt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/bugs/bug51561.inc diff --git a/ext/soap/tests/bugs/bug51561.inc b/ext/soap/tests/bugs/bug51561.inc new file mode 100644 index 0000000000000..27778d07c5e6c --- /dev/null +++ b/ext/soap/tests/bugs/bug51561.inc @@ -0,0 +1,2 @@ +