From ab4aafd3fa0ba9656b79e519cc287773a07fd3df Mon Sep 17 00:00:00 2001 From: Kevin Dunglas Date: Thu, 16 Jul 2026 12:49:12 +0200 Subject: [PATCH] fix: reset session state when session ext is not linked at build time frankenphp_reset_session_state() clears PS(id) between worker requests so a session id cannot bleed across concurrent clients. That function is guarded by HAVE_PHP_SESSION, defined only when the session extension is visible at FrankenPHP compile time. When PHP ships session as a shared or out-of-tree module, HAVE_PHP_SESSION is undefined and the reset is compiled out. Session still works at runtime, but PS(id) survives the request boundary. Since session_start() reads the request cookie only when PS(id) is NULL, a worker reuses the previous request's id and can hand one client another client's session id. Fall back to resetting the session module through the module registry in that build configuration so the per-request cleanup always runs. Fixes GHSA-v3ph-cgqh-r8p5 --- frankenphp.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frankenphp.c b/frankenphp.c index 99e95bbc9e..823ba11c1c 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -64,7 +64,21 @@ ZEND_TSRMLS_CACHE_DEFINE() * * @see https://github.com/DataDog/dd-trace-php/pull/3169 for an example */ -static const char *MODULES_TO_RELOAD[] = {"filter", NULL}; +static const char *MODULES_TO_RELOAD[] = { + "filter", +#ifndef HAVE_PHP_SESSION + /* When the session extension is not visible at build time (e.g. it is + * loaded as a shared module), frankenphp_reset_session_state() is + * compiled out. Reset the session module through the registry instead so + * PS(id) is cleared between worker requests and a stale id cannot bleed + * across concurrent clients. See GHSA-v3ph-cgqh-r8p5. + * + * Unlike frankenphp_reset_session_state(), the module RSHUTDOWN frees + * save handlers registered from the worker bootstrap; that is an + * acceptable trade-off for a build where the extension isn't linked. */ + "session", +#endif + NULL}; frankenphp_version frankenphp_get_version() { return (frankenphp_version){