Apache Tomcat: APR/Native Connector crash leading to DoS
Description
Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') vulnerability in Apache Tomcat when using the APR/Native connector. This was particularly noticeable with client initiated closes of HTTP/2 connections.
This issue affects Apache Tomcat: from 9.0.0.M1 through 9.0.106. The following versions were EOL at the time the CVE was created but are known to be affected: 8.5.0 through 8.5.100. Other, older, EOL versions may also be affected.
Users are recommended to upgrade to version 9.0.107, which fixes the issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache Tomcat's APR/Native connector has a race condition that can cause a crash or denial of service during simultaneous client-initiated close of HTTP/2 connections.
Vulnerability
Overview
CVE-2025-52434 is a race condition vulnerability in Apache Tomcat's APR/Native connector, identified as 'Concurrent Execution using Shared Resource with Improper Synchronization.' The issue arises when multiple threads attempt to close or write to the same socket without proper locking, leading to a crash or denial of service. This is particularly noticeable during client-initiated closes of HTTP/2 connections [1][2].
Exploitation and
Attack Surface
The vulnerability resides in the APR/Native connector's handling of socket wrapper operations. An attacker can trigger the race condition by sending a crafted sequence of HTTP/2 requests and immediate connection closures. The fix introduces proper lock acquisition before checking if the socket is closed or performing close operations, preventing a situation where one thread closes the socket while another tries to write to it [1]. The bug affects all Tomcat 9.0.x versions up to 9.0.106, and EOL versions 8.5.x up to 8.5.100 may also be vulnerable [2].
Impact and
Mitigation
Successful exploitation allows an attacker to cause a denial of service by crashing the Tomcat server. No authentication is required as the attack vector is over the network via HTTP/2. Users are advised to upgrade to Apache Tomcat 9.0.107, which contains the necessary synchronization fix. No workarounds are currently available, but disabling the APR/Native connector may mitigate the risk [2][4].
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.tomcat:tomcat-utilMaven | >= 9.0.0.M1, < 9.0.107 | 9.0.107 |
org.apache.tomcat:tomcat-utilMaven | >= 8.5.0, <= 8.5.100 | — |
Affected products
2- Apache Software Foundation/Apache Tomcatv5Range: 9.0.0.M1
Patches
18a83c3c42d20Improve stability of APR/native connector.
2 files changed · +57 −28
java/org/apache/tomcat/util/net/AprEndpoint.java+54 −28 modified@@ -651,17 +651,8 @@ public void stopInternal() { // Close the SocketWrapper for each open connection - this should // trigger a IOException when the app (or container) tries to write. - // Use the blocking status write lock as a proxy for a lock on - // writing to the socket. Don't want to close it while another - // thread is writing as that could trigger a JVM crash. for (SocketWrapperBase<Long> socketWrapper : connections.values()) { - WriteLock wl = ((AprSocketWrapper) socketWrapper).getBlockingStatusWriteLock(); - wl.lock(); - try { - socketWrapper.close(); - } finally { - wl.unlock(); - } + socketWrapper.close(); } for (Long socket : connections.keySet()) { @@ -2382,6 +2373,17 @@ private void checkClosed() throws IOException { } + @Override + public void close() { + Lock lock = getLock(); + lock.lock(); + try { + super.close(); + } finally { + lock.unlock(); + } + } + @Override protected void doClose() { if (log.isDebugEnabled()) { @@ -2585,25 +2587,31 @@ public SendfileState processSendfile(SendfileDataBase sendfileData) { @Override protected void populateRemoteAddr() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_REMOTE, socket); remoteAddr = Address.getip(sa); } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noRemoteAddr", getSocket()), e); + } finally { + lock.unlock(); } } @Override protected void populateRemoteHost() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_REMOTE, socket); remoteHost = Address.getnameinfo(sa, 0); @@ -2612,68 +2620,86 @@ protected void populateRemoteHost() { } } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noRemoteHost", getSocket()), e); + } finally { + lock.unlock(); } } @Override protected void populateRemotePort() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_REMOTE, socket); Sockaddr addr = Address.getInfo(sa); remotePort = addr.port; } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noRemotePort", getSocket()), e); + } finally { + lock.unlock(); } } @Override protected void populateLocalName() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_LOCAL, socket); localName = Address.getnameinfo(sa, 0); } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noLocalName"), e); + } finally { + lock.unlock(); } } @Override protected void populateLocalAddr() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_LOCAL, socket); localAddr = Address.getip(sa); } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noLocalAddr"), e); + } finally { + lock.unlock(); } } @Override protected void populateLocalPort() { - if (isClosed()) { - return; - } + Lock lock = getLock(); + lock.lock(); try { + if (isClosed()) { + return; + } long socket = getSocket().longValue(); long sa = Address.get(Socket.APR_LOCAL, socket); Sockaddr addr = Address.getInfo(sa); localPort = addr.port; } catch (Exception e) { log.warn(sm.getString("endpoint.warn.noLocalPort"), e); + } finally { + lock.unlock(); } }
webapps/docs/changelog.xml+3 −0 modified@@ -145,6 +145,9 @@ Fix JMX value for <code>keepAliveCount</code> on the endpoint. Also add the value of <code>useVirtualThreads</code> in JMX. (remm) </fix> + <fix> + Improve stability of APR/native connector. (markt) + </fix> </changelog> </subsection> <subsection name="Jasper">
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-4j3c-42xv-3f84ghsaADVISORY
- lists.apache.org/thread/gxgh65004f25y8519coth6w7vchww030ghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2025-52434ghsaADVISORY
- www.openwall.com/lists/oss-security/2025/07/10/11ghsaWEB
- github.com/apache/tomcat/commit/8a83c3c42d20762782678932c14005cd3397a018ghsaWEB
- lists.debian.org/debian-lts-announce/2025/07/msg00009.htmlghsaWEB
News mentions
0No linked articles in our index yet.