Fix ALPN resolver being collected while OpenSSL still holds it - #81
Merged
Conversation
SeanTAllen
added a commit
that referenced
this pull request
Jul 9, 2026
`SSLContext.alpn_set_resolver` hands the resolver to OpenSSL as ALPN select callback data. OpenSSL keeps a raw pointer to it and reads it live from the `SSL_CTX` on every server handshake. Nothing on the Pony side kept the Pony `SSLContext` alive once the caller dropped it: a session held only the raw `SSL_CTX` pointer, which keeps the C context alive through a refcount but is invisible to the garbage collector. So the context, and the resolver it holds, could be collected while a session made from it was still handshaking, and a peer connecting would drive a server handshake into freed memory. A session now holds its `SSLContext`. The context holds the current resolver, so one live session keeps both alive, and the same fix covers setting the resolver after a session exists and replacing it. To hold the context, a session needs a sendable reference to it, and it has to be `val` rather than `tag` because the garbage collector does not trace a `tag`'s fields, so `client` and `server` take a `val` receiver. That also makes reconfiguring a context after making sessions from it impossible to write. `alpn_set_resolver` and the `ALPNProtocolResolver` interface take `val` for the same sharing reason. Closes #69 Closes #83
SeanTAllen
force-pushed
the
fix-alpn-resolver-lifetime
branch
from
July 9, 2026 23:29
c9d160c to
de8e8c9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SSLContext.alpn_set_resolverhanded the resolver to OpenSSL as ALPN select callback data, and nothing on the Pony side kept it alive. The deeper problem is that a session never held the PonySSLContextthat made it —SSL._createonly took the rawSSL_CTXpointer, which keeps the C context alive through a refcount but is invisible to the garbage collector. So the Pony context, and the resolver it holds, could be collected while a session made from it was still handshaking. OpenSSL reads the resolver pointer live from theSSL_CTXand calls into freed memory. The trigger is remote: a peer connecting drives a server handshake into the callback.The fix is that a session now holds its
SSLContext. The context stays alive as long as any session made from it, and the context holds the current resolver, so the resolver lives as long as anything can call it. That closes the original drop-the-context case and the two follow-on cases — setting the resolver after a session exists, and replacing it — in one move. There is no per-session resolver copy anymore; the context is the single place the resolver is held.To hold the context, a session, which is
isoand gets sent between actors, needs a sendable reference to it, and that reference has to bevalrather thantagbecause the garbage collector does not trace atag's fields. Soclient()andserver()now take avalreceiver, andalpn_set_resolverand theALPNProtocolResolverinterface takeval. This also makes the dangerous orderings impossible to write: setting the resolver needs a mutable context, making a session needs an immutable one, and a context is frozen once, so all configuration happens before any session exists.ALPNStandardProtocolResolveris alreadyval. Callers configure a context and then make sessions from it, which is already the idiomaticrecover val SSLContext .> ... endpattern; callers who held a mutable context across session creation now have to freeze it first.Closes #69. Closes #83.