[Git][java-team/tomcat9][buster] Fix CVE-2023-46589

Bastien Roucariès (@rouca) gitlab at salsa.debian.org
Sat Dec 16 19:00:01 GMT 2023



Bastien Roucariès pushed to branch buster at Debian Java Maintainers / tomcat9


Commits:
d158e4ec by Bastien Roucariès at 2023-12-16T15:41:59+00:00
Fix CVE-2023-46589

- - - - -


5 changed files:

- debian/changelog
- + debian/patches/0038-Improve-error-handling-if-non-blocking-IO-code-swall.patch
- + debian/patches/0039-1-2-CVE-2023-46589-Differentiate-request-cancellatio.patch
- + debian/patches/0040-2-2-CVE-2023-46589-Ensure-IOException-on-request-rea.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,15 @@
+tomcat9 (9.0.31-1~deb10u11) buster-security; urgency=high
+
+  * Team upload
+  * Fix CVE-2023-46589 (Closes: #1057082):
+    An Improper Input Validation vulnerability was found. Tomcat
+    did not correctly parse HTTP trailer headers. A trailer
+    header that exceeded the header size limit could cause Tomcat
+    to treat a single request as multiple requests leading
+    to the possibility of request smuggling when behind a reverse proxy.
+
+ -- Bastien Roucariès <rouca at debian.org>  Sat, 16 Dec 2023 14:25:15 +0000
+
 tomcat9 (9.0.31-1~deb10u10) buster-security; urgency=high
 
   * Team upload.


=====================================
debian/patches/0038-Improve-error-handling-if-non-blocking-IO-code-swall.patch
=====================================
@@ -0,0 +1,319 @@
+From: Mark Thomas <markt at apache.org>
+Date: Wed, 24 Feb 2021 18:43:22 +0000
+Subject: Improve error handling if non-blocking IO code swallows IOExceptions
+
+Tomcat previously expected IOExceptions in onWritePossible() and
+onDataAvailable() to not be swallowed. These additions allow correct
+error handling even if the application code swallows the exception.
+
+This is needed for fixing CVE-2023-46589
+
+origin: https://github.com/apache/tomcat/commit/3082475acb55e9b0aaa17600b498c30189e20eca
+---
+ .../apache/catalina/connector/CoyoteAdapter.java   |  8 +++
+ .../org/apache/catalina/connector/InputBuffer.java | 57 +++++++++-------------
+ .../apache/catalina/connector/OutputBuffer.java    |  1 +
+ java/org/apache/coyote/Request.java                | 33 +++++++++++++
+ java/org/apache/coyote/Response.java               | 11 +++--
+ webapps/docs/changelog.xml                         |  6 +++
+ 6 files changed, 76 insertions(+), 40 deletions(-)
+
+diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
+index 5955731..a332408 100644
+--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
++++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
+@@ -190,6 +190,10 @@ public class CoyoteAdapter implements Adapter {
+                                 readListener != null) {
+                             readListener.onAllDataRead();
+                         }
++                        // User code may have swallowed an IOException
++                        if (response.getCoyoteResponse().isExceptionPresent()) {
++                            throw response.getCoyoteResponse().getErrorException();
++                        }
+                     } catch (Throwable t) {
+                         ExceptionUtils.handleThrowable(t);
+                         writeListener.onError(t);
+@@ -212,6 +216,10 @@ public class CoyoteAdapter implements Adapter {
+                         if (request.isFinished() && req.sendAllDataReadEvent()) {
+                             readListener.onAllDataRead();
+                         }
++                        // User code may have swallowed an IOException
++                        if (request.getCoyoteRequest().isExceptionPresent()) {
++                            throw request.getCoyoteRequest().getErrorException();
++                        }
+                     } catch (Throwable t) {
+                         ExceptionUtils.handleThrowable(t);
+                         readListener.onError(t);
+diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java
+index 4da5b22..6c9c6d1 100644
+--- a/java/org/apache/catalina/connector/InputBuffer.java
++++ b/java/org/apache/catalina/connector/InputBuffer.java
+@@ -335,6 +335,7 @@ public class InputBuffer extends Reader
+         try {
+             return coyoteRequest.doRead(this);
+         } catch (IOException ioe) {
++            coyoteRequest.setErrorException(ioe);
+             // An IOException on a read is almost always due to
+             // the remote client aborting the request.
+             throw new ClientAbortException(ioe);
+@@ -343,9 +344,7 @@ public class InputBuffer extends Reader
+ 
+ 
+     public int readByte() throws IOException {
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (checkByteBufferEof()) {
+             return -1;
+@@ -355,9 +354,7 @@ public class InputBuffer extends Reader
+ 
+ 
+     public int read(byte[] b, int off, int len) throws IOException {
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (checkByteBufferEof()) {
+             return -1;
+@@ -380,9 +377,7 @@ public class InputBuffer extends Reader
+      * @throws IOException if an input or output exception has occurred
+      */
+     public int read(ByteBuffer to) throws IOException {
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (checkByteBufferEof()) {
+             return -1;
+@@ -436,10 +431,7 @@ public class InputBuffer extends Reader
+ 
+     @Override
+     public int read() throws IOException {
+-
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (checkCharBufferEof()) {
+             return -1;
+@@ -450,21 +442,14 @@ public class InputBuffer extends Reader
+ 
+     @Override
+     public int read(char[] cbuf) throws IOException {
+-
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
+-
++        throwIfClosed();
+         return read(cbuf, 0, cbuf.length);
+     }
+ 
+ 
+     @Override
+     public int read(char[] cbuf, int off, int len) throws IOException {
+-
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (checkCharBufferEof()) {
+             return -1;
+@@ -477,9 +462,7 @@ public class InputBuffer extends Reader
+ 
+     @Override
+     public long skip(long n) throws IOException {
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (n < 0) {
+             throw new IllegalArgumentException();
+@@ -505,9 +488,7 @@ public class InputBuffer extends Reader
+ 
+     @Override
+     public boolean ready() throws IOException {
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+         if (state == INITIAL_STATE) {
+             state = CHAR_STATE;
+         }
+@@ -524,9 +505,7 @@ public class InputBuffer extends Reader
+     @Override
+     public void mark(int readAheadLimit) throws IOException {
+ 
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (cb.remaining() <= 0) {
+             clear(cb);
+@@ -544,15 +523,15 @@ public class InputBuffer extends Reader
+     @Override
+     public void reset() throws IOException {
+ 
+-        if (closed) {
+-            throw new IOException(sm.getString("inputBuffer.streamClosed"));
+-        }
++        throwIfClosed();
+ 
+         if (state == CHAR_STATE) {
+             if (markPos < 0) {
+                 clear(cb);
+                 markPos = -1;
+-                throw new IOException();
++                IOException ioe = new IOException();
++                coyoteRequest.setErrorException(ioe);
++                throw ioe;
+             } else {
+                 cb.position(markPos);
+             }
+@@ -562,6 +541,14 @@ public class InputBuffer extends Reader
+     }
+ 
+ 
++    private void throwIfClosed() throws IOException {
++        if (closed) {
++            IOException ioe = new IOException(sm.getString("inputBuffer.streamClosed"));
++            coyoteRequest.setErrorException(ioe);
++            throw ioe;
++        }
++    }
++
+     public void checkConverter() throws IOException {
+         if (conv != null) {
+             return;
+diff --git a/java/org/apache/catalina/connector/OutputBuffer.java b/java/org/apache/catalina/connector/OutputBuffer.java
+index 5fa8fc2..2d5a6fe 100644
+--- a/java/org/apache/catalina/connector/OutputBuffer.java
++++ b/java/org/apache/catalina/connector/OutputBuffer.java
+@@ -348,6 +348,7 @@ public class OutputBuffer extends Writer {
+                 // An IOException on a write is almost always due to
+                 // the remote client aborting the request. Wrap this
+                 // so that it can be handled better by the error dispatcher.
++                coyoteResponse.setErrorException(e);
+                 throw new ClientAbortException(e);
+             }
+         }
+diff --git a/java/org/apache/coyote/Request.java b/java/org/apache/coyote/Request.java
+index 01b30bd..6d3cf34 100644
+--- a/java/org/apache/coyote/Request.java
++++ b/java/org/apache/coyote/Request.java
+@@ -160,6 +160,11 @@ public final class Request {
+ 
+     private boolean sendfile = true;
+ 
++    /**
++     * Holds request body reading error exception.
++     */
++    private Exception errorException = null;
++
+     volatile ReadListener listener;
+ 
+     public ReadListener getReadListener() {
+@@ -556,6 +561,34 @@ public final class Request {
+     }
+ 
+ 
++    // -------------------- Error tracking --------------------
++
++    /**
++     * Set the error Exception that occurred during the writing of the response
++     * processing.
++     *
++     * @param ex The exception that occurred
++     */
++    public void setErrorException(Exception ex) {
++        errorException = ex;
++    }
++
++
++    /**
++     * Get the Exception that occurred during the writing of the response.
++     *
++     * @return The exception that occurred
++     */
++    public Exception getErrorException() {
++        return errorException;
++    }
++
++
++    public boolean isExceptionPresent() {
++        return errorException != null;
++    }
++
++
+     // -------------------- debug --------------------
+ 
+     @Override
+diff --git a/java/org/apache/coyote/Response.java b/java/org/apache/coyote/Response.java
+index b9f22b7..c7943b4 100644
+--- a/java/org/apache/coyote/Response.java
++++ b/java/org/apache/coyote/Response.java
+@@ -125,9 +125,9 @@ public final class Response {
+     private long commitTime = -1;
+ 
+     /**
+-     * Holds request error exception.
++     * Holds response writing error exception.
+      */
+-    Exception errorException = null;
++    private Exception errorException = null;
+ 
+     /**
+      * With the introduction of async processing and the possibility of
+@@ -275,7 +275,8 @@ public final class Response {
+     // -----------------Error State --------------------
+ 
+     /**
+-     * Set the error Exception that occurred during request processing.
++     * Set the error Exception that occurred during the writing of the response
++     * processing.
+      *
+      * @param ex The exception that occurred
+      */
+@@ -285,7 +286,7 @@ public final class Response {
+ 
+ 
+     /**
+-     * Get the Exception that occurred during request processing.
++     * Get the Exception that occurred during the writing of the response.
+      *
+      * @return The exception that occurred
+      */
+@@ -295,7 +296,7 @@ public final class Response {
+ 
+ 
+     public boolean isExceptionPresent() {
+-        return ( errorException != null );
++        return errorException != null;
+     }
+ 
+ 
+diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
+index 9c446bd..b59407c 100644
+--- a/webapps/docs/changelog.xml
++++ b/webapps/docs/changelog.xml
+@@ -138,6 +138,12 @@
+       <add>
+         Improve validation of storage location when using FileStore. (markt)
+       </add>
++      <add>
++        Make the non-blocking I/O error handling more robust by handling the
++        case where the application code swallows an <code>IOException</code> in
++        <code>WriteListener.onWritePossible()</code> and
++        <code>ReadListener.onDataAvailable()</code>. (markt)
++      </add>
+     </changelog>
+   </subsection>
+   <subsection name="Coyote">


=====================================
debian/patches/0039-1-2-CVE-2023-46589-Differentiate-request-cancellatio.patch
=====================================
@@ -0,0 +1,211 @@
+From: Mark Thomas <markt at apache.org>
+Date: Wed, 8 Nov 2023 14:59:41 +0000
+Subject: [1/2] CVE-2023-46589: Differentiate request cancellation from a bad
+ request
+
+Introduce BadRequestException as a sub-class of IOException and the
+super class of ClientAbortException. This allows Tomcat to differentiate
+between an invalid request (e.g. invalid HTTP headers) and a request
+that the user cancelled before it completed.
+
+[debian] This patch is needed for fixing CVE-2023-46589
+
+origin: https://github.com/apache/tomcat/commit/030e7bb77bdb7b4f8fb7f065e3af9165da910ce3.patch
+bug-freexian-security: https://deb.freexian.com/extended-lts/tracker/CVE-2023-46589
+bug: https://www.openwall.com/lists/oss-security/2023/11/28/2
+bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1057082
+---
+ .../catalina/connector/BadRequestException.java    | 68 ++++++++++++++++++++++
+ .../catalina/connector/ClientAbortException.java   |  4 +-
+ .../org/apache/catalina/connector/InputBuffer.java |  6 +-
+ .../catalina/core/ApplicationDispatcher.java       | 10 ++--
+ .../apache/catalina/core/StandardWrapperValve.java | 12 ++--
+ 5 files changed, 83 insertions(+), 17 deletions(-)
+ create mode 100644 java/org/apache/catalina/connector/BadRequestException.java
+
+diff --git a/java/org/apache/catalina/connector/BadRequestException.java b/java/org/apache/catalina/connector/BadRequestException.java
+new file mode 100644
+index 0000000..71a792d
+--- /dev/null
++++ b/java/org/apache/catalina/connector/BadRequestException.java
+@@ -0,0 +1,68 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.catalina.connector;
++
++import java.io.IOException;
++
++/**
++ * Extend IOException to identify it as being caused by a bad request from a remote client.
++ */
++public class BadRequestException extends IOException {
++
++    private static final long serialVersionUID = 1L;
++
++
++    // ------------------------------------------------------------ Constructors
++
++    /**
++     * Construct a new BadRequestException with no other information.
++     */
++    public BadRequestException() {
++        super();
++    }
++
++
++    /**
++     * Construct a new BadRequestException for the specified message.
++     *
++     * @param message Message describing this exception
++     */
++    public BadRequestException(String message) {
++        super(message);
++    }
++
++
++    /**
++     * Construct a new BadRequestException for the specified throwable.
++     *
++     * @param throwable Throwable that caused this exception
++     */
++    public BadRequestException(Throwable throwable) {
++        super(throwable);
++    }
++
++
++    /**
++     * Construct a new BadRequestException for the specified message and throwable.
++     *
++     * @param message   Message describing this exception
++     * @param throwable Throwable that caused this exception
++     */
++    public BadRequestException(String message, Throwable throwable) {
++        super(message, throwable);
++    }
++}
+diff --git a/java/org/apache/catalina/connector/ClientAbortException.java b/java/org/apache/catalina/connector/ClientAbortException.java
+index c02a208..05c62ae 100644
+--- a/java/org/apache/catalina/connector/ClientAbortException.java
++++ b/java/org/apache/catalina/connector/ClientAbortException.java
+@@ -16,15 +16,13 @@
+  */
+ package org.apache.catalina.connector;
+ 
+-import java.io.IOException;
+-
+ /**
+  * Wrap an IOException identifying it as being caused by an abort
+  * of a request by a remote client.
+  *
+  * @author Glenn L. Nielsen
+  */
+-public final class ClientAbortException extends IOException {
++public final class ClientAbortException extends BadRequestException {
+ 
+     private static final long serialVersionUID = 1L;
+ 
+diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java
+index 6c9c6d1..dba4e4e 100644
+--- a/java/org/apache/catalina/connector/InputBuffer.java
++++ b/java/org/apache/catalina/connector/InputBuffer.java
+@@ -334,10 +334,12 @@ public class InputBuffer extends Reader
+ 
+         try {
+             return coyoteRequest.doRead(this);
++        } catch (BadRequestException bre) {
++            coyoteRequest.setErrorException(bre);
++            throw bre;
+         } catch (IOException ioe) {
+             coyoteRequest.setErrorException(ioe);
+-            // An IOException on a read is almost always due to
+-            // the remote client aborting the request.
++            // Any other IOException on a read is almost always due to the remote client aborting the request.
+             throw new ClientAbortException(ioe);
+         }
+     }
+diff --git a/java/org/apache/catalina/core/ApplicationDispatcher.java b/java/org/apache/catalina/core/ApplicationDispatcher.java
+index 522e4a7..de4ee54 100644
+--- a/java/org/apache/catalina/core/ApplicationDispatcher.java
++++ b/java/org/apache/catalina/core/ApplicationDispatcher.java
+@@ -41,7 +41,7 @@ import org.apache.catalina.AsyncDispatcher;
+ import org.apache.catalina.Context;
+ import org.apache.catalina.Globals;
+ import org.apache.catalina.Wrapper;
+-import org.apache.catalina.connector.ClientAbortException;
++import org.apache.catalina.connector.BadRequestException;
+ import org.apache.catalina.connector.Request;
+ import org.apache.catalina.connector.RequestFacade;
+ import org.apache.catalina.connector.Response;
+@@ -712,7 +712,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
+                filterChain.doFilter(request, response);
+              }
+             // Servlet Service Method is called by the FilterChain
+-        } catch (ClientAbortException e) {
++        } catch (BadRequestException e) {
+             ioException = e;
+         } catch (IOException e) {
+             wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+@@ -725,9 +725,9 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
+             wrapper.unavailable(e);
+         } catch (ServletException e) {
+             Throwable rootCause = StandardWrapper.getRootCause(e);
+-            if (!(rootCause instanceof ClientAbortException)) {
+-                wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
+-                        wrapper.getName()), rootCause);
++            if (!(rootCause instanceof BadRequestException)) {
++                wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()),
++                        rootCause);
+             }
+             servletException = e;
+         } catch (RuntimeException e) {
+diff --git a/java/org/apache/catalina/core/StandardWrapperValve.java b/java/org/apache/catalina/core/StandardWrapperValve.java
+index 8d2f54b..ce9a548 100644
+--- a/java/org/apache/catalina/core/StandardWrapperValve.java
++++ b/java/org/apache/catalina/core/StandardWrapperValve.java
+@@ -33,7 +33,7 @@ import org.apache.catalina.Container;
+ import org.apache.catalina.Context;
+ import org.apache.catalina.Globals;
+ import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.connector.ClientAbortException;
++import org.apache.catalina.connector.BadRequestException;
+ import org.apache.catalina.connector.Request;
+ import org.apache.catalina.connector.Response;
+ import org.apache.catalina.valves.ValveBase;
+@@ -204,7 +204,7 @@ final class StandardWrapperValve
+                 }
+ 
+             }
+-        } catch (ClientAbortException | CloseNowException e) {
++        } catch (BadRequestException | CloseNowException e) {
+             if (container.getLogger().isDebugEnabled()) {
+                 container.getLogger().debug(sm.getString(
+                         "standardWrapper.serviceException", wrapper.getName(),
+@@ -240,11 +240,9 @@ final class StandardWrapperValve
+             // do not want to do exception(request, response, e) processing
+         } catch (ServletException e) {
+             Throwable rootCause = StandardWrapper.getRootCause(e);
+-            if (!(rootCause instanceof ClientAbortException)) {
+-                container.getLogger().error(sm.getString(
+-                        "standardWrapper.serviceExceptionRoot",
+-                        wrapper.getName(), context.getName(), e.getMessage()),
+-                        rootCause);
++            if (!(rootCause instanceof BadRequestException)) {
++                container.getLogger().error(sm.getString("standardWrapper.serviceExceptionRoot", wrapper.getName(),
++                        context.getName(), e.getMessage()), rootCause);
+             }
+             throwable = e;
+             exception(request, response, e);


=====================================
debian/patches/0040-2-2-CVE-2023-46589-Ensure-IOException-on-request-rea.patch
=====================================
@@ -0,0 +1,169 @@
+From: Mark Thomas <markt at apache.org>
+Date: Wed, 8 Nov 2023 15:25:17 +0000
+Subject: [2/2] CVE-2023-46589 Ensure IOException on request read always
+ triggers error
+
+Improper Input Validation vulnerability in Apache Tomcat.
+Tomcat did not correctly parse HTTP trailer headers.
+
+A trailer header that exceeded the header size limit could cause Tomcat
+to treat a single request as multiple requests leading to the possibility of request smuggling when behind a reverse proxy
+
+origin: https://github.com/apache/tomcat/commit/7a2d8818fcea0b51747a67af9510ce7977245ebd.patch
+bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1057082
+bug-freexian-security: https://deb.freexian.com/extended-lts/tracker/CVE-2023-46589
+bug: https://www.openwall.com/lists/oss-security/2023/11/28/2
+---
+ .../org/apache/catalina/connector/InputBuffer.java | 14 ++++
+ .../http11/filters/TestChunkedInputFilter.java     | 77 ++++++++++++++++++++++
+ webapps/docs/changelog.xml                         |  5 ++
+ 3 files changed, 96 insertions(+)
+
+diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java
+index dba4e4e..d320696 100644
+--- a/java/org/apache/catalina/connector/InputBuffer.java
++++ b/java/org/apache/catalina/connector/InputBuffer.java
+@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
+ import java.util.concurrent.atomic.AtomicBoolean;
+ 
+ import javax.servlet.ReadListener;
++import javax.servlet.RequestDispatcher;
+ 
+ import org.apache.catalina.security.SecurityUtil;
+ import org.apache.coyote.ActionCode;
+@@ -319,6 +320,7 @@ public class InputBuffer extends Reader
+      *
+      * @throws IOException An underlying IOException occurred
+      */
++    @SuppressWarnings("deprecation")
+     @Override
+     public int realReadBytes() throws IOException {
+         if (closed) {
+@@ -335,11 +337,23 @@ public class InputBuffer extends Reader
+         try {
+             return coyoteRequest.doRead(this);
+         } catch (BadRequestException bre) {
++            // Set flag used by asynchronous processing to detect errors on non-container threads
+             coyoteRequest.setErrorException(bre);
++            // In synchronous processing, this exception may be swallowed by the application so set error flags here.
++            coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, bre);
++            coyoteRequest.getResponse().setStatus(400);
++            coyoteRequest.getResponse().setError();
++            // Make the exception visible to the application
+             throw bre;
+         } catch (IOException ioe) {
++            // Set flag used by asynchronous processing to detect errors on non-container threads
+             coyoteRequest.setErrorException(ioe);
++            // In synchronous processing, this exception may be swallowed by the application so set error flags here.
++            coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
++            coyoteRequest.getResponse().setStatus(400);
++            coyoteRequest.getResponse().setError();
+             // Any other IOException on a read is almost always due to the remote client aborting the request.
++            // Make the exception visible to the application
+             throw new ClientAbortException(ioe);
+         }
+     }
+diff --git a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
+index 16a5894..4bb896a 100644
+--- a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
++++ b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java
+@@ -429,6 +429,83 @@ public class TestChunkedInputFilter extends TomcatBaseTest {
+         }
+     }
+ 
++
++    @Test
++    public void testTrailerHeaderNameNotTokenThrowException() throws Exception {
++        doTestTrailerHeaderNameNotToken(false);
++    }
++
++    @Test
++    public void testTrailerHeaderNameNotTokenSwallowException() throws Exception {
++        doTestTrailerHeaderNameNotToken(true);
++    }
++
++    private void doTestTrailerHeaderNameNotToken(boolean swallowException) throws Exception {
++
++        // Setup Tomcat instance
++        Tomcat tomcat = getTomcatInstance();
++
++        // No file system docBase required
++        Context ctx = tomcat.addContext("", null);
++
++        Tomcat.addServlet(ctx, "servlet", new SwallowBodyServlet(swallowException));
++        ctx.addServletMappingDecoded("/", "servlet");
++
++        tomcat.start();
++
++        String[] request = new String[]{
++            "POST / HTTP/1.1" + SimpleHttpClient.CRLF +
++            "Host: localhost" + SimpleHttpClient.CRLF +
++            "Transfer-encoding: chunked" + SimpleHttpClient.CRLF +
++            "Content-Type: application/x-www-form-urlencoded" + SimpleHttpClient.CRLF +
++            "Connection: close" + SimpleHttpClient.CRLF +
++            SimpleHttpClient.CRLF +
++            "3" + SimpleHttpClient.CRLF +
++            "a=0" + SimpleHttpClient.CRLF +
++            "4" + SimpleHttpClient.CRLF +
++            "&b=1" + SimpleHttpClient.CRLF +
++            "0" + SimpleHttpClient.CRLF +
++            "x at trailer: Test" + SimpleHttpClient.CRLF +
++            SimpleHttpClient.CRLF };
++
++        TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort());
++        client.setRequest(request);
++
++        client.connect();
++        client.processRequest();
++        // Expected to fail because of invalid trailer header name
++        Assert.assertTrue(client.getResponseLine(), client.isResponse400());
++    }
++
++    private static class SwallowBodyServlet extends HttpServlet {
++        private static final long serialVersionUID = 1L;
++
++        private final boolean swallowException;
++
++        SwallowBodyServlet(boolean swallowException) {
++            this.swallowException = swallowException;
++        }
++
++        @Override
++        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
++                throws ServletException, IOException {
++            resp.setContentType("text/plain");
++            PrintWriter pw = resp.getWriter();
++
++            // Read the body
++            InputStream is = req.getInputStream();
++            try {
++                while (is.read() > -1) {
++                }
++                pw.write("OK");
++            } catch (IOException ioe) {
++                if (!swallowException) {
++                    throw ioe;
++                }
++            }
++        }
++    }
++
+     private static class EchoHeaderServlet extends HttpServlet {
+         private static final long serialVersionUID = 1L;
+ 
+diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
+index b59407c..86ae2a4 100644
+--- a/webapps/docs/changelog.xml
++++ b/webapps/docs/changelog.xml
+@@ -327,6 +327,11 @@
+         the authenticated Principal is not cached in the session when caching is
+         disabled. This is the fix for CVE-2019-17563. (markt/kkolinko)
+       </fix>
++      <fix>
++        Ensure that an <code>IOException</code> during the reading of the
++        request triggers always error handling, regardless of whether the
++        application swallows the exception. (markt)
++      </fix>
+     </changelog>
+   </subsection>
+   <subsection name="Coyote">


=====================================
debian/patches/series
=====================================
@@ -35,3 +35,6 @@ CVE-2023-41080.patch
 CVE-2023-42795.patch
 CVE-2023-44487.patch
 CVE-2023-45648.patch
+0038-Improve-error-handling-if-non-blocking-IO-code-swall.patch
+0039-1-2-CVE-2023-46589-Differentiate-request-cancellatio.patch
+0040-2-2-CVE-2023-46589-Ensure-IOException-on-request-rea.patch



View it on GitLab: https://salsa.debian.org/java-team/tomcat9/-/commit/d158e4ecd585f3415b344ccd4ad4460cea84fcb0

-- 
View it on GitLab: https://salsa.debian.org/java-team/tomcat9/-/commit/d158e4ecd585f3415b344ccd4ad4460cea84fcb0
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20231216/9a991db6/attachment.htm>


More information about the pkg-java-commits mailing list