CVE-2026-79677
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Apache Tomcat is an open-source implementation of the Jakarta Servlet, Jakarta Pages, Jakarta Expression Language, Jakarta WebSocket, Jakarta Annotations, and Jakarta Authentication specifications. It serves Java web applications either as a standalone servlet container and HTTP server or embedded inside an application through the tomcat-embed-core artifact. Its Catalina servlet container (org.apache.tomcat:tomcat-catalina) supplies the request-processing pipeline, including Valves such as RewriteValve, along with connectors, realms, and session management.
A Denial of Service (DoS) vulnerability (CVE-2026-79677) has been identified in the server-side WebSocket write timeout handling of Apache Tomcat, which allows attackers to keep WebSocket connections and their associated resources alive indefinitely by holding asynchronous writes open until their timeouts are lost.
Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others.
This issue affects the server-side Jakarta WebSocket implementation of Apache Tomcat.
Module Info
- Product: Apache Tomcat
- Affected packages:
org.apache.tomcat:tomcat-websocket,org.apache.tomcat.embed:tomcat-embed-websocket - Affected versions: >=11.0.0-M1 <11.0.26, >=10.1.0-M1 <10.1.60, >=9.0.0.M1 <9.0.122, >=8.5.0 <=8.5.100, >=7.0.43 <=7.0.109
- GitHub repository: https://github.com/apache/tomcat
- Published packages: https://central.sonatype.com/artifact/org.apache.tomcat/tomcat-websocket
- Package manager: Maven
- Fixed in:
- NES for Apache Tomcat 8.5.100-tomcat-8.5.112
- Apache Tomcat 11.0.26, 10.1.60, 9.0.122 (OSS)
Vulnerability Info
This Medium-severity vulnerability is found in the org.apache.tomcat:tomcat-websocket package in the server-side WebSocket implementation of Apache Tomcat.
Tomcat tracks pending asynchronous WebSocket writes in WsWriteTimeout, a background process that runs once per second and expires endpoints whose write has taken too long. Every endpoint with an outstanding non-blocking write is added to a ConcurrentSkipListSet whose ordering comes from a comparator that looks only at the endpoint's timeout expiry:
private final Set<WsRemoteEndpointImplServer> endpoints = new ConcurrentSkipListSet<>(new EndpointComparator());
public void register(WsRemoteEndpointImplServer endpoint) {
boolean result = endpoints.add(endpoint);
if (result) {
int newCount = count.incrementAndGet();
if (newCount == 1) {
BackgroundProcessManager.getInstance().register(this);
}
}
}
private static class EndpointComparator implements Comparator<WsRemoteEndpointImplServer> {
@Override
public int compare(WsRemoteEndpointImplServer o1, WsRemoteEndpointImplServer o2) {
long t1 = o1.getTimeoutExpiry();
long t2 = o2.getTimeoutExpiry();
if (t1 < t2) {
return -1;
} else if (t1 == t2) {
return 0;
} else {
return 1;
}
}
}
A sorted set treats a comparator result of 0 as "already present", so when two different endpoints have write timeouts that expire in the same millisecond the second add() returns false and that endpoint is silently never registered. The write it was guarding then has no timeout at all: nothing will ever call back into it to fail the pending send, so the connection, its socket, and its buffers stay allocated for as long as the client keeps the write from completing. The expiry value is supplied by the remote peer's behaviour, since a client that stops reading controls when writes stall and therefore when the timeouts of concurrent connections collide.
The same expiry-based ordering is also used to cut the scan short, even though an endpoint's expiry can be changed concurrently by a completing or newly started write, so endpoints that are registered can be skipped as well:
long now = System.currentTimeMillis();
for (WsRemoteEndpointImplServer endpoint : endpoints) {
if (endpoint.getTimeoutExpiry() < now) {
// Background thread, not the thread that triggered the
// write so no need to use a dispatch
endpoint.onTimeout(false);
} else {
// Endpoints are ordered by timeout expiry so if this point
// is reached there is no need to check the remaining
// endpoints
break;
}
}
Registration and completion are not coordinated either. WsRemoteEndpointImplServer publishes the handler, computes the expiry, and registers with the timeout thread outside of any lock, and the timeout callback acts on whatever it observes without re-reading the expiry it was woken for:
} else {
this.handler = handler;
timeout = getSendTimeout();
if (timeout > 0) {
// Register with timeout thread
timeoutExpiry = timeout + System.currentTimeMillis();
wsWriteTimeout.register(this);
}
}
protected void onTimeout(boolean useDispatch) {
if (handler != null) {
clearHandler(new SocketTimeoutException(), useDispatch);
}
close();
}
A remote peer that opens many WebSocket connections and stops consuming data can keep asynchronous writes pending on all of them. Timeouts that collide on the same millisecond are dropped, the affected sends never fail, and the server accumulates connections, threads, and buffers that are never reclaimed, exhausting resources and denying service to legitimate clients.
This vulnerability was introduced in 2013 with Apache Tomcat 8.0.0-RC1.
Mitigation
Only recent versions of Apache Tomcat are community-supported. The community support version will not receive any updates to address this issue. For more information, see here.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a patched version of Apache Tomcat.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.