From 5af10540053549b83aae75315df17be54b5d4834 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 13 May 2026 15:53:42 -0400 Subject: [PATCH] ext/openssl: Refresh SSL_CTX ex_data slot when a child stream reuses parent CTX php_openssl_create_server_ctx() writes the stream pointer into SSL_CTX_set_ex_data() so the per-CTX session callbacks (php_openssl_session_new_cb / _get_cb / _remove_cb) can find the php_stream and dispatch into the user-supplied PHP callables. The setup_crypto reuse branch (when a child stream is opened with the session option pointing at a parent stream) up-refs the parent's SSL_CTX and shares it, but does not refresh the ex_data slot. The slot keeps pointing at the parent stream; if the parent is closed first while the child still holds a CTX reference, the ex_data is a stale pointer. The dangle is currently dormant: the only PHP path that registers SSL_CTX_sess_set_remove_cb (the consumer that would deref the slot) also forces SSL_SESS_CACHE_NO_INTERNAL and SSL_OP_NO_TICKET, which together leave libssl with nothing to evict, so remove_cb does not fire. Client-side wiring never registers the remove callback at all. No PHP-script path currently triggers the UAF. The fix is one line of defense-in-depth: refresh the slot to the most recent owner stream after the up_ref. Any future change that relaxes NO_INTERNAL, drops NO_TICKET, exposes SSL_CTX_remove_session, or wires the remove callback on a client CTX now lands on a live pointer instead of regressing into a UAF. --- ext/openssl/xp_ssl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index ed72ca49677f..f51820c2203f 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -2127,6 +2127,7 @@ static zend_result php_openssl_setup_crypto(php_stream *stream, } else { SSL_CTX_up_ref(parent_sslsock->ctx); sslsock->ctx = parent_sslsock->ctx; + SSL_CTX_set_ex_data(sslsock->ctx, php_openssl_get_ctx_stream_data_index(), stream); if (parent_sslsock->session_callbacks) { parent_sslsock->session_callbacks->refcount++; sslsock->session_callbacks = parent_sslsock->session_callbacks;