Skip to content

Fix bug GH-11941: soap with session persistence will silently fails when "seession" built as a shared object #14362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ PHP NEWS
. Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
wsdl operation !has output). (nielsdos)
. Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
. Fixed bug GH-11941 (soap with session persistence will silently fail when
"session" built as a shared object). (nielsdos)

- Sockets:
. Removed the deprecated inet_ntoa call support. (David Carlier)
Expand Down
5 changes: 5 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ PHP 8.4 UPGRADE NOTES
. SoapClient::$typemap is now an array rather than a resource.
Checks using is_resource() (i.e. is_resource($client->typemap)) should be
replaced with checks for null (i.e. $client->typemap !== null).
. The SOAP extension gained an optional dependency on the session extension.
If you build PHP without the session extension and with --enable-rtld-now,
you will experience errors on startup if you also use the SOAP extension.
To solve this, either don't use rtld-now or load the session extension.

- SPL:
. Out of bounds accesses in SplFixedArray now throw an exception of type
Expand Down Expand Up @@ -300,6 +304,7 @@ PHP 8.4 UPGRADE NOTES
. Instances of DateTimeInterface that are passed to xsd:datetime or similar
elements are now serialized as such instead of being serialized as an
empty string.
. Session persistence now works with a shared session module.

- XSL:
. It is now possible to use parameters that contain both single and double
Expand Down
6 changes: 6 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ PHP 8.4 INTERNALS UPGRADE NOTES
Moreover, providing it with a binary safe string is the responsibility of
the caller now.

h. ext/session
- Added the php_get_session_status() API to get the session status, which is
equivalent to reading PS(session_status) but works with shared objects too.
- Added the php_get_session_var_str() API to set a session variable without
needing to create a zend_string.

========================
4. OpCode changes
========================
Expand Down
2 changes: 2 additions & 0 deletions ext/session/php_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ PHPAPI zend_result php_session_destroy(void);
PHPAPI void php_add_session_var(zend_string *name);
PHPAPI zval *php_set_session_var(zend_string *name, zval *state_val, php_unserialize_data_t *var_hash);
PHPAPI zval *php_get_session_var(zend_string *name);
PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len);

PHPAPI zend_result php_session_register_module(const ps_module *);

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

PHPAPI zend_result php_session_start(void);
PHPAPI zend_result php_session_flush(int write);
PHPAPI php_session_status php_get_session_status(void);

PHPAPI const ps_module *_php_find_ps_module(const char *name);
PHPAPI const ps_serializer *_php_find_ps_serializer(const char *name);
Expand Down
13 changes: 13 additions & 0 deletions ext/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ PHPAPI zval* php_get_session_var(zend_string *name) /* {{{ */
}
/* }}} */

PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len)
{
IF_SESSION_VARS() {
return zend_hash_str_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), name, name_len);
}
return NULL;
}

static void php_session_track_init(void) /* {{{ */
{
zval session_vars;
Expand Down Expand Up @@ -1632,6 +1640,11 @@ PHPAPI zend_result php_session_flush(int write) /* {{{ */
}
/* }}} */

PHPAPI php_session_status php_get_session_status(void)
{
return PS(session_status);
}

static zend_result php_session_abort(void) /* {{{ */
{
if (PS(session_status) == php_session_active) {
Expand Down
1 change: 1 addition & 0 deletions ext/soap/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ if test "$PHP_SOAP" != "no"; then
PHP_SUBST(SOAP_SHARED_LIBADD)
])
PHP_ADD_EXTENSION_DEP(soap, libxml)
PHP_ADD_EXTENSION_DEP(soap, session, true)
fi
1 change: 1 addition & 0 deletions ext/soap/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ if (PHP_SOAP != "no") {
) {
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");
AC_DEFINE('HAVE_SOAP', 1, "SOAP support");
ADD_EXTENSION_DEP('soap', 'session', true);

if (!PHP_SOAP_SHARED) {
ADD_FLAG('CFLAGS_SOAP', "/D LIBXML_STATIC ");
Expand Down
69 changes: 31 additions & 38 deletions ext/soap/soap.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@
#include "config.h"
#endif
#include "php_soap.h"
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
#include "ext/session/php_session.h"
#endif
#include "soap_arginfo.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "ext/standard/php_incomplete_class.h"

/* We only have session support if PHP was configured with session support
* or if the session module could be loaded dynamically, which will only
* work if soap is loaded dynamically as well. */
#if defined(HAVE_PHP_SESSION) || defined(COMPILE_DL_SOAP)
# define SOAP_HAS_SESSION_SUPPORT
#endif

typedef struct _soapHeader {
sdlFunctionPtr function;
zval function_name;
Expand Down Expand Up @@ -298,6 +303,7 @@ PHP_MINFO_FUNCTION(soap);
static const zend_module_dep soap_deps[] = {
ZEND_MOD_REQUIRED("date")
ZEND_MOD_REQUIRED("libxml")
ZEND_MOD_OPTIONAL("session")
ZEND_MOD_END
};

Expand Down Expand Up @@ -995,6 +1001,12 @@ PHP_METHOD(SoapServer, setPersistence)
if (service->type == SOAP_CLASS) {
if (value == SOAP_PERSISTENCE_SESSION ||
value == SOAP_PERSISTENCE_REQUEST) {
if (value == SOAP_PERSISTENCE_SESSION && !zend_hash_str_exists(&module_registry, "session", sizeof("session")-1)) {
SOAP_SERVER_END_CODE();
zend_throw_error(NULL, "SoapServer::setPersistence(): Session persistence cannot be enabled because the session module is not enabled");
RETURN_THROWS();
}

service->soap_class.persistence = value;
} else {
zend_argument_value_error(
Expand Down Expand Up @@ -1406,22 +1418,18 @@ PHP_METHOD(SoapServer, handle)
soap_obj = &service->soap_object;
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
} else if (service->type == SOAP_CLASS) {
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
/* If persistent then set soap_obj from from the previous created session (if available) */
/* If persistent then set soap_obj from the previous created session (if available) */
#ifdef SOAP_HAS_SESSION_SUPPORT
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
zval *session_vars, *tmp_soap_p;

if (PS(session_status) != php_session_active &&
PS(session_status) != php_session_disabled) {
php_session_status session_status = php_get_session_status();
if (session_status != php_session_active &&
session_status != php_session_disabled) {
php_session_start();
}

/* Find the soap object and assign */
session_vars = &PS(http_session_vars);
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) {
zval *tmp_soap_p = php_get_session_var_str("_bogus_session_name", sizeof("_bogus_session_name")-1);
if (tmp_soap_p != NULL && 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) {
Expand All @@ -1431,9 +1439,9 @@ PHP_METHOD(SoapServer, handle)
}
}
#endif

/* If new session or something weird happned */
if (soap_obj == NULL) {

object_init_ex(&tmp_soap, service->soap_class.ce);

/* Call constructor */
Expand All @@ -1448,25 +1456,23 @@ PHP_METHOD(SoapServer, handle)
goto fail;
}
}
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)

/* If session then update session hash with new object */
#ifdef SOAP_HAS_SESSION_SUPPORT
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
zval *session_vars = &PS(http_session_vars), *tmp_soap_p;

ZVAL_DEREF(session_vars);
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
(tmp_soap_p = zend_hash_str_update(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1, &tmp_soap)) != NULL) {
zend_string *session_var_name = ZSTR_INIT_LITERAL("_bogus_session_name", false);
zval *tmp_soap_p = php_set_session_var(session_var_name, &tmp_soap, NULL);
if (tmp_soap_p != NULL) {
soap_obj = tmp_soap_p;
} else {
soap_obj = &tmp_soap;
}
} else {
zend_string_release_ex(session_var_name, false);
} else
#endif
{
soap_obj = &tmp_soap;
}
#else
soap_obj = &tmp_soap;
#endif

}
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
} else {
Expand Down Expand Up @@ -1534,15 +1540,10 @@ PHP_METHOD(SoapServer, handle)
if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) {
call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params);
if (service->type == SOAP_CLASS) {
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
zval_ptr_dtor(soap_obj);
soap_obj = NULL;
}
#else
zval_ptr_dtor(soap_obj);
soap_obj = NULL;
#endif
}
} else {
call_status = call_user_function(EG(function_table), NULL, &function_name, &retval, num_params, params);
Expand All @@ -1557,11 +1558,7 @@ PHP_METHOD(SoapServer, handle)
php_output_discard();
_soap_server_exception(service, function, ZEND_THIS);
if (service->type == SOAP_CLASS) {
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
#else
if (soap_obj) {
#endif
zval_ptr_dtor(soap_obj);
}
}
Expand Down Expand Up @@ -1597,11 +1594,7 @@ PHP_METHOD(SoapServer, handle)
php_output_discard();
_soap_server_exception(service, function, ZEND_THIS);
if (service->type == SOAP_CLASS) {
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
#else
if (soap_obj) {
#endif
zval_ptr_dtor(soap_obj);
}
}
Expand Down
17 changes: 17 additions & 0 deletions ext/soap/tests/bugs/gh11941_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
GH-11941 (soap with session persistence will silently fail when "session" built as a shared object)
--EXTENSIONS--
soap
--SKIPIF--
<?php
// We explicitly want to test with the soap extension enabled and session extension disabled
if (extension_loaded("session")) die("skip this test must run with the session extension disabled");
?>
--FILE--
<?php
$server = new SoapServer(null, array('uri'=>"http://testuri.org"));
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
?>
--EXPECTF--
%aUncaught Error: SoapServer::setPersistence(): Persistence cannot be set when the SOAP server is used in function mode in %s:%d
%a
Loading