Skip to content

Commit 53218b1

Browse files
committed
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.
1 parent e715224 commit 53218b1

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ SimpleXML:
3333

3434
SOAP:
3535
. Add support for clark notation for namespaces in class map. (lxShaDoWxl)
36+
. Mitigate #51561 (SoapServer with a extented class and using sessions,
37+
lost the setPersistence()). (nielsdos)
3638

3739
Standard:
3840
. Implement GH-12188 (Indication for the int size in phpinfo()). (timwolla)

ext/soap/soap.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "soap_arginfo.h"
2727
#include "zend_exceptions.h"
2828
#include "zend_interfaces.h"
29+
#include "ext/standard/php_incomplete_class.h"
2930

3031

3132
static int le_sdl = 0;
@@ -1330,9 +1331,13 @@ PHP_METHOD(SoapServer, handle)
13301331
ZVAL_DEREF(session_vars);
13311332
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
13321333
(tmp_soap_p = zend_hash_str_find(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1)) != NULL &&
1333-
Z_TYPE_P(tmp_soap_p) == IS_OBJECT &&
1334-
Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce) {
1335-
soap_obj = tmp_soap_p;
1334+
Z_TYPE_P(tmp_soap_p) == IS_OBJECT) {
1335+
if (EXPECTED(Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce)) {
1336+
soap_obj = tmp_soap_p;
1337+
} else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) {
1338+
/* See #51561, communicate limitation to user */
1339+
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);
1340+
}
13361341
}
13371342
}
13381343
#endif

ext/soap/tests/bugs/bug51561.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
class Server2 {}

ext/soap/tests/bugs/bug51561.phpt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
Bug #51561 (SoapServer with a extended class and using sessions, lost the setPersistence())
3+
--EXTENSIONS--
4+
soap
5+
--SKIPIF--
6+
<?php
7+
if (!file_exists(__DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc")) {
8+
echo "skip sapi/cli/tests/php_cli_server.inc required but not found";
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include __DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc";
15+
16+
$args = ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
17+
if (php_ini_loaded_file()) {
18+
// Necessary such that it works from a development directory in which case extension_dir might not be the real extension dir
19+
$args[] = "-c";
20+
$args[] = php_ini_loaded_file();
21+
}
22+
$code = "session_start();" .
23+
"require_once '" . __DIR__ . "/bug51561.inc';" .
24+
<<<'PHP'
25+
class Server extends Server2 {
26+
private $value;
27+
public function setValue($param) { $this->value = $param; }
28+
public function getValue() { return $this->value; }
29+
}
30+
$server = new SoapServer(null, array('uri' => "blablabla.com",'encoding' => "ISO-8859-1",'soap_version' => SOAP_1_2));
31+
$server->setClass("Server");
32+
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
33+
$server->handle();
34+
PHP;
35+
36+
php_cli_server_start($code, null, $args);
37+
38+
$cli = new SoapClient(null, array('location' => "http://".PHP_CLI_SERVER_ADDRESS, 'uri' => "blablabla.com",'encoding' => "ISO-8859-1",'soap_version' => SOAP_1_2));
39+
$cli->setValue(100);
40+
$response = $cli->getValue();
41+
echo "Get = ".$response;
42+
43+
?>
44+
--EXPECTF--
45+
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
46+
Stack trace:
47+
#0 %s(%d): SoapClient->__call('getValue', Array)
48+
#1 {main}
49+
thrown in %s on line %d

0 commit comments

Comments
 (0)