Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP3:GA
tomcat
tomcat-8.0-CVE-2023-46589.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tomcat-8.0-CVE-2023-46589.patch of Package tomcat
Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/InputBuffer.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/connector/InputBuffer.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/InputBuffer.java @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHa 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; @@ -326,6 +327,7 @@ public class InputBuffer extends Reader * * @throws IOException An underlying IOException occurred */ + @SuppressWarnings("deprecation") @Override public int realReadBytes(byte cbuf[], int off, int len) throws IOException { @@ -341,19 +343,35 @@ public class InputBuffer extends Reader state = BYTE_STATE; } - int result = coyoteRequest.doRead(bb); - - return result; - + try { + return coyoteRequest.doRead(bb); + } 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); + } } public int readByte() throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); return bb.substract(); } @@ -362,9 +380,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(); return bb.substract(b, off, len); } @@ -439,9 +455,7 @@ public class InputBuffer extends Reader public int read() throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); return cb.substract(); } @@ -451,9 +465,7 @@ public class InputBuffer extends Reader public int read(char[] cbuf) throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); return read(cbuf, 0, cbuf.length); } @@ -463,9 +475,7 @@ public class InputBuffer extends Reader public int read(char[] cbuf, int off, int len) throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); return cb.substract(cbuf, off, len); } @@ -475,10 +485,7 @@ public class InputBuffer extends Reader public long skip(long n) throws IOException { - - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); if (n < 0) { throw new IllegalArgumentException(); @@ -514,9 +521,8 @@ public class InputBuffer extends Reader public boolean ready() throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); + if (state == INITIAL_STATE) { state = CHAR_STATE; } @@ -534,9 +540,7 @@ public class InputBuffer extends Reader public void mark(int readAheadLimit) throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); if (cb.getLength() <= 0) { cb.setOffset(0); @@ -559,9 +563,7 @@ public class InputBuffer extends Reader public void reset() throws IOException { - if (closed) { - throw new IOException(sm.getString("inputBuffer.streamClosed")); - } + throwIfClosed(); if (state == CHAR_STATE) { if (markPos < 0) { @@ -623,4 +625,11 @@ 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; + } + } } Index: apache-tomcat-8.0.53-src/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java =================================================================== --- apache-tomcat-8.0.53-src.orig/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java +++ apache-tomcat-8.0.53-src/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java @@ -423,6 +423,82 @@ public class TestChunkedInputFilter exte } } + @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@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; Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/BadRequestException.java =================================================================== --- /dev/null +++ apache-tomcat-8.0.53-src/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); + } +} Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/ClientAbortException.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/connector/ClientAbortException.java +++ apache-tomcat-8.0.53-src/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 + * Wrap an BadRequestException 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; Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/core/ApplicationDispatcher.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/core/ApplicationDispatcher.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/core/ApplicationDispatcher.java @@ -40,7 +40,7 @@ import org.apache.catalina.Context; import org.apache.catalina.Globals; import org.apache.catalina.InstanceEvent; 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; @@ -718,7 +718,7 @@ final class ApplicationDispatcher implem // Servlet Service Method is called by the FilterChain support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT, servlet, request, response); - } catch (ClientAbortException e) { + } catch (BadRequestException e) { support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT, servlet, request, response); ioException = e; @@ -739,7 +739,7 @@ final class ApplicationDispatcher implem support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT, servlet, request, response); Throwable rootCause = StandardWrapper.getRootCause(e); - if (!(rootCause instanceof ClientAbortException)) { + if (!(rootCause instanceof BadRequestException)) { wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), rootCause); } Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/CoyoteAdapter.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/connector/CoyoteAdapter.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/CoyoteAdapter.java @@ -322,6 +322,10 @@ public class CoyoteAdapter implements Ad 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); @@ -344,6 +348,10 @@ public class CoyoteAdapter implements Ad 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); Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/OutputBuffer.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/connector/OutputBuffer.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/OutputBuffer.java @@ -393,6 +393,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); } } Index: apache-tomcat-8.0.53-src/java/org/apache/coyote/Request.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/coyote/Request.java +++ apache-tomcat-8.0.53-src/java/org/apache/coyote/Request.java @@ -142,6 +142,10 @@ public final class Request { private final RequestInfo reqProcessorMX=new RequestInfo(this); + /** + * Holds request body reading error exception. + */ + private Exception errorException = null; volatile ReadListener listener; @@ -480,6 +484,32 @@ public final class Request { return n; } + // -------------------- 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 -------------------- Index: apache-tomcat-8.0.53-src/java/org/apache/coyote/Response.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/coyote/Response.java +++ apache-tomcat-8.0.53-src/java/org/apache/coyote/Response.java @@ -21,6 +21,7 @@ import java.io.StringReader; import java.nio.charset.Charset; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.WriteListener; @@ -111,15 +112,46 @@ public final class Response { private long commitTime = -1; /** - * Holds request error exception. + * Holds response writing error exception. */ - Exception errorException = null; + private Exception errorException = null; /** * Has the charset been explicitly set. */ boolean charsetSet = false; + /** + * With the introduction of async processing and the possibility of + * non-container threads calling sendError() tracking the current error + * state and ensuring that the correct error page is called becomes more + * complicated. This state attribute helps by tracking the current error + * state and informing callers that attempt to change state if the change + * was successful or if another thread got there first. + * + * <pre> + * The state machine is very simple: + * + * 0 - NONE + * 1 - NOT_REPORTED + * 2 - REPORTED + * + * + * -->---->-- >NONE + * | | | + * | | | setError() + * ^ ^ | + * | | \|/ + * | |-<-NOT_REPORTED + * | | + * ^ | report() + * | | + * | \|/ + * |----<----REPORTED + * </pre> + */ + private final AtomicInteger errorState = new AtomicInteger(0); + Request req; // ------------------------------------------------------------- Properties @@ -256,7 +288,37 @@ public final class Response { public boolean isExceptionPresent() { - return ( errorException != null ); + return errorException != null; + } + + + /** + * Set the error flag. + * + * @return <code>false</code> if the error flag was already set + */ + public boolean setError() { + return errorState.compareAndSet(0, 1); + } + + + /** + * Error flag accessor. + * + * @return <code>true</code> if the response has encountered an error + */ + public boolean isError() { + return errorState.get() > 0; + } + + + public boolean isErrorReportRequired() { + return errorState.get() == 1; + } + + + public boolean setErrorReported() { + return errorState.compareAndSet(1, 2); } @@ -507,6 +569,7 @@ public final class Response { commited = false; commitTime = -1; errorException = null; + errorState.set(0); headers.clear(); // Servlet 3.1 non-blocking write listener listener = null; Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/Response.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/connector/Response.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/connector/Response.java @@ -34,7 +34,6 @@ import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.Vector; -import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletOutputStream; import javax.servlet.SessionTrackingMode; @@ -182,38 +181,6 @@ public class Response implements HttpSer private boolean isCharacterEncodingSet = false; /** - * With the introduction of async processing and the possibility of - * non-container threads calling sendError() tracking the current error - * state and ensuring that the correct error page is called becomes more - * complicated. This state attribute helps by tracking the current error - * state and informing callers that attempt to change state if the change - * was successful or if another thread got there first. - * - * <pre> - * The state machine is very simple: - * - * 0 - NONE - * 1 - NOT_REPORTED - * 2 - REPORTED - * - * - * -->---->-- >NONE - * | | | - * | | | setError() - * ^ ^ | - * | | \|/ - * | |-<-NOT_REPORTED - * | | - * ^ | report() - * | | - * | \|/ - * |----<----REPORTED - * </pre> - */ - private final AtomicInteger errorState = new AtomicInteger(0); - - - /** * Using output stream flag. */ protected boolean usingOutputStream = false; @@ -251,7 +218,6 @@ public class Response implements HttpSer usingWriter = false; appCommitted = false; included = false; - errorState.set(0); isCharacterEncodingSet = false; if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) { @@ -399,7 +365,7 @@ public class Response implements HttpSer * Set the error flag. */ public boolean setError() { - boolean result = errorState.compareAndSet(0, 1); + boolean result = getCoyoteResponse().setError(); if (result) { Wrapper wrapper = getRequest().getWrapper(); if (wrapper != null) { @@ -414,17 +380,17 @@ public class Response implements HttpSer * Error flag accessor. */ public boolean isError() { - return errorState.get() > 0; + return getCoyoteResponse().isError(); } public boolean isErrorReportRequired() { - return errorState.get() == 1; + return getCoyoteResponse().isErrorReportRequired(); } public boolean setErrorReported() { - return errorState.compareAndSet(1, 2); + return getCoyoteResponse().setErrorReported(); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor