Skip to content

SSL: return nullptr from SSLNetVCAccess for non-SSL handles - #13630

Draft
brbzull0 wants to merge 1 commit into
apache:masterfrom
brbzull0:sni-ssl-netvc-access-guard
Draft

SSL: return nullptr from SSLNetVCAccess for non-SSL handles#13630
brbzull0 wants to merge 1 commit into
apache:masterfrom
brbzull0:sni-ssl-netvc-access-guard

Conversation

@brbzull0

@brbzull0 brbzull0 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

SSLNetVCAttach() (src/iocore/net/SSLUtils.cc:1994) is called from exactly one
place, SSLNetVConnection::SSLNetVConnection
(src/iocore/net/SSLNetVConnection.cc:145). Nothing on the QUIC side attaches,
so an SSL handle belonging to a QUIC connection has no ssl_vc_index ex_data.

SSLNetVCAccess() then did:

netvc = static_cast<SSLNetVConnection *>(SSL_get_ex_data(ssl, ssl_vc_index));
ink_assert(dynamic_cast<SSLNetVConnection *>(static_cast<NetVConnection *>(SSL_get_ex_data(ssl, ssl_vc_index))));
return netvc;

The dynamic_cast was only evaluated inside ink_assert, so in a release build
the unchecked value was returned as-is. Three callbacks that a QUIC handshake
also reaches then used it without a null check:

  • SNI_IpAllow::SNIAction() -- src/iocore/net/SNIActionPerformer.cc:418
  • ssl_client_cert_callback() -- src/iocore/net/SSLClientUtils.cc:211
  • ssl_verify_client_callback() -- src/iocore/net/SSLUtils.cc:200

This makes the dynamic_cast the return value, so the accessor reports a handle
that is not an SSLNetVConnection instead of handing back something unchecked,
and adds the corresponding guard at those three call sites.

The cast is well defined: SSLNetVConnection derives from
UnixNetVConnection, which derives from NetVConnection, leftmost at both
levels, so NetVConnection is the primary base subobject at offset 0 and the
void * recovered from ex_data needs no adjustment. The ink_assert being
removed already performed this exact cast.

Test

No new test. Reaching the null case requires a QUIC handshake to drive an
SSL_CTX callback shared with the TLS path, which the existing autest harness
does not set up directly.

Existing coverage run against this change, 8/8 pass:

  • tls_sni_ip_allow -- exercises SNI_IpAllow::SNIAction() directly
  • tls_client_verify, tls_client_verify2, tls_client_verify3 --
    ssl_verify_client_callback()
  • tls_client_cert, tls_client_cert_override -- ssl_client_cert_callback()
  • h3_sni_check, h3_proxy_verifier -- the QUIC path

These confirm the non-null path through all three callbacks is unchanged.

SSLNetVCAttach() is only ever called by SSLNetVConnection, so an SSL
handle belonging to a QUIC connection carries no ssl_vc_index ex_data.
SSLNetVCAccess() static_cast that null result to SSLNetVConnection * and
checked the dynamic_cast only through ink_assert, so release builds
returned it unchecked to three callbacks that QUIC also reaches. Use the
dynamic_cast as the return value and guard those three callers.
@brbzull0 brbzull0 self-assigned this Sep 3, 2026
@brbzull0 brbzull0 added this to the 11.0.0 milestone Sep 3, 2026
@brbzull0

brbzull0 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

[approve ci autest 0 2]

@brbzull0

brbzull0 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

[approve ci autest 2]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant