CVE-2024-1300
Description
A vulnerability in the Eclipse Vert.x toolkit causes a memory leak in TCP servers configured with TLS and SNI support. When processing an unknown SNI server name assigned the default certificate instead of a mapped certificate, the SSL context is erroneously cached in the server name map, leading to memory exhaustion. This flaw allows attackers to send TLS client hello messages with fake server names, triggering a JVM out-of-memory error.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
io.vertx:vertx-coreMaven | >= 4.3.4, < 4.4.8 | 4.4.8 |
io.vertx:vertx-coreMaven | >= 4.5.0, < 4.5.3 | 4.5.3 |
Patches
27ad34ea9d78fThe SslChannelProvider class maintains a map of server name to Netty SslContext that is filled when a client provides a server name. When a server name does not resolve to a KeyManagerFactory or TrustManagerFactory, the default factories are used and the entry is stored in the map. Instead no specific factory is resolved the default Netty SslContext is used, since this can lead to a a memory leak when a client specifies spurious SNI server names. This affects only a TCP server when SNI is set in the HttpServerOptions.
5 files changed · +39 −40
src/main/java/io/vertx/core/net/impl/SslChannelProvider.java+13 −8 modified@@ -65,6 +65,10 @@ public SslChannelProvider(SslContextProvider sslContextProvider, this.sslContextProvider = sslContextProvider; } + public int sniEntrySize() { + return sslContextMaps[0].size() + sslContextMaps[1].size(); + } + public SslContextProvider sslContextProvider() { return sslContextProvider; } @@ -83,17 +87,18 @@ public SslContext sslClientContext(String serverName, boolean useAlpn, boolean t public SslContext sslContext(String serverName, boolean useAlpn, boolean server, boolean trustAll) throws Exception { int idx = idx(useAlpn); - if (serverName == null) { - if (sslContexts[idx] == null) { - SslContext context = sslContextProvider.createContext(server, null, null, null, useAlpn, trustAll); - sslContexts[idx] = context; - } - return sslContexts[idx]; - } else { + if (serverName != null) { KeyManagerFactory kmf = sslContextProvider.resolveKeyManagerFactory(serverName); TrustManager[] trustManagers = trustAll ? null : sslContextProvider.resolveTrustManagers(serverName); - return sslContextMaps[idx].computeIfAbsent(serverName, s -> sslContextProvider.createContext(server, kmf, trustManagers, s, useAlpn, trustAll)); + if (kmf != null || trustManagers != null || !server) { + return sslContextMaps[idx].computeIfAbsent(serverName, s -> sslContextProvider.createContext(server, kmf, trustManagers, s, useAlpn, trustAll)); + } + } + if (sslContexts[idx] == null) { + SslContext context = sslContextProvider.createContext(server, null, null, serverName, useAlpn, trustAll); + sslContexts[idx] = context; } + return sslContexts[idx]; } public SslContext sslServerContext(boolean useAlpn) {
src/main/java/io/vertx/core/net/impl/SslContextProvider.java+7 −25 modified@@ -154,13 +154,6 @@ protected void initEngine(SSLEngine engine) { } } - public KeyManagerFactory loadKeyManagerFactory(String serverName) throws Exception { - if (keyManagerFactoryMapper != null) { - return keyManagerFactoryMapper.apply(serverName); - } - return null; - } - public TrustManager[] defaultTrustManagers() { return trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null; } @@ -174,8 +167,7 @@ public KeyManagerFactory defaultKeyManagerFactory() { } /** - * Resolve the {@link KeyManagerFactory} for the {@code serverName}, when a factory cannot be resolved, the default - * factory is returned. + * Resolve the {@link KeyManagerFactory} for the {@code serverName}, when a factory cannot be resolved, {@code null} is returned. * <br/> * This can block and should be executed on the appropriate thread. * @@ -184,23 +176,14 @@ public KeyManagerFactory defaultKeyManagerFactory() { * @throws Exception anything that would prevent loading the factory */ public KeyManagerFactory resolveKeyManagerFactory(String serverName) throws Exception { - KeyManagerFactory kmf = loadKeyManagerFactory(serverName); - if (kmf == null) { - kmf = keyManagerFactory; - } - return kmf; - } - - public TrustManager[] loadTrustManagers(String serverName) throws Exception { - if (trustManagerMapper != null) { - return trustManagerMapper.apply(serverName); + if (keyManagerFactoryMapper != null) { + return keyManagerFactoryMapper.apply(serverName); } return null; } /** - * Resolve the {@link TrustManager}[] for the {@code serverName}, when managers cannot be resolved, the default - * managers are returned. + * Resolve the {@link TrustManager}[] for the {@code serverName}, when managers cannot be resolved, {@code null} is returned. * <br/> * This can block and should be executed on the appropriate thread. * @@ -209,11 +192,10 @@ public TrustManager[] loadTrustManagers(String serverName) throws Exception { * @throws Exception anything that would prevent loading the managers */ public TrustManager[] resolveTrustManagers(String serverName) throws Exception { - TrustManager[] trustManagers = loadTrustManagers(serverName); - if (trustManagers == null && trustManagerFactory != null) { - trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagerMapper != null) { + return trustManagerMapper.apply(serverName); } - return trustManagers; + return null; } private VertxTrustManagerFactory buildVertxTrustManagerFactory(TrustManager[] mgrs) {
src/main/java/io/vertx/core/net/impl/SSLHelper.java+8 −0 modified@@ -126,6 +126,14 @@ public SSLHelper(TCPSSLOptions options, List<String> applicationProtocols) { this.applicationProtocols = applicationProtocols; } + public synchronized int sniEntrySize() { + CachedProvider res = cachedProvider.result(); + if (res != null) { + return res.sslChannelProvider.sniEntrySize(); + } + return 0; + } + private static class CachedProvider { final SSLOptions options; final long id;
src/main/java/io/vertx/core/net/impl/TCPServerBase.java+4 −0 modified@@ -125,6 +125,10 @@ private GlobalTrafficShapingHandler createTrafficShapingHandler(EventLoopGroup e return trafficShapingHandler; } + public int sniEntrySize() { + return sslHelper.sniEntrySize(); + } + public Future<Boolean> updateSSLOptions(SSLOptions options, boolean force) { TCPServerBase server = actualServer; if (server != null && server != this) {
src/test/java/io/vertx/core/net/NetTest.java+7 −7 modified@@ -58,10 +58,7 @@ import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.core.net.impl.HAProxyMessageCompletionHandler; -import io.vertx.core.net.impl.NetServerImpl; -import io.vertx.core.net.impl.NetSocketInternal; -import io.vertx.core.net.impl.VertxHandler; +import io.vertx.core.net.impl.*; import io.vertx.core.spi.tls.SslContextFactory; import io.vertx.core.streams.ReadStream; import io.vertx.test.core.CheckingSender; @@ -1538,14 +1535,17 @@ public void testClientSniMultipleServerName() throws Exception { receivedServerNames.add(so.indicatedServerName()); }); startServer(); - List<String> serverNames = Arrays.asList("host1", "host2.com"); + List<String> serverNames = Arrays.asList("host1", "host2.com", "fake"); + List<String> cns = new ArrayList<>(); client = vertx.createNetClient(new NetClientOptions().setSsl(true).setTrustAll(true)); for (String serverName : serverNames) { NetSocket so = client.connect(testAddress, serverName).toCompletionStage().toCompletableFuture().get(); String host = cnOf(so.peerCertificates().get(0)); - assertEquals(serverName, host); + cns.add(host); } - assertWaitUntil(() -> receivedServerNames.size() == 2); + assertEquals(Arrays.asList("host1", "host2.com", "localhost"), cns); + assertEquals(2, ((TCPServerBase)server).sniEntrySize()); + assertWaitUntil(() -> receivedServerNames.size() == 3); assertEquals(receivedServerNames, serverNames); }
3d9235cadf44The SslChannelProvider class maintains a map of server name to Netty SslContext that is filled when a client provides a server name. When a server name does not resolve to a KeyManagerFactory or TrustManagerFactory, the default factories are used and the entry is stored in the map. Instead no specific factory is resolved the default Netty SslContext is used, since this can lead to a a memory leak when a client specifies spurious SNI server names. This affects only a TCP server when SNI is set in the HttpServerOptions.
5 files changed · +39 −40
src/main/java/io/vertx/core/net/impl/SslChannelProvider.java+13 −8 modified@@ -65,6 +65,10 @@ public SslChannelProvider(SslContextProvider sslContextProvider, this.sslContextProvider = sslContextProvider; } + public int sniEntrySize() { + return sslContextMaps[0].size() + sslContextMaps[1].size(); + } + public SslContextProvider sslContextProvider() { return sslContextProvider; } @@ -83,17 +87,18 @@ public SslContext sslClientContext(String serverName, boolean useAlpn, boolean t public SslContext sslContext(String serverName, boolean useAlpn, boolean server, boolean trustAll) throws Exception { int idx = idx(useAlpn); - if (serverName == null) { - if (sslContexts[idx] == null) { - SslContext context = sslContextProvider.createContext(server, null, null, null, useAlpn, trustAll); - sslContexts[idx] = context; - } - return sslContexts[idx]; - } else { + if (serverName != null) { KeyManagerFactory kmf = sslContextProvider.resolveKeyManagerFactory(serverName); TrustManager[] trustManagers = trustAll ? null : sslContextProvider.resolveTrustManagers(serverName); - return sslContextMaps[idx].computeIfAbsent(serverName, s -> sslContextProvider.createContext(server, kmf, trustManagers, s, useAlpn, trustAll)); + if (kmf != null || trustManagers != null || !server) { + return sslContextMaps[idx].computeIfAbsent(serverName, s -> sslContextProvider.createContext(server, kmf, trustManagers, s, useAlpn, trustAll)); + } + } + if (sslContexts[idx] == null) { + SslContext context = sslContextProvider.createContext(server, null, null, serverName, useAlpn, trustAll); + sslContexts[idx] = context; } + return sslContexts[idx]; } public SslContext sslServerContext(boolean useAlpn) {
src/main/java/io/vertx/core/net/impl/SslContextProvider.java+7 −25 modified@@ -154,13 +154,6 @@ protected void initEngine(SSLEngine engine) { } } - public KeyManagerFactory loadKeyManagerFactory(String serverName) throws Exception { - if (keyManagerFactoryMapper != null) { - return keyManagerFactoryMapper.apply(serverName); - } - return null; - } - public TrustManager[] defaultTrustManagers() { return trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null; } @@ -174,8 +167,7 @@ public KeyManagerFactory defaultKeyManagerFactory() { } /** - * Resolve the {@link KeyManagerFactory} for the {@code serverName}, when a factory cannot be resolved, the default - * factory is returned. + * Resolve the {@link KeyManagerFactory} for the {@code serverName}, when a factory cannot be resolved, {@code null} is returned. * <br/> * This can block and should be executed on the appropriate thread. * @@ -184,23 +176,14 @@ public KeyManagerFactory defaultKeyManagerFactory() { * @throws Exception anything that would prevent loading the factory */ public KeyManagerFactory resolveKeyManagerFactory(String serverName) throws Exception { - KeyManagerFactory kmf = loadKeyManagerFactory(serverName); - if (kmf == null) { - kmf = keyManagerFactory; - } - return kmf; - } - - public TrustManager[] loadTrustManagers(String serverName) throws Exception { - if (trustManagerMapper != null) { - return trustManagerMapper.apply(serverName); + if (keyManagerFactoryMapper != null) { + return keyManagerFactoryMapper.apply(serverName); } return null; } /** - * Resolve the {@link TrustManager}[] for the {@code serverName}, when managers cannot be resolved, the default - * managers are returned. + * Resolve the {@link TrustManager}[] for the {@code serverName}, when managers cannot be resolved, {@code null} is returned. * <br/> * This can block and should be executed on the appropriate thread. * @@ -209,11 +192,10 @@ public TrustManager[] loadTrustManagers(String serverName) throws Exception { * @throws Exception anything that would prevent loading the managers */ public TrustManager[] resolveTrustManagers(String serverName) throws Exception { - TrustManager[] trustManagers = loadTrustManagers(serverName); - if (trustManagers == null && trustManagerFactory != null) { - trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagerMapper != null) { + return trustManagerMapper.apply(serverName); } - return trustManagers; + return null; } private VertxTrustManagerFactory buildVertxTrustManagerFactory(TrustManager[] mgrs) {
src/main/java/io/vertx/core/net/impl/SSLHelper.java+8 −0 modified@@ -124,6 +124,14 @@ public SSLHelper(TCPSSLOptions options, List<String> applicationProtocols) { this.applicationProtocols = applicationProtocols; } + public synchronized int sniEntrySize() { + CachedProvider res = cachedProvider.result(); + if (res != null) { + return res.sslChannelProvider.sniEntrySize(); + } + return 0; + } + private static class CachedProvider { final SSLOptions options; final SslChannelProvider sslChannelProvider;
src/main/java/io/vertx/core/net/impl/TCPServerBase.java+4 −0 modified@@ -127,6 +127,10 @@ private GlobalTrafficShapingHandler createTrafficShapingHandler(EventLoopGroup e return trafficShapingHandler; } + public int sniEntrySize() { + return sslHelper.sniEntrySize(); + } + public Future<Void> updateSSLOptions(SSLOptions options) { TCPServerBase server = actualServer; if (server != null && server != this) {
src/test/java/io/vertx/core/net/NetTest.java+7 −7 modified@@ -58,10 +58,7 @@ import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.core.net.impl.HAProxyMessageCompletionHandler; -import io.vertx.core.net.impl.NetServerImpl; -import io.vertx.core.net.impl.NetSocketInternal; -import io.vertx.core.net.impl.VertxHandler; +import io.vertx.core.net.impl.*; import io.vertx.core.spi.tls.SslContextFactory; import io.vertx.core.streams.ReadStream; import io.vertx.test.core.CheckingSender; @@ -1536,14 +1533,17 @@ public void testClientSniMultipleServerName() throws Exception { receivedServerNames.add(so.indicatedServerName()); }); startServer(); - List<String> serverNames = Arrays.asList("host1", "host2.com"); + List<String> serverNames = Arrays.asList("host1", "host2.com", "fake"); + List<String> cns = new ArrayList<>(); client = vertx.createNetClient(new NetClientOptions().setSsl(true).setTrustAll(true)); for (String serverName : serverNames) { NetSocket so = client.connect(testAddress, serverName).toCompletionStage().toCompletableFuture().get(); String host = cnOf(so.peerCertificates().get(0)); - assertEquals(serverName, host); + cns.add(host); } - assertWaitUntil(() -> receivedServerNames.size() == 2); + assertEquals(Arrays.asList("host1", "host2.com", "localhost"), cns); + assertEquals(2, ((TCPServerBase)server).sniEntrySize()); + assertWaitUntil(() -> receivedServerNames.size() == 3); assertEquals(receivedServerNames, serverNames); }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
18- github.com/advisories/GHSA-9ph3-v2vh-3qx7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-1300ghsaADVISORY
- access.redhat.com/errata/RHSA-2024:1662nvdWEB
- access.redhat.com/errata/RHSA-2024:1706nvdWEB
- access.redhat.com/errata/RHSA-2024:1923nvdWEB
- access.redhat.com/errata/RHSA-2024:2088nvdWEB
- access.redhat.com/errata/RHSA-2024:2833nvdWEB
- access.redhat.com/errata/RHSA-2024:3527nvdWEB
- access.redhat.com/errata/RHSA-2024:3989nvdWEB
- access.redhat.com/errata/RHSA-2024:4884nvdWEB
- access.redhat.com/security/cve/CVE-2024-1300nvdWEB
- bugzilla.redhat.com/show_bug.cginvdWEB
- github.com/eclipse-vertx/vert.x/commit/3d9235cadf44df39a70dc75bddfe0b8fcbd6a683ghsaWEB
- github.com/eclipse-vertx/vert.x/commit/7ad34ea9d78f85e26b231ee3ec8d492d10046479ghsaWEB
- github.com/eclipse-vertx/vert.x/pull/5099ghsaWEB
- github.com/eclipse-vertx/vert.x/pull/5100ghsaWEB
- github.com/eclipse-vertx/vert.x/pull/5101ghsaWEB
- vertx.io/docs/vertx-core/java/nvdWEB
News mentions
0No linked articles in our index yet.