Skip to content

Fix ALPN resolver being collected while OpenSSL still holds it - #81

Merged
SeanTAllen merged 1 commit into
mainfrom
fix-alpn-resolver-lifetime
Jul 10, 2026
Merged

Fix ALPN resolver being collected while OpenSSL still holds it#81
SeanTAllen merged 1 commit into
mainfrom
fix-alpn-resolver-lifetime

Conversation

@SeanTAllen

@SeanTAllen SeanTAllen commented Jul 9, 2026

Copy link
Copy Markdown
Member

SSLContext.alpn_set_resolver handed 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 Pony SSLContext that made it — SSL._create only took the raw SSL_CTX pointer, 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 the SSL_CTX and 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 iso and gets sent between actors, needs a sendable reference to it, and that reference has to be val rather than tag because the garbage collector does not trace a tag's fields. So client() and server() now take a val receiver, and alpn_set_resolver and the ALPNProtocolResolver interface take val. 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.

ALPNStandardProtocolResolver is already val. Callers configure a context and then make sessions from it, which is already the idiomatic recover val SSLContext .> ... end pattern; callers who held a mutable context across session creation now have to freeze it first.

Closes #69. Closes #83.

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Jul 9, 2026
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
SeanTAllen force-pushed the fix-alpn-resolver-lifetime branch from c9d160c to de8e8c9 Compare July 9, 2026 23:29
@SeanTAllen
SeanTAllen merged commit d1b24de into main Jul 10, 2026
12 checks passed
@SeanTAllen
SeanTAllen deleted the fix-alpn-resolver-lifetime branch July 10, 2026 00:01
@ponylang-main ponylang-main removed the discuss during sync Should be discussed during an upcoming sync label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants