Skip to content

Commit 5b40d06

Browse files
committed
Refactor session_set_save_handler()
Use proper ZPP callables with FCI/FCC
1 parent 15c0df7 commit 5b40d06

File tree

4 files changed

+141
-103
lines changed

4 files changed

+141
-103
lines changed

ext/session/session.c

Lines changed: 133 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,18 +1924,18 @@ PHP_FUNCTION(session_module_name)
19241924
}
19251925
/* }}} */
19261926

1927-
static zend_result save_handler_check_session(void) {
1927+
static bool can_session_handler_be_changed(void) {
19281928
if (PS(session_status) == php_session_active) {
19291929
php_error_docref(NULL, E_WARNING, "Session save handler cannot be changed when a session is active");
1930-
return FAILURE;
1930+
return false;
19311931
}
19321932

19331933
if (SG(headers_sent)) {
19341934
php_error_docref(NULL, E_WARNING, "Session save handler cannot be changed after headers have already been sent");
1935-
return FAILURE;
1935+
return false;
19361936
}
19371937

1938-
return SUCCESS;
1938+
return true;
19391939
}
19401940

19411941
static inline void set_user_save_handler_ini(void) {
@@ -1950,91 +1950,107 @@ static inline void set_user_save_handler_ini(void) {
19501950
zend_string_release_ex(ini_name, 0);
19511951
}
19521952

1953+
#define SESSION_RELEASE_USER_HANDLER_OO(struct_name) \
1954+
if (!Z_ISUNDEF(PS(mod_user_names).name.struct_name)) { \
1955+
zval_ptr_dtor(&PS(mod_user_names).name.struct_name); \
1956+
ZVAL_UNDEF(&PS(mod_user_names).name.struct_name); \
1957+
}
1958+
1959+
#define SESSION_SET_USER_HANDLER_OO(struct_name, zstr_method_name) \
1960+
array_init_size(&PS(mod_user_names).name.struct_name, 2); \
1961+
Z_ADDREF_P(obj); \
1962+
add_next_index_zval(&PS(mod_user_names).name.struct_name, obj); \
1963+
add_next_index_str(&PS(mod_user_names).name.struct_name, zstr_method_name);
1964+
1965+
#define SESSION_SET_USER_HANDLER_OO_MANDATORY(struct_name, method_name) \
1966+
if (!Z_ISUNDEF(PS(mod_user_names).name.struct_name)) { \
1967+
zval_ptr_dtor(&PS(mod_user_names).name.struct_name); \
1968+
} \
1969+
array_init_size(&PS(mod_user_names).name.struct_name, 2); \
1970+
Z_ADDREF_P(obj); \
1971+
add_next_index_zval(&PS(mod_user_names).name.struct_name, obj); \
1972+
add_next_index_str(&PS(mod_user_names).name.struct_name, zend_string_init(method_name, strlen(method_name), false));
1973+
1974+
#define SESSION_SET_USER_HANDLER_PROCEDURAL(struct_name, fci) \
1975+
if (!Z_ISUNDEF(PS(mod_user_names).name.struct_name)) { \
1976+
zval_ptr_dtor(&PS(mod_user_names).name.struct_name); \
1977+
} \
1978+
ZVAL_COPY(&PS(mod_user_names).name.struct_name, &fci.function_name);
1979+
1980+
#define SESSION_SET_USER_HANDLER_PROCEDURAL_OPTIONAL(struct_name, fci) \
1981+
if (ZEND_FCI_INITIALIZED(fci)) { \
1982+
SESSION_SET_USER_HANDLER_PROCEDURAL(struct_name, fci); \
1983+
}
1984+
19531985
/* {{{ Sets user-level functions */
19541986
PHP_FUNCTION(session_set_save_handler)
19551987
{
1956-
zval *args = NULL;
1957-
int i, num_args, argc = ZEND_NUM_ARGS();
1958-
1959-
if (argc > 0 && argc <= 2) {
1988+
/* OOP Version */
1989+
if (ZEND_NUM_ARGS() <= 2) {
19601990
zval *obj = NULL;
1961-
zend_string *func_name;
1962-
zend_function *current_mptr;
19631991
bool register_shutdown = 1;
19641992

19651993
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &obj, php_session_iface_entry, &register_shutdown) == FAILURE) {
19661994
RETURN_THROWS();
19671995
}
19681996

1969-
if (save_handler_check_session() == FAILURE) {
1997+
if (!can_session_handler_be_changed()) {
19701998
RETURN_FALSE;
19711999
}
19722000

1973-
/* For compatibility reason, implemented interface is not checked */
1974-
/* Find implemented methods - SessionHandlerInterface */
1975-
i = 0;
1976-
ZEND_HASH_MAP_FOREACH_STR_KEY(&php_session_iface_entry->function_table, func_name) {
1977-
if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
1978-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
1979-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
1980-
}
2001+
if (PS(mod_user_class_name)) {
2002+
zend_string_release(PS(mod_user_class_name));
2003+
}
2004+
PS(mod_user_class_name) = zend_string_copy(Z_OBJCE_P(obj)->name);
19812005

1982-
array_init_size(&PS(mod_user_names).names[i], 2);
1983-
Z_ADDREF_P(obj);
1984-
add_next_index_zval(&PS(mod_user_names).names[i], obj);
1985-
add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
1986-
} else {
1987-
php_error_docref(NULL, E_ERROR, "Session save handler function table is corrupt");
1988-
RETURN_FALSE;
1989-
}
2006+
/* Define mandatory handlers */
2007+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_open, "open");
2008+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_close, "close");
2009+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_read, "read");
2010+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_write, "write");
2011+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_destroy, "destroy");
2012+
SESSION_SET_USER_HANDLER_OO_MANDATORY(ps_gc, "gc");
19902013

1991-
++i;
1992-
} ZEND_HASH_FOREACH_END();
2014+
/* Elements of object_methods HashTable are zend_function *method */
2015+
HashTable *object_methods = &Z_OBJCE_P(obj)->function_table;
19932016

19942017
/* Find implemented methods - SessionIdInterface (optional) */
1995-
ZEND_HASH_MAP_FOREACH_STR_KEY(&php_session_id_iface_entry->function_table, func_name) {
1996-
if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
1997-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
1998-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
1999-
}
2000-
array_init_size(&PS(mod_user_names).names[i], 2);
2001-
Z_ADDREF_P(obj);
2002-
add_next_index_zval(&PS(mod_user_names).names[i], obj);
2003-
add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
2004-
} else {
2005-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
2006-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
2007-
ZVAL_UNDEF(&PS(mod_user_names).names[i]);
2008-
}
2009-
}
2010-
2011-
++i;
2012-
} ZEND_HASH_FOREACH_END();
2018+
/* First release old handlers */
2019+
SESSION_RELEASE_USER_HANDLER_OO(ps_create_sid);
2020+
zend_string *create_sid_name = zend_string_init("create_sid", strlen("create_sid"), false);
2021+
if (instanceof_function(Z_OBJCE_P(obj), php_session_id_iface_entry)) {
2022+
SESSION_SET_USER_HANDLER_OO(ps_create_sid, zend_string_copy(create_sid_name));
2023+
} else if (zend_hash_find_ptr(object_methods, create_sid_name)) {
2024+
/* For BC reasons we accept methods even if the class does not implement the interface */
2025+
SESSION_SET_USER_HANDLER_OO(ps_create_sid, zend_string_copy(create_sid_name));
2026+
}
2027+
zend_string_release_ex(create_sid_name, false);
20132028

20142029
/* Find implemented methods - SessionUpdateTimestampInterface (optional) */
2015-
ZEND_HASH_MAP_FOREACH_STR_KEY(&php_session_update_timestamp_iface_entry->function_table, func_name) {
2016-
if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
2017-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
2018-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
2019-
}
2020-
array_init_size(&PS(mod_user_names).names[i], 2);
2021-
Z_ADDREF_P(obj);
2022-
add_next_index_zval(&PS(mod_user_names).names[i], obj);
2023-
add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
2024-
} else {
2025-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
2026-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
2027-
ZVAL_UNDEF(&PS(mod_user_names).names[i]);
2028-
}
2030+
/* First release old handlers */
2031+
SESSION_RELEASE_USER_HANDLER_OO(ps_validate_sid);
2032+
SESSION_RELEASE_USER_HANDLER_OO(ps_update_timestamp);
2033+
/* Method names need to be lowercase */
2034+
zend_string *validate_sid_name = zend_string_init("validateid", strlen("validateid"), false);
2035+
zend_string *update_timestamp_name = zend_string_init("updatetimestamp", strlen("updatetimestamp"), false);
2036+
if (instanceof_function(Z_OBJCE_P(obj), php_session_update_timestamp_iface_entry)) {
2037+
/* Validate ID handler */
2038+
SESSION_SET_USER_HANDLER_OO(ps_validate_sid, zend_string_copy(validate_sid_name));
2039+
/* Update Timestamp handler */
2040+
SESSION_SET_USER_HANDLER_OO(ps_update_timestamp, zend_string_copy(update_timestamp_name));
2041+
} else {
2042+
/* For BC reasons we accept methods even if the class does not implement the interface */
2043+
if (zend_hash_find_ptr(object_methods, validate_sid_name)) {
2044+
/* For BC reasons we accept methods even if the class does not implement the interface */
2045+
SESSION_SET_USER_HANDLER_OO(ps_validate_sid, zend_string_copy(validate_sid_name));
2046+
}
2047+
if (zend_hash_find_ptr(object_methods, update_timestamp_name)) {
2048+
/* For BC reasons we accept methods even if the class does not implement the interface */
2049+
SESSION_SET_USER_HANDLER_OO(ps_update_timestamp, zend_string_copy(update_timestamp_name));
20292050
}
2030-
++i;
2031-
} ZEND_HASH_FOREACH_END();
2032-
2033-
2034-
if (PS(mod_user_class_name)) {
2035-
zend_string_release(PS(mod_user_class_name));
20362051
}
2037-
PS(mod_user_class_name) = zend_string_copy(Z_OBJCE_P(obj)->name);
2052+
zend_string_release_ex(validate_sid_name, false);
2053+
zend_string_release_ex(update_timestamp_name, false);
20382054

20392055
if (register_shutdown) {
20402056
/* create shutdown function */
@@ -2049,14 +2065,14 @@ PHP_FUNCTION(session_set_save_handler)
20492065
ZEND_ASSERT(result == SUCCESS);
20502066

20512067
/* add shutdown function, removing the old one if it exists */
2052-
if (!register_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1, &shutdown_function_entry)) {
2068+
if (!register_user_shutdown_function("session_shutdown", strlen("session_shutdown"), &shutdown_function_entry)) {
20532069
zval_ptr_dtor(&callable);
20542070
php_error_docref(NULL, E_WARNING, "Unable to register session shutdown function");
20552071
RETURN_FALSE;
20562072
}
20572073
} else {
20582074
/* remove shutdown function */
2059-
remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
2075+
remove_user_shutdown_function("session_shutdown", strlen("session_shutdown"));
20602076
}
20612077

20622078
if (PS(session_status) != php_session_active && (!PS(mod) || PS(mod) != &ps_mod_user)) {
@@ -2066,47 +2082,69 @@ PHP_FUNCTION(session_set_save_handler)
20662082
RETURN_TRUE;
20672083
}
20682084

2069-
/* Set procedural save handler functions */
2070-
if (argc < 6 || PS_NUM_APIS < argc) {
2071-
WRONG_PARAM_COUNT;
2072-
}
2073-
2074-
if (zend_parse_parameters(argc, "+", &args, &num_args) == FAILURE) {
2085+
/* Procedural version */
2086+
zend_fcall_info open_fci = {0};
2087+
zend_fcall_info_cache open_fcc;
2088+
zend_fcall_info close_fci = {0};
2089+
zend_fcall_info_cache close_fcc;
2090+
zend_fcall_info read_fci = {0};
2091+
zend_fcall_info_cache read_fcc;
2092+
zend_fcall_info write_fci = {0};
2093+
zend_fcall_info_cache write_fcc;
2094+
zend_fcall_info destroy_fci = {0};
2095+
zend_fcall_info_cache destroy_fcc;
2096+
zend_fcall_info gc_fci = {0};
2097+
zend_fcall_info_cache gc_fcc;
2098+
zend_fcall_info create_id_fci = {0};
2099+
zend_fcall_info_cache create_id_fcc;
2100+
zend_fcall_info validate_id_fci = {0};
2101+
zend_fcall_info_cache validate_id_fcc;
2102+
zend_fcall_info update_timestamp_fci = {0};
2103+
zend_fcall_info_cache update_timestamp_fcc;
2104+
2105+
if (zend_parse_parameters(ZEND_NUM_ARGS(),
2106+
"ffffff|f!f!f!",
2107+
&open_fci, &open_fcc,
2108+
&close_fci, &close_fcc,
2109+
&read_fci, &read_fcc,
2110+
&write_fci, &write_fcc,
2111+
&destroy_fci, &destroy_fcc,
2112+
&gc_fci, &gc_fcc,
2113+
&create_id_fci, &create_id_fcc,
2114+
&validate_id_fci, &validate_id_fcc,
2115+
&update_timestamp_fci, &update_timestamp_fcc) == FAILURE
2116+
) {
20752117
RETURN_THROWS();
20762118
}
2077-
2078-
/* At this point argc can only be between 6 and PS_NUM_APIS */
2079-
for (i = 0; i < argc; i++) {
2080-
if (!zend_is_callable(&args[i], IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL)) {
2081-
zend_string *name = zend_get_callable_name(&args[i]);
2082-
zend_argument_type_error(i + 1, "must be a valid callback, function \"%s\" not found or invalid function name", ZSTR_VAL(name));
2083-
zend_string_release(name);
2084-
RETURN_THROWS();
2085-
}
2086-
}
2087-
2088-
if (save_handler_check_session() == FAILURE) {
2119+
if (!can_session_handler_be_changed()) {
20892120
RETURN_FALSE;
20902121
}
20912122

2123+
/* If a custom session handler is already set, release relevant info */
20922124
if (PS(mod_user_class_name)) {
20932125
zend_string_release(PS(mod_user_class_name));
20942126
PS(mod_user_class_name) = NULL;
20952127
}
20962128

20972129
/* remove shutdown function */
2098-
remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
2130+
remove_user_shutdown_function("session_shutdown", strlen("session_shutdown"));
20992131

21002132
if (!PS(mod) || PS(mod) != &ps_mod_user) {
21012133
set_user_save_handler_ini();
21022134
}
21032135

2104-
for (i = 0; i < argc; i++) {
2105-
if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
2106-
zval_ptr_dtor(&PS(mod_user_names).names[i]);
2107-
}
2108-
ZVAL_COPY(&PS(mod_user_names).names[i], &args[i]);
2109-
}
2136+
/* Define mandatory handlers */
2137+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_open, open_fci);
2138+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_close, close_fci);
2139+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_read, read_fci);
2140+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_write, write_fci);
2141+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_destroy, destroy_fci);
2142+
SESSION_SET_USER_HANDLER_PROCEDURAL(ps_gc, gc_fci);
2143+
2144+
/* Check for optional handlers */
2145+
SESSION_SET_USER_HANDLER_PROCEDURAL_OPTIONAL(ps_create_sid, create_id_fci);
2146+
SESSION_SET_USER_HANDLER_PROCEDURAL_OPTIONAL(ps_validate_sid, validate_id_fci);
2147+
SESSION_SET_USER_HANDLER_PROCEDURAL_OPTIONAL(ps_update_timestamp, update_timestamp_fci);
21102148

21112149
RETURN_TRUE;
21122150
}

ext/session/session.stub.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ function session_set_save_handler(
7575
callable $write = UNKNOWN,
7676
callable $destroy = UNKNOWN,
7777
callable $gc = UNKNOWN,
78-
callable $create_sid = UNKNOWN,
79-
callable $validate_sid = UNKNOWN,
80-
callable $update_timestamp = UNKNOWN
78+
?callable $create_sid = null,
79+
?callable $validate_sid = null,
80+
?callable $update_timestamp = null
8181
): bool {}
8282

8383
/** @refcount 1 */

ext/session/session_arginfo.h

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/session/tests/user_session_module/bug31454.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ try {
2121
echo "Done\n";
2222
?>
2323
--EXPECT--
24-
session_set_save_handler(): Argument #1 ($open) must be a valid callback, function "Array" not found or invalid function name
24+
session_set_save_handler(): Argument #1 ($open) must be a valid callback, first array member is not a valid class name or object
2525
Done

0 commit comments

Comments
 (0)