From 37c62449ac378cc7e190db2758cc1b8e831297de Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 5 Aug 2026 08:20:52 -0700 Subject: [PATCH] Look the SSLContext up from the SSL_CTX in the ALPN/NPN callbacks ossl_sslctx_mark uses rb_gc_mark_movable, so the SSLContext relocates. Its VALUE is stored in four places: the SSL_CTX's ex_data, and the callback argument of the NPN advertise, NPN select and ALPN select callbacks. ossl_sslctx_compact updates the first. Nothing updates the other three, so after a compaction they hold the pre-move address. The three callbacks all receive the SSL, and the SSL_CTX's ex_data copy is already kept current -- so they can look the object up instead of carrying their own copy, which leaves exactly one stored copy and one place to maintain. Registration is one-shot (ossl_sslctx_setup returns early when self is frozen), so the stale address is captured at the first handshake and never refreshed. Fixes #1088. --- ext/openssl/ossl_ssl.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index fcbbec0b3..7cedec7bf 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -579,12 +579,21 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, return SSL_TLSEXT_ERR_OK; } +static VALUE +ossl_sslctx_obj_from_ssl(const SSL *ssl) +{ + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + + return (VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx); +} + #ifdef OSSL_USE_NEXTPROTONEG static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { - VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded); + VALUE protocols = rb_attr_get(ossl_sslctx_obj_from_ssl(ssl), + id_npn_protocols_encoded); *out = (const unsigned char *) RSTRING_PTR(protocols); *outlen = RSTRING_LENINT(protocols); @@ -598,7 +607,7 @@ ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, { VALUE sslctx_obj, cb; - sslctx_obj = (VALUE) arg; + sslctx_obj = ossl_sslctx_obj_from_ssl(ssl); cb = rb_attr_get(sslctx_obj, id_i_npn_select_cb); return ssl_npn_select_cb_common(ssl, cb, (const unsigned char **)out, @@ -612,7 +621,7 @@ ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, { VALUE sslctx_obj, cb; - sslctx_obj = (VALUE) arg; + sslctx_obj = ossl_sslctx_obj_from_ssl(ssl); cb = rb_attr_get(sslctx_obj, id_i_alpn_select_cb); return ssl_npn_select_cb_common(ssl, cb, out, outlen, in, inlen); @@ -807,11 +816,11 @@ ossl_sslctx_setup(VALUE self) if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); rb_ivar_set(self, id_npn_protocols_encoded, encoded); - SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); + SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, NULL); OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { - SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); + SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, NULL); OSSL_Debug("SSL NPN select callback added"); } #endif @@ -827,7 +836,7 @@ ossl_sslctx_setup(VALUE self) OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) { - SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); + SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, NULL); OSSL_Debug("SSL ALPN select callback added"); }