Skip to content

ext/openssl: Refresh SSL_CTX ex_data slot when a child stream reuses parent CTX - #66

Open
iliaal wants to merge 1 commit into
masterfrom
fix/openssl-ss002-ex-data-refresh
Open

iliaal wants to merge 1 commit into
masterfrom
fix/openssl-ss002-ex-data-refresh

Conversation

@iliaal

@iliaal iliaal commented May 13, 2026 •

Copy link
Copy Markdown
Owner

php_openssl_create_server_ctx() stores the stream pointer via SSL_CTX_set_ex_data() so the per-CTX session callbacks can find their php_stream. When setup_crypto opens a child stream whose session context option points at a parent stream, it up-refs and shares the parent SSL_CTX without refreshing ex_data; the slot keeps pointing at the parent stream and dangles once the parent closes before the child. The reuse branch now refreshes ex_data to the most recent owner stream right after the up_ref, so any future remove-callback consumer lands on a live pointer instead of freed memory.

…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant