VYPR
Low severityNVD Advisory· Published Sep 28, 2022· Updated May 21, 2025

Apache Tomcat: Information disclosure

CVE-2021-43980

Description

The simplified implementation of blocking reads and writes introduced in Tomcat 10 and back-ported to Tomcat 9.0.47 onwards exposed a long standing (but extremely hard to trigger) concurrency bug in Apache Tomcat 10.1.0 to 10.1.0-M12, 10.0.0-M1 to 10.0.18, 9.0.0-M1 to 9.0.60 and 8.5.0 to 8.5.77 that could cause client connections to share an Http11Processor instance resulting in responses, or part responses, to be received by the wrong client.

AI Insight

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

A concurrency bug in Apache Tomcat's simplified blocking I/O can cause client connections to share an Http11Processor, leading to responses being sent to wrong clients.

Vulnerability

Overview

CVE-2021-43980 is a concurrency bug in Apache Tomcat that affects versions 10.1.0 to 10.1.0-M12, 10.0.0-M1 to 10.0.18, 9.0.0-M1 to 9.0.60, and 8.5.0 to 8.5.77. The vulnerability was exposed by a simplified implementation of blocking reads and writes introduced in Tomcat 10 and backported to Tomcat 9.0.47 onwards. This simplification revealed a long-standing but extremely hard to trigger concurrency flaw that could cause client connections to share an Http11Processor instance [4].

Exploitation

The bug is triggered under specific timing conditions during concurrent handling of blocking I/O operations. No authentication or special privileges are required, as the vulnerability exists in the core request processing loop. An attacker would need to be able to send HTTP requests to the server, and the attack would rely on precise timing to exploit the race condition that leads to processor sharing.

Impact

Successful exploitation can result in responses—or partial responses—being delivered to the wrong client connection. This could lead to information disclosure, as sensitive data from one user's session might be exposed to another user. The impact is limited to response misrouting; the attacker does not gain code execution or direct access to server resources.

Mitigation

Patches are available in Tomcat 10.1.1, 10.0.19, 9.0.61, and 8.5.78. Users should upgrade to these or later versions. No workaround is documented, and the vulnerability is not known to be exploited in the wild as of publication [1][3][4].

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
>= 8.5.0, < 8.5.788.5.78
org.apache.tomcat:tomcatMaven
>= 9.0.0-M1, < 9.0.629.0.62
org.apache.tomcat:tomcatMaven
>= 10.0.0-M1, < 10.0.2010.0.20
org.apache.tomcat:tomcatMaven
>= 10.1.0-M1, < 10.1.0-M1410.1.0-M14

Affected products

19

Patches

4
170e0f792bd1

Improve the recycling of Processor objects to make it more robust.

https://github.com/apache/tomcatMark ThomasMar 29, 2022via ghsa
3 files changed · +33 20
  • java/org/apache/coyote/AbstractProtocol.java+17 15 modified
    @@ -797,7 +797,11 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 S socket = wrapper.getSocket();
     
    -            Processor processor = (Processor) wrapper.getCurrentProcessor();
    +            // We take complete ownership of the Processor inside of this method to ensure
    +            // no other thread can release it while we're using it. Whatever processor is
    +            // held by this variable will be associated with the SocketWrapper before this
    +            // method returns.
    +            Processor processor = (Processor) wrapper.takeCurrentProcessor();
                 if (getLog().isDebugEnabled()) {
                     getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                             processor, socket));
    @@ -881,9 +885,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     processor.setSslSupport(
                             wrapper.getSslSupport(getProtocol().getClientCertProvider()));
     
    -                // Associate the processor with the connection
    -                wrapper.setCurrentProcessor(processor);
    -
                     SocketState state = SocketState.CLOSED;
                     do {
                         state = processor.process(wrapper, status);
    @@ -903,8 +904,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     release(processor);
                                     // Create the upgrade processor
                                     processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
    -                                // Associate with the processor with the connection
    -                                wrapper.setCurrentProcessor(processor);
                                 } else {
                                     if (getLog().isDebugEnabled()) {
                                         getLog().debug(sm.getString(
    @@ -924,8 +923,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                                             processor, wrapper));
                                 }
    -                            // Associate with the processor with the connection
    -                            wrapper.setCurrentProcessor(processor);
                                 // Initialise the upgrade handler (which may trigger
                                 // some IO using the new protocol which is why the lines
                                 // above are necessary)
    @@ -963,8 +960,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     } else if (state == SocketState.OPEN) {
                         // In keep-alive but between requests. OK to recycle
                         // processor. Continue to poll for the next request.
    -                    wrapper.setCurrentProcessor(null);
                         release(processor);
    +                    processor = null;
                         wrapper.registerReadInterest();
                     } else if (state == SocketState.SENDFILE) {
                         // Sendfile in progress. If it fails, the socket will be
    @@ -989,8 +986,7 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                         // Connection closed. OK to recycle the processor.
                         // Processors handling upgrades require additional clean-up
                         // before release.
    -                    wrapper.setCurrentProcessor(null);
    -                    if (processor.isUpgrade()) {
    +                    if (processor != null && processor.isUpgrade()) {
                             UpgradeToken upgradeToken = processor.getUpgradeToken();
                             HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                             InstanceManager instanceManager = upgradeToken.getInstanceManager();
    @@ -1011,7 +1007,13 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                 }
                             }
                         }
    +
                         release(processor);
    +                    processor = null;
    +                }
    +
    +                if (processor != null) {
    +                    wrapper.setCurrentProcessor(processor);
                     }
                     return state;
                 } catch(java.net.SocketException e) {
    @@ -1047,7 +1049,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 // Make sure socket/processor is removed from the list of current
                 // connections
    -            wrapper.setCurrentProcessor(null);
                 release(processor);
                 return SocketState.CLOSED;
             }
    @@ -1081,7 +1082,9 @@ public Set<S> getOpenSockets() {
     
             /**
              * Expected to be used by the handler once the processor is no longer
    -         * required.
    +         * required. Care must be taken to ensure that this method is only
    +         * called once per processor, after the request processing has
    +         * completed.
              *
              * @param processor Processor being released (that was associated with
              *                  the socket)
    @@ -1119,8 +1122,7 @@ private void release(Processor processor) {
              */
             @Override
             public void release(SocketWrapperBase<S> socketWrapper) {
    -            Processor processor = (Processor) socketWrapper.getCurrentProcessor();
    -            socketWrapper.setCurrentProcessor(null);
    +            Processor processor = (Processor) socketWrapper.takeCurrentProcessor();
                 release(processor);
             }
     
    
  • java/org/apache/tomcat/util/net/SocketWrapperBase.java+12 5 modified
    @@ -29,6 +29,7 @@
     import java.util.concurrent.Semaphore;
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicReference;
     
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
    @@ -106,10 +107,12 @@ public abstract class SocketWrapperBase<E> {
         protected volatile OperationState<?> writeOperation = null;
     
         /**
    -     * The org.apache.coyote.Processor instance currently associated
    -     * with the wrapper.
    +     * The org.apache.coyote.Processor instance currently associated with the
    +     * wrapper. Only populated when required to maintain wrapper<->Processor
    +     * mapping between calls to
    +     * {@link AbstractEndpoint.Handler#process(SocketWrapperBase, SocketEvent)}.
          */
    -    protected Object currentProcessor = null;
    +    private final AtomicReference<Object> currentProcessor = new AtomicReference<>();
     
         public SocketWrapperBase(E socket, AbstractEndpoint<E,?> endpoint) {
             this.socket = socket;
    @@ -136,11 +139,15 @@ protected AbstractEndpoint<E,?> getEndpoint() {
         }
     
         public Object getCurrentProcessor() {
    -        return currentProcessor;
    +        return currentProcessor.get();
         }
     
         public void setCurrentProcessor(Object currentProcessor) {
    -        this.currentProcessor = currentProcessor;
    +        this.currentProcessor.set(currentProcessor);
    +    }
    +
    +    public Object takeCurrentProcessor() {
    +        return currentProcessor.getAndSet(null);
         }
     
         /**
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -138,6 +138,10 @@
             with TLS 1.3 but the JSSE TLS 1.3 implementation does not support PHA.
             (markt)
           </add>
    +      <fix>
    +        Improve the recycling of Processor objects to make it more robust.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Jasper">
    
17f177eeb7df

Improve the recycling of Processor objects to make it more robust.

https://github.com/apache/tomcatMark ThomasMar 29, 2022via ghsa
3 files changed · +33 20
  • java/org/apache/coyote/AbstractProtocol.java+17 15 modified
    @@ -767,7 +767,11 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 S socket = wrapper.getSocket();
     
    -            Processor processor = (Processor) wrapper.getCurrentProcessor();
    +            // We take complete ownership of the Processor inside of this method to ensure
    +            // no other thread can release it while we're using it. Whatever processor is
    +            // held by this variable will be associated with the SocketWrapper before this
    +            // method returns.
    +            Processor processor = (Processor) wrapper.takeCurrentProcessor();
                 if (getLog().isDebugEnabled()) {
                     getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                             processor, socket));
    @@ -849,9 +853,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                     processor.setSslSupport(wrapper.getSslSupport());
     
    -                // Associate the processor with the connection
    -                wrapper.setCurrentProcessor(processor);
    -
                     SocketState state = SocketState.CLOSED;
                     do {
                         state = processor.process(wrapper, status);
    @@ -871,8 +872,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     release(processor);
                                     // Create the upgrade processor
                                     processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
    -                                // Associate with the processor with the connection
    -                                wrapper.setCurrentProcessor(processor);
                                 } else {
                                     if (getLog().isDebugEnabled()) {
                                         getLog().debug(sm.getString(
    @@ -892,8 +891,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                                             processor, wrapper));
                                 }
    -                            // Associate with the processor with the connection
    -                            wrapper.setCurrentProcessor(processor);
                                 // Initialise the upgrade handler (which may trigger
                                 // some IO using the new protocol which is why the lines
                                 // above are necessary)
    @@ -931,8 +928,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     } else if (state == SocketState.OPEN) {
                         // In keep-alive but between requests. OK to recycle
                         // processor. Continue to poll for the next request.
    -                    wrapper.setCurrentProcessor(null);
                         release(processor);
    +                    processor = null;
                         wrapper.registerReadInterest();
                     } else if (state == SocketState.SENDFILE) {
                         // Sendfile in progress. If it fails, the socket will be
    @@ -957,8 +954,7 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                         // Connection closed. OK to recycle the processor.
                         // Processors handling upgrades require additional clean-up
                         // before release.
    -                    wrapper.setCurrentProcessor(null);
    -                    if (processor.isUpgrade()) {
    +                    if (processor != null && processor.isUpgrade()) {
                             UpgradeToken upgradeToken = processor.getUpgradeToken();
                             HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                             InstanceManager instanceManager = upgradeToken.getInstanceManager();
    @@ -979,7 +975,13 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                 }
                             }
                         }
    +
                         release(processor);
    +                    processor = null;
    +                }
    +
    +                if (processor != null) {
    +                    wrapper.setCurrentProcessor(processor);
                     }
                     return state;
                 } catch(java.net.SocketException e) {
    @@ -1015,7 +1017,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 // Make sure socket/processor is removed from the list of current
                 // connections
    -            wrapper.setCurrentProcessor(null);
                 release(processor);
                 return SocketState.CLOSED;
             }
    @@ -1035,7 +1036,9 @@ protected void longPoll(SocketWrapperBase<?> socket, Processor processor) {
     
             /**
              * Expected to be used by the handler once the processor is no longer
    -         * required.
    +         * required. Care must be taken to ensure that this method is only
    +         * called once per processor, after the request processing has
    +         * completed.
              *
              * @param processor Processor being released (that was associated with
              *                  the socket)
    @@ -1073,8 +1076,7 @@ private void release(Processor processor) {
              */
             @Override
             public void release(SocketWrapperBase<S> socketWrapper) {
    -            Processor processor = (Processor) socketWrapper.getCurrentProcessor();
    -            socketWrapper.setCurrentProcessor(null);
    +            Processor processor = (Processor) socketWrapper.takeCurrentProcessor();
                 release(processor);
             }
     
    
  • java/org/apache/tomcat/util/net/SocketWrapperBase.java+12 5 modified
    @@ -30,6 +30,7 @@
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
     import java.util.concurrent.atomic.AtomicLong;
    +import java.util.concurrent.atomic.AtomicReference;
     
     import jakarta.servlet.ServletConnection;
     
    @@ -122,10 +123,12 @@ public abstract class SocketWrapperBase<E> {
         protected volatile OperationState<?> writeOperation = null;
     
         /**
    -     * The org.apache.coyote.Processor instance currently associated
    -     * with the wrapper.
    +     * The org.apache.coyote.Processor instance currently associated with the
    +     * wrapper. Only populated when required to maintain wrapper<->Processor
    +     * mapping between calls to
    +     * {@link AbstractEndpoint.Handler#process(SocketWrapperBase, SocketEvent)}.
          */
    -    protected Object currentProcessor = null;
    +    private final AtomicReference<Object> currentProcessor = new AtomicReference<>();
     
         public SocketWrapperBase(E socket, AbstractEndpoint<E,?> endpoint) {
             this.socket = socket;
    @@ -153,11 +156,15 @@ protected AbstractEndpoint<E,?> getEndpoint() {
         }
     
         public Object getCurrentProcessor() {
    -        return currentProcessor;
    +        return currentProcessor.get();
         }
     
         public void setCurrentProcessor(Object currentProcessor) {
    -        this.currentProcessor = currentProcessor;
    +        this.currentProcessor.set(currentProcessor);
    +    }
    +
    +    public Object takeCurrentProcessor() {
    +        return currentProcessor.getAndSet(null);
         }
     
         /**
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -143,6 +143,10 @@
             with TLS 1.3 but the JSSE TLS 1.3 implementation does not support PHA.
             (markt)
           </add>
    +      <fix>
    +        Improve the recycling of Processor objects to make it more robust.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Jasper">
    
9651b83a1d04

Improve the recycling of Processor objects to make it more robust.

https://github.com/apache/tomcatMark ThomasMar 29, 2022via ghsa
3 files changed · +33 20
  • java/org/apache/coyote/AbstractProtocol.java+17 15 modified
    @@ -774,7 +774,11 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 S socket = wrapper.getSocket();
     
    -            Processor processor = (Processor) wrapper.getCurrentProcessor();
    +            // We take complete ownership of the Processor inside of this method to ensure
    +            // no other thread can release it while we're using it. Whatever processor is
    +            // held by this variable will be associated with the SocketWrapper before this
    +            // method returns.
    +            Processor processor = (Processor) wrapper.takeCurrentProcessor();
                 if (getLog().isDebugEnabled()) {
                     getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                             processor, socket));
    @@ -858,9 +862,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     processor.setSslSupport(
                             wrapper.getSslSupport(getProtocol().getClientCertProvider()));
     
    -                // Associate the processor with the connection
    -                wrapper.setCurrentProcessor(processor);
    -
                     SocketState state = SocketState.CLOSED;
                     do {
                         state = processor.process(wrapper, status);
    @@ -880,8 +881,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     release(processor);
                                     // Create the upgrade processor
                                     processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
    -                                // Associate with the processor with the connection
    -                                wrapper.setCurrentProcessor(processor);
                                 } else {
                                     if (getLog().isDebugEnabled()) {
                                         getLog().debug(sm.getString(
    @@ -901,8 +900,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                                             processor, wrapper));
                                 }
    -                            // Associate with the processor with the connection
    -                            wrapper.setCurrentProcessor(processor);
                                 // Initialise the upgrade handler (which may trigger
                                 // some IO using the new protocol which is why the lines
                                 // above are necessary)
    @@ -940,8 +937,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     } else if (state == SocketState.OPEN) {
                         // In keep-alive but between requests. OK to recycle
                         // processor. Continue to poll for the next request.
    -                    wrapper.setCurrentProcessor(null);
                         release(processor);
    +                    processor = null;
                         wrapper.registerReadInterest();
                     } else if (state == SocketState.SENDFILE) {
                         // Sendfile in progress. If it fails, the socket will be
    @@ -966,8 +963,7 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                         // Connection closed. OK to recycle the processor.
                         // Processors handling upgrades require additional clean-up
                         // before release.
    -                    wrapper.setCurrentProcessor(null);
    -                    if (processor.isUpgrade()) {
    +                    if (processor != null && processor.isUpgrade()) {
                             UpgradeToken upgradeToken = processor.getUpgradeToken();
                             HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                             InstanceManager instanceManager = upgradeToken.getInstanceManager();
    @@ -988,7 +984,13 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                 }
                             }
                         }
    +
                         release(processor);
    +                    processor = null;
    +                }
    +
    +                if (processor != null) {
    +                    wrapper.setCurrentProcessor(processor);
                     }
                     return state;
                 } catch(java.net.SocketException e) {
    @@ -1024,7 +1026,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 // Make sure socket/processor is removed from the list of current
                 // connections
    -            wrapper.setCurrentProcessor(null);
                 release(processor);
                 return SocketState.CLOSED;
             }
    @@ -1044,7 +1045,9 @@ protected void longPoll(SocketWrapperBase<?> socket, Processor processor) {
     
             /**
              * Expected to be used by the handler once the processor is no longer
    -         * required.
    +         * required. Care must be taken to ensure that this method is only
    +         * called once per processor, after the request processing has
    +         * completed.
              *
              * @param processor Processor being released (that was associated with
              *                  the socket)
    @@ -1082,8 +1085,7 @@ private void release(Processor processor) {
              */
             @Override
             public void release(SocketWrapperBase<S> socketWrapper) {
    -            Processor processor = (Processor) socketWrapper.getCurrentProcessor();
    -            socketWrapper.setCurrentProcessor(null);
    +            Processor processor = (Processor) socketWrapper.takeCurrentProcessor();
                 release(processor);
             }
     
    
  • java/org/apache/tomcat/util/net/SocketWrapperBase.java+12 5 modified
    @@ -29,6 +29,7 @@
     import java.util.concurrent.Semaphore;
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicReference;
     
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
    @@ -104,10 +105,12 @@ public abstract class SocketWrapperBase<E> {
         protected volatile OperationState<?> writeOperation = null;
     
         /**
    -     * The org.apache.coyote.Processor instance currently associated
    -     * with the wrapper.
    +     * The org.apache.coyote.Processor instance currently associated with the
    +     * wrapper. Only populated when required to maintain wrapper<->Processor
    +     * mapping between calls to
    +     * {@link AbstractEndpoint.Handler#process(SocketWrapperBase, SocketEvent)}.
          */
    -    protected Object currentProcessor = null;
    +    private final AtomicReference<Object> currentProcessor = new AtomicReference<>();
     
         public SocketWrapperBase(E socket, AbstractEndpoint<E,?> endpoint) {
             this.socket = socket;
    @@ -134,11 +137,15 @@ protected AbstractEndpoint<E,?> getEndpoint() {
         }
     
         public Object getCurrentProcessor() {
    -        return currentProcessor;
    +        return currentProcessor.get();
         }
     
         public void setCurrentProcessor(Object currentProcessor) {
    -        this.currentProcessor = currentProcessor;
    +        this.currentProcessor.set(currentProcessor);
    +    }
    +
    +    public Object takeCurrentProcessor() {
    +        return currentProcessor.getAndSet(null);
         }
     
         /**
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -138,6 +138,10 @@
             with TLS 1.3 but the JSSE TLS 1.3 implementation does not support PHA.
             (markt)
           </add>
    +      <fix>
    +        Improve the recycling of Processor objects to make it more robust.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Jasper">
    
4a00b0c08905

Improve the recycling of Processor objects to make it more robust.

https://github.com/apache/tomcatMark ThomasMar 29, 2022via ghsa
3 files changed · +33 20
  • java/org/apache/coyote/AbstractProtocol.java+17 15 modified
    @@ -790,7 +790,11 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 S socket = wrapper.getSocket();
     
    -            Processor processor = (Processor) wrapper.getCurrentProcessor();
    +            // We take complete ownership of the Processor inside of this method to ensure
    +            // no other thread can release it while we're using it. Whatever processor is
    +            // held by this variable will be associated with the SocketWrapper before this
    +            // method returns.
    +            Processor processor = (Processor) wrapper.takeCurrentProcessor();
                 if (getLog().isDebugEnabled()) {
                     getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                             processor, socket));
    @@ -873,9 +877,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     processor.setSslSupport(
                             wrapper.getSslSupport(getProtocol().getClientCertProvider()));
     
    -                // Associate the processor with the connection
    -                wrapper.setCurrentProcessor(processor);
    -
                     SocketState state = SocketState.CLOSED;
                     do {
                         state = processor.process(wrapper, status);
    @@ -895,8 +896,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     release(processor);
                                     // Create the upgrade processor
                                     processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
    -                                // Associate with the processor with the connection
    -                                wrapper.setCurrentProcessor(processor);
                                 } else {
                                     if (getLog().isDebugEnabled()) {
                                         getLog().debug(sm.getString(
    @@ -916,8 +915,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                     getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                                             processor, wrapper));
                                 }
    -                            // Associate with the processor with the connection
    -                            wrapper.setCurrentProcessor(processor);
                                 // Initialise the upgrade handler (which may trigger
                                 // some IO using the new protocol which is why the lines
                                 // above are necessary)
    @@ -949,8 +946,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                     } else if (state == SocketState.OPEN) {
                         // In keep-alive but between requests. OK to recycle
                         // processor. Continue to poll for the next request.
    -                    wrapper.setCurrentProcessor(null);
                         release(processor);
    +                    processor = null;
                         wrapper.registerReadInterest();
                     } else if (state == SocketState.SENDFILE) {
                         // Sendfile in progress. If it fails, the socket will be
    @@ -975,8 +972,7 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                         // Connection closed. OK to recycle the processor.
                         // Processors handling upgrades require additional clean-up
                         // before release.
    -                    wrapper.setCurrentProcessor(null);
    -                    if (processor.isUpgrade()) {
    +                    if (processor != null && processor.isUpgrade()) {
                             UpgradeToken upgradeToken = processor.getUpgradeToken();
                             HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                             InstanceManager instanceManager = upgradeToken.getInstanceManager();
    @@ -997,7 +993,13 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
                                 }
                             }
                         }
    +
                         release(processor);
    +                    processor = null;
    +                }
    +
    +                if (processor != null) {
    +                    wrapper.setCurrentProcessor(processor);
                     }
                     return state;
                 } catch(java.net.SocketException e) {
    @@ -1033,7 +1035,6 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
     
                 // Make sure socket/processor is removed from the list of current
                 // connections
    -            wrapper.setCurrentProcessor(null);
                 release(processor);
                 return SocketState.CLOSED;
             }
    @@ -1067,7 +1068,9 @@ public Set<S> getOpenSockets() {
     
             /**
              * Expected to be used by the handler once the processor is no longer
    -         * required.
    +         * required. Care must be taken to ensure that this method is only
    +         * called once per processor, after the request processing has
    +         * completed.
              *
              * @param processor Processor being released (that was associated with
              *                  the socket)
    @@ -1105,8 +1108,7 @@ private void release(Processor processor) {
              */
             @Override
             public void release(SocketWrapperBase<S> socketWrapper) {
    -            Processor processor = (Processor) socketWrapper.getCurrentProcessor();
    -            socketWrapper.setCurrentProcessor(null);
    +            Processor processor = (Processor) socketWrapper.takeCurrentProcessor();
                 release(processor);
             }
     
    
  • java/org/apache/tomcat/util/net/SocketWrapperBase.java+12 5 modified
    @@ -29,6 +29,7 @@
     import java.util.concurrent.Semaphore;
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicReference;
     
     import org.apache.juli.logging.Log;
     import org.apache.juli.logging.LogFactory;
    @@ -106,10 +107,12 @@ public abstract class SocketWrapperBase<E> {
         protected volatile OperationState<?> writeOperation = null;
     
         /**
    -     * The org.apache.coyote.Processor instance currently associated
    -     * with the wrapper.
    +     * The org.apache.coyote.Processor instance currently associated with the
    +     * wrapper. Only populated when required to maintain wrapper<->Processor
    +     * mapping between calls to
    +     * {@link AbstractEndpoint.Handler#process(SocketWrapperBase, SocketEvent)}.
          */
    -    protected Object currentProcessor = null;
    +    private final AtomicReference<Object> currentProcessor = new AtomicReference<>();
     
         public SocketWrapperBase(E socket, AbstractEndpoint<E,?> endpoint) {
             this.socket = socket;
    @@ -136,11 +139,15 @@ public AbstractEndpoint<E,?> getEndpoint() {
         }
     
         public Object getCurrentProcessor() {
    -        return currentProcessor;
    +        return currentProcessor.get();
         }
     
         public void setCurrentProcessor(Object currentProcessor) {
    -        this.currentProcessor = currentProcessor;
    +        this.currentProcessor.set(currentProcessor);
    +    }
    +
    +    public Object takeCurrentProcessor() {
    +        return currentProcessor.getAndSet(null);
         }
     
         /**
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -138,6 +138,10 @@
             with TLS 1.3 but the JSSE TLS 1.3 implementation does not support PHA.
             (markt)
           </add>
    +      <fix>
    +        Improve the recycling of Processor objects to make it more robust.
    +        (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

13

News mentions

0

No linked articles in our index yet.