VYPR
Moderate severityNVD Advisory· Published Jul 10, 2025· Updated Nov 4, 2025

Apache Tomcat: APR/Native Connector crash leading to DoS

CVE-2025-52434

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.

PackageAffected versionsPatched versions
org.apache.tomcat:tomcat-utilMaven
>= 9.0.0.M1, < 9.0.1079.0.107
org.apache.tomcat:tomcat-utilMaven
>= 8.5.0, <= 8.5.100

Affected products

2
  • Apache/Tomcatllm-fuzzy
    Range: >=9.0.0.M1 <=9.0.106
  • Apache Software Foundation/Apache Tomcatv5
    Range: 9.0.0.M1

Patches

1
8a83c3c42d20

Improve stability of APR/native connector.

https://github.com/apache/tomcatMark ThomasJun 26, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.