Skip to content

Commit 499fbcd

Browse files
committed
Actually fix GH-9583
The issue is that PS(mod)->s_validate_sid is always defined for user modules, thus we need to check that the actual callable is set Add another regression test to ensure current working behaviour is not broken (which was by the previous incorrect fix) Closes GH-9638
1 parent 072dc3c commit 499fbcd

File tree

2 files changed

+69
-14
lines changed

2 files changed

+69
-14
lines changed

ext/session/session.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,9 +1083,10 @@ PHPAPI int php_session_register_module(const ps_module *ptr) /* {{{ */
10831083
/* }}} */
10841084

10851085
/* Dummy PS module function */
1086-
/* We consider any ID valid, so we return FAILURE to indicate that a session doesn't exist */
1086+
/* We consider any ID valid (thus also implying that a session with such an ID exists),
1087+
thus we always return SUCCESS */
10871088
PHPAPI int php_session_validate_sid(PS_VALIDATE_SID_ARGS) {
1088-
return FAILURE;
1089+
return SUCCESS;
10891090
}
10901091

10911092
/* Dummy PS module function */
@@ -2255,18 +2256,24 @@ PHP_FUNCTION(session_regenerate_id)
22552256
}
22562257
RETURN_THROWS();
22572258
}
2258-
if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
2259-
PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == SUCCESS) {
2260-
zend_string_release_ex(PS(id), 0);
2261-
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
2262-
if (!PS(id)) {
2263-
PS(mod)->s_close(&PS(mod_data));
2264-
PS(session_status) = php_session_none;
2265-
if (!EG(exception)) {
2266-
zend_throw_error(NULL, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
2259+
if (PS(use_strict_mode)) {
2260+
if ((!PS(mod_user_implemented) && PS(mod)->s_validate_sid) || !Z_ISUNDEF(PS(mod_user_names).name.ps_validate_sid)) {
2261+
int limit = 3;
2262+
/* Try to generate non-existing ID */
2263+
while (limit-- && PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == SUCCESS) {
2264+
zend_string_release_ex(PS(id), 0);
2265+
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
2266+
if (!PS(id)) {
2267+
PS(mod)->s_close(&PS(mod_data));
2268+
PS(session_status) = php_session_none;
2269+
if (!EG(exception)) {
2270+
zend_throw_error(NULL, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
2271+
}
2272+
RETURN_THROWS();
2273+
}
22672274
}
2268-
RETURN_THROWS();
22692275
}
2276+
// TODO warn that ID cannot be verified? else { }
22702277
}
22712278
/* Read is required to make new session data at this point. */
22722279
if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) {
@@ -2293,7 +2300,6 @@ PHP_FUNCTION(session_regenerate_id)
22932300
/* }}} */
22942301

22952302
/* {{{ Generate new session ID. Intended for user save handlers. */
2296-
/* This is not used yet */
22972303
PHP_FUNCTION(session_create_id)
22982304
{
22992305
zend_string *prefix = NULL, *new_id;
@@ -2317,7 +2323,7 @@ PHP_FUNCTION(session_create_id)
23172323
int limit = 3;
23182324
while (limit--) {
23192325
new_id = PS(mod)->s_create_sid(&PS(mod_data));
2320-
if (!PS(mod)->s_validate_sid) {
2326+
if (!PS(mod)->s_validate_sid || (PS(mod_user_implemented) && Z_ISUNDEF(PS(mod_user_names).name.ps_validate_sid))) {
23212327
break;
23222328
} else {
23232329
/* Detect collision and retry */

ext/session/tests/gh9583-extra.phpt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-9583: session_create_id() fails with user defined save handler that doesn't have a validateId() method
3+
--EXTENSIONS--
4+
session
5+
--SKIPIF--
6+
<?php include('skipif.inc'); ?>
7+
--FILE--
8+
<?php
9+
10+
class SessionHandlerTester implements \SessionHandlerInterface
11+
{
12+
13+
public function close(): bool { return true; }
14+
15+
public function destroy($id): bool { return true; }
16+
17+
public function gc($max_lifetime): int|false { return 1; }
18+
19+
public function open($path, $name): bool { return true; }
20+
21+
public function read($id): string { return ''; }
22+
23+
public function write($id, $data): bool { return true; }
24+
25+
//public function create_sid() { return uniqid(); }
26+
27+
//public function validateId($key) { return true; }
28+
}
29+
30+
$obj = new SessionHandlerTester();
31+
ini_set('session.use_strict_mode','1');
32+
session_set_save_handler($obj);
33+
session_start();
34+
35+
$originalSessionId = session_id();
36+
37+
session_write_close();
38+
39+
session_start();
40+
41+
$newSessionId = session_id();
42+
43+
echo 'validateId() ', (method_exists($obj, 'validateId') ? ('returns ' . ($obj->validateId(1) ? 'true' : 'false')) : 'is commented out'), "\n";
44+
var_dump($originalSessionId == $newSessionId);
45+
46+
?>
47+
--EXPECT--
48+
validateId() is commented out
49+
bool(true)

0 commit comments

Comments
 (0)