From f9b3c7bdc6a88b61ab62ce744cc8b6c00848b639 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 29 Jul 2026 12:01:44 -0400 Subject: [PATCH] ext/session: check the created ID before validating it session_create_id() passes the return value of s_create_sid() straight into s_validate_sid(). A userland create_sid() that throws makes ps_create_sid_user() return NULL, and ps_validate_sid_user() then reaches ZVAL_STR_COPY() with a NULL key. Stop the retry loop when no ID was created and propagate a pending exception instead of reporting a plain failure. The four s_create_sid() call sites in php_session_initialize() and session_regenerate_id() already test for NULL. Closes GH-22924 --- ext/session/session.c | 6 +++ .../session_create_id_create_sid_throws.phpt | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt diff --git a/ext/session/session.c b/ext/session/session.c index ba71d709a536..8e393b08fe56 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -2505,6 +2505,9 @@ PHP_FUNCTION(session_create_id) int limit = 3; while (limit--) { new_id = PS(mod)->s_create_sid(&PS(mod_data)); + if (!new_id) { + break; + } if (!PS(mod)->s_validate_sid || (PS(mod_user_implemented) && Z_ISUNDEF(PS(mod_user_names).ps_validate_sid))) { break; } else { @@ -2526,6 +2529,9 @@ PHP_FUNCTION(session_create_id) zend_string_release_ex(new_id, 0); } else { smart_str_free(&id); + if (EG(exception)) { + RETURN_THROWS(); + } php_error_docref(NULL, E_WARNING, "Failed to create new ID"); RETURN_FALSE; } diff --git a/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt b/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt new file mode 100644 index 000000000000..b65c0671d940 --- /dev/null +++ b/ext/session/tests/user_session_module/session_create_id_create_sid_throws.phpt @@ -0,0 +1,49 @@ +--TEST-- +session_create_id() when the create_sid handler throws +--INI-- +session.save_handler=files +session.name=PHPSESSID +session.gc_probability=0 +--EXTENSIONS-- +session +--FILE-- +calls++ > 0) { + throw new Exception('create_sid failed'); + } + return parent::create_sid(); + } + + public function validateId(string $id): bool + { + return false; + } +} + +session_set_save_handler(new MySessionHandler(), true); +session_start(); + +try { + session_create_id(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; + $previous = $e->getPrevious(); + echo $previous::class, ": ", $previous->getMessage(), PHP_EOL; +} + +var_dump(session_status() === PHP_SESSION_ACTIVE); + +?> +--EXPECT-- +Error: Session id must be a string +Exception: create_sid failed +bool(true)