VYPR
High severityNVD Advisory· Published Jun 26, 2020· Updated Aug 4, 2024

CVE-2020-11996

CVE-2020-11996

Description

A specially crafted sequence of HTTP/2 requests sent to Apache Tomcat 10.0.0-M1 to 10.0.0-M5, 9.0.0.M1 to 9.0.35 and 8.5.0 to 8.5.55 could trigger high CPU usage for several seconds. If a sufficient number of such requests were made on concurrent HTTP/2 connections, the server could become unresponsive.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A specially crafted sequence of HTTP/2 requests can cause high CPU usage and denial of service in Apache Tomcat versions 8.5.0-8.5.55, 9.0.0.M1-9.0.35, and 10.0.0-M1-10.0.0-M5.

Vulnerability

CVE-2020-11996 is a denial-of-service vulnerability in Apache Tomcat's HTTP/2 handling. The root cause is that a specially crafted sequence of HTTP/2 requests can trigger excessive CPU consumption for several seconds. This occurs when the server processes malformed or specially constructed frames that cause expensive internal operations, leading to a temporary resource exhaustion spike. [1][2][3]

Exploitation

The attack does not require authentication; an unauthenticated remote attacker can send a small number of malicious HTTP/2 requests to trigger the condition. However, to make the server unresponsive, an attacker needs to send such requests concurrently across multiple HTTP/2 connections. The vulnerability is network-based and does not require any special privileges beyond the ability to establish an HTTP/2 connection to the server. [4]

Impact

A successful exploitation results in high CPU usage for several seconds. If a sufficient number of concurrent requests are made, the server can become completely unresponsive, effectively leading to a denial-of-service (DoS) condition. This can impact availability of web applications hosted on the affected Tomcat versions. No code execution or data breach is associated with this vulnerability. [4]

Mitigation

Apache Tomcat has released security updates to fix this issue. The vulnerability is fixed in Tomcat 8.5.56, 9.0.36, and 10.0.0-M6 or later. Users should upgrade to these patched versions. No workaround is provided for this vulnerability. [1][2][3]

AI Insight generated on May 21, 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:tomcatMaven
>= 10.0.0-M1, < 10.0.0-M510.0.0-M5
org.apache.tomcat:tomcatMaven
>= 9.0.0.M1, < 9.0.359.0.35
org.apache.tomcat:tomcatMaven
>= 8.5.0, < 8.5.558.5.55

Affected products

19

Patches

3
9434a44d3449

Fix BZ 64467. Improve performance of closing idle streams

https://github.com/apache/tomcatMark ThomasMay 22, 2020via ghsa
3 files changed · +36 9
  • java/org/apache/coyote/http2/Http2UpgradeHandler.java+5 5 modified
    @@ -1473,11 +1473,11 @@ public HeaderEmitter headersStart(int streamId, boolean headersEndStream)
         }
     
     
    -    private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
    -        for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
    -            Stream stream = getStream(i, false);
    -            if (stream != null) {
    -                stream.closeIfIdle();
    +    private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
    +        for (Entry<Integer,Stream> entry : streams.entrySet()) {
    +            if (entry.getKey().intValue() > maxActiveRemoteStreamId &&
    +                    entry.getKey().intValue() < newMaxActiveRemoteStreamId) {
    +                entry.getValue().closeIfIdle();
                 }
             }
             maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
    
  • test/org/apache/coyote/http2/TestHttp2Section_5_1.java+27 4 modified
    @@ -147,21 +147,44 @@ public void testClientSendOldStream() throws Exception {
     
         @Test
         public void testImplicitClose() throws Exception {
    +        doTestImplicitClose(5);
    +    }
    +
    +
    +    // https://bz.apache.org/bugzilla/show_bug.cgi?id=64467
    +    @Test
    +    public void testImplicitCloseLargeId() throws Exception {
    +        doTestImplicitClose(Integer.MAX_VALUE - 8);
    +    }
    +
    +
    +    private void doTestImplicitClose(int lastStreamId) throws Exception {
    +
    +        long startFirst = System.nanoTime();
             http2Connect();
    +        long durationFirst = System.nanoTime() - startFirst;
     
             sendPriority(3, 0, 16);
    -        sendPriority(5, 0, 16);
    +        sendPriority(lastStreamId, 0, 16);
     
    -        sendSimpleGetRequest(5);
    +        long startSecond = System.nanoTime();
    +        sendSimpleGetRequest(lastStreamId);
             readSimpleGetResponse();
    -        Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
    +        long durationSecond = System.nanoTime() - startSecond;
    +
    +        Assert.assertEquals(getSimpleResponseTrace(lastStreamId), output.getTrace());
             output.clearTrace();
     
    +        // Allow second request to take up to 5 times first request or up to 1 second - whichever is the larger - mainly
    +        // to allow for CI systems under load that can exhibit significant timing variation.
    +        Assert.assertTrue("First request took [" + durationFirst/1000000 + "ms], second request took [" +
    +                durationSecond/1000000 + "ms]", durationSecond < 1000000000 || durationSecond < durationFirst * 3);
    +
             // Should trigger an error since stream 3 should have been implicitly
             // closed.
             sendSimpleGetRequest(3);
     
    -        handleGoAwayResponse(5);
    +        handleGoAwayResponse(lastStreamId);
         }
     
     
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -71,6 +71,10 @@
           <update>
             Add support for ALPN on recent OpenJDK 8 releases. (remm)
           </update>
    +      <fix>
    +        <bug>64467</bug>: Improve performance of closing idle HTTP/2 streams.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    
c8acd2ab7371

Fix BZ 64467. Improve performance of closing idle streams

https://github.com/apache/tomcatMark ThomasMay 22, 2020via ghsa
3 files changed · +36 9
  • java/org/apache/coyote/http2/Http2UpgradeHandler.java+5 5 modified
    @@ -1582,11 +1582,11 @@ public HeaderEmitter headersStart(int streamId, boolean headersEndStream)
         }
     
     
    -    private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
    -        for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
    -            Stream stream = getStream(i, false);
    -            if (stream != null) {
    -                stream.closeIfIdle();
    +    private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
    +        for (Entry<Integer,Stream> entry : streams.entrySet()) {
    +            if (entry.getKey().intValue() > maxActiveRemoteStreamId &&
    +                    entry.getKey().intValue() < newMaxActiveRemoteStreamId) {
    +                entry.getValue().closeIfIdle();
                 }
             }
             maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
    
  • test/org/apache/coyote/http2/TestHttp2Section_5_1.java+27 4 modified
    @@ -152,21 +152,44 @@ public void testClientSendOldStream() throws Exception {
     
         @Test
         public void testImplicitClose() throws Exception {
    +        doTestImplicitClose(5);
    +    }
    +
    +
    +    // https://bz.apache.org/bugzilla/show_bug.cgi?id=64467
    +    @Test
    +    public void testImplicitCloseLargeId() throws Exception {
    +        doTestImplicitClose(Integer.MAX_VALUE - 8);
    +    }
    +
    +
    +    private void doTestImplicitClose(int lastStreamId) throws Exception {
    +
    +        long startFirst = System.nanoTime();
             http2Connect();
    +        long durationFirst = System.nanoTime() - startFirst;
     
             sendPriority(3, 0, 16);
    -        sendPriority(5, 0, 16);
    +        sendPriority(lastStreamId, 0, 16);
     
    -        sendSimpleGetRequest(5);
    +        long startSecond = System.nanoTime();
    +        sendSimpleGetRequest(lastStreamId);
             readSimpleGetResponse();
    -        Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
    +        long durationSecond = System.nanoTime() - startSecond;
    +
    +        Assert.assertEquals(getSimpleResponseTrace(lastStreamId), output.getTrace());
             output.clearTrace();
     
    +        // Allow second request to take up to 5 times first request or up to 1 second - whichever is the larger - mainly
    +        // to allow for CI systems under load that can exhibit significant timing variation.
    +        Assert.assertTrue("First request took [" + durationFirst/1000000 + "ms], second request took [" +
    +                durationSecond/1000000 + "ms]", durationSecond < 1000000000 || durationSecond < durationFirst * 3);
    +
             // Should trigger an error since stream 3 should have been implicitly
             // closed.
             sendSimpleGetRequest(3);
     
    -        handleGoAwayResponse(5);
    +        handleGoAwayResponse(lastStreamId);
         }
     
     
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -63,6 +63,10 @@
           <update>
             Add support for ALPN on recent OpenJDK 8 releases. (remm)
           </update>
    +      <fix>
    +        <bug>64467</bug>: Improve performance of closing idle HTTP/2 streams.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    
9a0231683a77

Fix BZ 64467. Improve performance of closing idle streams

https://github.com/apache/tomcatMark ThomasMay 22, 2020via ghsa
3 files changed · +36 9
  • java/org/apache/coyote/http2/Http2UpgradeHandler.java+5 5 modified
    @@ -1473,11 +1473,11 @@ public HeaderEmitter headersStart(int streamId, boolean headersEndStream)
         }
     
     
    -    private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
    -        for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
    -            Stream stream = getStream(i, false);
    -            if (stream != null) {
    -                stream.closeIfIdle();
    +    private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
    +        for (Entry<Integer,Stream> entry : streams.entrySet()) {
    +            if (entry.getKey().intValue() > maxActiveRemoteStreamId &&
    +                    entry.getKey().intValue() < newMaxActiveRemoteStreamId) {
    +                entry.getValue().closeIfIdle();
                 }
             }
             maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
    
  • test/org/apache/coyote/http2/TestHttp2Section_5_1.java+27 4 modified
    @@ -147,21 +147,44 @@ public void testClientSendOldStream() throws Exception {
     
         @Test
         public void testImplicitClose() throws Exception {
    +        doTestImplicitClose(5);
    +    }
    +
    +
    +    // https://bz.apache.org/bugzilla/show_bug.cgi?id=64467
    +    @Test
    +    public void testImplicitCloseLargeId() throws Exception {
    +        doTestImplicitClose(Integer.MAX_VALUE - 8);
    +    }
    +
    +
    +    private void doTestImplicitClose(int lastStreamId) throws Exception {
    +
    +        long startFirst = System.nanoTime();
             http2Connect();
    +        long durationFirst = System.nanoTime() - startFirst;
     
             sendPriority(3, 0, 16);
    -        sendPriority(5, 0, 16);
    +        sendPriority(lastStreamId, 0, 16);
     
    -        sendSimpleGetRequest(5);
    +        long startSecond = System.nanoTime();
    +        sendSimpleGetRequest(lastStreamId);
             readSimpleGetResponse();
    -        Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
    +        long durationSecond = System.nanoTime() - startSecond;
    +
    +        Assert.assertEquals(getSimpleResponseTrace(lastStreamId), output.getTrace());
             output.clearTrace();
     
    +        // Allow second request to take up to 5 times first request or up to 1 second - whichever is the larger - mainly
    +        // to allow for CI systems under load that can exhibit significant timing variation.
    +        Assert.assertTrue("First request took [" + durationFirst/1000000 + "ms], second request took [" +
    +                durationSecond/1000000 + "ms]", durationSecond < 1000000000 || durationSecond < durationFirst * 3);
    +
             // Should trigger an error since stream 3 should have been implicitly
             // closed.
             sendSimpleGetRequest(3);
     
    -        handleGoAwayResponse(5);
    +        handleGoAwayResponse(lastStreamId);
         }
     
     
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -71,6 +71,10 @@
           <update>
             Add support for ALPN on recent OpenJDK 8 releases. (remm)
           </update>
    +      <fix>
    +        <bug>64467</bug>: Improve performance of closing idle HTTP/2 streams.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="WebSocket">
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

49

News mentions

0

No linked articles in our index yet.