Skip to content

Commit c7797fc

Browse files
authored
Fix bug phpGH-11941: soap with session persistence will silently fails when "seession" built as a shared object (php#14362)
This adds an optional dependency on the session extension and adds the necessary APIs to make the functionality work with lazy binding. This can be tested by configuring PHP with `--enable-session=shared` and `--enable-soap=shared` and running the test suite, in particular the buggy behaviour can be observed by the existing test `server009.phpt`.
1 parent 5db0595 commit c7797fc

File tree

9 files changed

+78
-38
lines changed

9 files changed

+78
-38
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ PHP NEWS
246246
. Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
247247
wsdl operation !has output). (nielsdos)
248248
. Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
249+
. Fixed bug GH-11941 (soap with session persistence will silently fail when
250+
"session" built as a shared object). (nielsdos)
249251

250252
- Sockets:
251253
. Removed the deprecated inet_ntoa call support. (David Carlier)

UPGRADING

+5
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ PHP 8.4 UPGRADE NOTES
145145
. SoapClient::$typemap is now an array rather than a resource.
146146
Checks using is_resource() (i.e. is_resource($client->typemap)) should be
147147
replaced with checks for null (i.e. $client->typemap !== null).
148+
. The SOAP extension gained an optional dependency on the session extension.
149+
If you build PHP without the session extension and with --enable-rtld-now,
150+
you will experience errors on startup if you also use the SOAP extension.
151+
To solve this, either don't use rtld-now or load the session extension.
148152

149153
- SPL:
150154
. Out of bounds accesses in SplFixedArray now throw an exception of type
@@ -301,6 +305,7 @@ PHP 8.4 UPGRADE NOTES
301305
. Instances of DateTimeInterface that are passed to xsd:datetime or similar
302306
elements are now serialized as such instead of being serialized as an
303307
empty string.
308+
. Session persistence now works with a shared session module.
304309

305310
- XSL:
306311
. It is now possible to use parameters that contain both single and double

UPGRADING.INTERNALS

+6
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ PHP 8.4 INTERNALS UPGRADE NOTES
253253
Moreover, providing it with a binary safe string is the responsibility of
254254
the caller now.
255255

256+
h. ext/session
257+
- Added the php_get_session_status() API to get the session status, which is
258+
equivalent to reading PS(session_status) but works with shared objects too.
259+
- Added the php_get_session_var_str() API to set a session variable without
260+
needing to create a zend_string.
261+
256262
========================
257263
4. OpCode changes
258264
========================

ext/session/php_session.h

+2
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ PHPAPI zend_result php_session_destroy(void);
256256
PHPAPI void php_add_session_var(zend_string *name);
257257
PHPAPI zval *php_set_session_var(zend_string *name, zval *state_val, php_unserialize_data_t *var_hash);
258258
PHPAPI zval *php_get_session_var(zend_string *name);
259+
PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len);
259260

260261
PHPAPI zend_result php_session_register_module(const ps_module *);
261262

@@ -265,6 +266,7 @@ PHPAPI zend_result php_session_register_serializer(const char *name,
265266

266267
PHPAPI zend_result php_session_start(void);
267268
PHPAPI zend_result php_session_flush(int write);
269+
PHPAPI php_session_status php_get_session_status(void);
268270

269271
PHPAPI const ps_module *_php_find_ps_module(const char *name);
270272
PHPAPI const ps_serializer *_php_find_ps_serializer(const char *name);

ext/session/session.c

+13
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ PHPAPI zval* php_get_session_var(zend_string *name) /* {{{ */
225225
}
226226
/* }}} */
227227

228+
PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len)
229+
{
230+
IF_SESSION_VARS() {
231+
return zend_hash_str_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), name, name_len);
232+
}
233+
return NULL;
234+
}
235+
228236
static void php_session_track_init(void) /* {{{ */
229237
{
230238
zval session_vars;
@@ -1632,6 +1640,11 @@ PHPAPI zend_result php_session_flush(int write) /* {{{ */
16321640
}
16331641
/* }}} */
16341642

1643+
PHPAPI php_session_status php_get_session_status(void)
1644+
{
1645+
return PS(session_status);
1646+
}
1647+
16351648
static zend_result php_session_abort(void) /* {{{ */
16361649
{
16371650
if (PS(session_status) == php_session_active) {

ext/soap/config.m4

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ if test "$PHP_SOAP" != "no"; then
1010
PHP_SUBST(SOAP_SHARED_LIBADD)
1111
])
1212
PHP_ADD_EXTENSION_DEP(soap, libxml)
13+
PHP_ADD_EXTENSION_DEP(soap, session, true)
1314
fi

ext/soap/config.w32

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ if (PHP_SOAP != "no") {
1010
) {
1111
EXTENSION('soap', 'soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_xml.c', null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
1212
AC_DEFINE('HAVE_SOAP', 1, "SOAP support");
13+
ADD_EXTENSION_DEP('soap', 'session', true);
1314

1415
if (!PHP_SOAP_SHARED) {
1516
ADD_FLAG('CFLAGS_SOAP', "/D LIBXML_STATIC ");

ext/soap/soap.c

+31-38
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,19 @@
2020
#include "config.h"
2121
#endif
2222
#include "php_soap.h"
23-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
2423
#include "ext/session/php_session.h"
25-
#endif
2624
#include "soap_arginfo.h"
2725
#include "zend_exceptions.h"
2826
#include "zend_interfaces.h"
2927
#include "ext/standard/php_incomplete_class.h"
3028

29+
/* We only have session support if PHP was configured with session support
30+
* or if the session module could be loaded dynamically, which will only
31+
* work if soap is loaded dynamically as well. */
32+
#if defined(HAVE_PHP_SESSION) || defined(COMPILE_DL_SOAP)
33+
# define SOAP_HAS_SESSION_SUPPORT
34+
#endif
35+
3136
typedef struct _soapHeader {
3237
sdlFunctionPtr function;
3338
zval function_name;
@@ -298,6 +303,7 @@ PHP_MINFO_FUNCTION(soap);
298303
static const zend_module_dep soap_deps[] = {
299304
ZEND_MOD_REQUIRED("date")
300305
ZEND_MOD_REQUIRED("libxml")
306+
ZEND_MOD_OPTIONAL("session")
301307
ZEND_MOD_END
302308
};
303309

@@ -995,6 +1001,12 @@ PHP_METHOD(SoapServer, setPersistence)
9951001
if (service->type == SOAP_CLASS) {
9961002
if (value == SOAP_PERSISTENCE_SESSION ||
9971003
value == SOAP_PERSISTENCE_REQUEST) {
1004+
if (value == SOAP_PERSISTENCE_SESSION && !zend_hash_str_exists(&module_registry, "session", sizeof("session")-1)) {
1005+
SOAP_SERVER_END_CODE();
1006+
zend_throw_error(NULL, "SoapServer::setPersistence(): Session persistence cannot be enabled because the session module is not enabled");
1007+
RETURN_THROWS();
1008+
}
1009+
9981010
service->soap_class.persistence = value;
9991011
} else {
10001012
zend_argument_value_error(
@@ -1406,22 +1418,18 @@ PHP_METHOD(SoapServer, handle)
14061418
soap_obj = &service->soap_object;
14071419
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
14081420
} else if (service->type == SOAP_CLASS) {
1409-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
1410-
/* If persistent then set soap_obj from from the previous created session (if available) */
1421+
/* If persistent then set soap_obj from the previous created session (if available) */
1422+
#ifdef SOAP_HAS_SESSION_SUPPORT
14111423
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
1412-
zval *session_vars, *tmp_soap_p;
1413-
1414-
if (PS(session_status) != php_session_active &&
1415-
PS(session_status) != php_session_disabled) {
1424+
php_session_status session_status = php_get_session_status();
1425+
if (session_status != php_session_active &&
1426+
session_status != php_session_disabled) {
14161427
php_session_start();
14171428
}
14181429

14191430
/* Find the soap object and assign */
1420-
session_vars = &PS(http_session_vars);
1421-
ZVAL_DEREF(session_vars);
1422-
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
1423-
(tmp_soap_p = zend_hash_str_find(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1)) != NULL &&
1424-
Z_TYPE_P(tmp_soap_p) == IS_OBJECT) {
1431+
zval *tmp_soap_p = php_get_session_var_str("_bogus_session_name", sizeof("_bogus_session_name")-1);
1432+
if (tmp_soap_p != NULL && Z_TYPE_P(tmp_soap_p) == IS_OBJECT) {
14251433
if (EXPECTED(Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce)) {
14261434
soap_obj = tmp_soap_p;
14271435
} else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) {
@@ -1431,9 +1439,9 @@ PHP_METHOD(SoapServer, handle)
14311439
}
14321440
}
14331441
#endif
1442+
14341443
/* If new session or something weird happned */
14351444
if (soap_obj == NULL) {
1436-
14371445
object_init_ex(&tmp_soap, service->soap_class.ce);
14381446

14391447
/* Call constructor */
@@ -1448,25 +1456,23 @@ PHP_METHOD(SoapServer, handle)
14481456
goto fail;
14491457
}
14501458
}
1451-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
1459+
14521460
/* If session then update session hash with new object */
1461+
#ifdef SOAP_HAS_SESSION_SUPPORT
14531462
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
1454-
zval *session_vars = &PS(http_session_vars), *tmp_soap_p;
1455-
1456-
ZVAL_DEREF(session_vars);
1457-
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
1458-
(tmp_soap_p = zend_hash_str_update(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1, &tmp_soap)) != NULL) {
1463+
zend_string *session_var_name = ZSTR_INIT_LITERAL("_bogus_session_name", false);
1464+
zval *tmp_soap_p = php_set_session_var(session_var_name, &tmp_soap, NULL);
1465+
if (tmp_soap_p != NULL) {
14591466
soap_obj = tmp_soap_p;
14601467
} else {
14611468
soap_obj = &tmp_soap;
14621469
}
1463-
} else {
1470+
zend_string_release_ex(session_var_name, false);
1471+
} else
1472+
#endif
1473+
{
14641474
soap_obj = &tmp_soap;
14651475
}
1466-
#else
1467-
soap_obj = &tmp_soap;
1468-
#endif
1469-
14701476
}
14711477
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
14721478
} else {
@@ -1534,15 +1540,10 @@ PHP_METHOD(SoapServer, handle)
15341540
if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) {
15351541
call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params);
15361542
if (service->type == SOAP_CLASS) {
1537-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
15381543
if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
15391544
zval_ptr_dtor(soap_obj);
15401545
soap_obj = NULL;
15411546
}
1542-
#else
1543-
zval_ptr_dtor(soap_obj);
1544-
soap_obj = NULL;
1545-
#endif
15461547
}
15471548
} else {
15481549
call_status = call_user_function(EG(function_table), NULL, &function_name, &retval, num_params, params);
@@ -1557,11 +1558,7 @@ PHP_METHOD(SoapServer, handle)
15571558
php_output_discard();
15581559
_soap_server_exception(service, function, ZEND_THIS);
15591560
if (service->type == SOAP_CLASS) {
1560-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
15611561
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
1562-
#else
1563-
if (soap_obj) {
1564-
#endif
15651562
zval_ptr_dtor(soap_obj);
15661563
}
15671564
}
@@ -1597,11 +1594,7 @@ PHP_METHOD(SoapServer, handle)
15971594
php_output_discard();
15981595
_soap_server_exception(service, function, ZEND_THIS);
15991596
if (service->type == SOAP_CLASS) {
1600-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
16011597
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
1602-
#else
1603-
if (soap_obj) {
1604-
#endif
16051598
zval_ptr_dtor(soap_obj);
16061599
}
16071600
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
GH-11941 (soap with session persistence will silently fail when "session" built as a shared object)
3+
--EXTENSIONS--
4+
soap
5+
--SKIPIF--
6+
<?php
7+
// We explicitly want to test with the soap extension enabled and session extension disabled
8+
if (extension_loaded("session")) die("skip this test must run with the session extension disabled");
9+
?>
10+
--FILE--
11+
<?php
12+
$server = new SoapServer(null, array('uri'=>"http://testuri.org"));
13+
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
14+
?>
15+
--EXPECTF--
16+
%aUncaught Error: SoapServer::setPersistence(): Persistence cannot be set when the SOAP server is used in function mode in %s:%d
17+
%a

0 commit comments

Comments
 (0)