File tomcat-9.0.36-CVE-2025-48988.patch of Package tomcat.40071

From ee8042ffce4cb9324dfd79efda5984f37bbb6910 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 3 Jun 2025 16:05:53 +0100
Subject: [PATCH] Provide finder grained control of multi-part requests

This exposes two new configuration attributes on the Connector:
- maxPartCount
- maxPartHeaderSize
---
From 6d58522c79f85132506c69f6fbefbef51501c0a7 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 5 Jun 2025 14:23:16 +0100
Subject: [PATCH] More updates towards a Commons FileUpload 1.6.0 RC/release

---
Index: apache-tomcat-9.0.36-src/MERGE.txt
===================================================================
--- apache-tomcat-9.0.36-src.orig/MERGE.txt
+++ apache-tomcat-9.0.36-src/MERGE.txt
@@ -51,7 +51,7 @@ FileUpload
 Sub-tree:
 src/main/java/org/apache/commons/fileupload2
 The SHA1 ID for the most recent commit to be merged to Tomcat is:
-34eb241c051b02eca3b0b1b04f67b3b4e6c3a24d (2023-01-03)
+f1028401e3d59bd42aee9ab4d26995991db1aadc (2025-06-05)
 
 Note: Tomcat's copy of fileupload also includes classes copied manually from
       Commons IO.
Index: apache-tomcat-9.0.36-src/java/org/apache/catalina/connector/Connector.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/catalina/connector/Connector.java
+++ apache-tomcat-9.0.36-src/java/org/apache/catalina/connector/Connector.java
@@ -202,6 +202,10 @@ public class Connector extends Lifecycle
      */
     private int maxCookieCount = 200;
 
+    private int maxPartCount = 10;
+
+    private int maxPartHeaderSize = 512;
+
     /**
      * The maximum number of parameters (GET plus POST) which will be
      * automatically parsed by the container. 10000 by default. A value of less
@@ -455,6 +459,26 @@ public class Connector extends Lifecycle
     }
 
 
+    public int getMaxPartCount() {
+        return maxPartCount;
+    }
+
+
+    public void setMaxPartCount(int maxPartCount) {
+        this.maxPartCount = maxPartCount;
+    }
+
+
+    public int getMaxPartHeaderSize() {
+        return maxPartHeaderSize;
+    }
+
+
+    public void setMaxPartHeaderSize(int maxPartHeaderSize) {
+        this.maxPartHeaderSize = maxPartHeaderSize;
+    }
+
+
     /**
      * Set the maximum number of parameters (GET plus POST) that will be
      * automatically parsed by the container. A value of less than 0 means no
Index: apache-tomcat-9.0.36-src/java/org/apache/catalina/connector/Request.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/catalina/connector/Request.java
+++ apache-tomcat-9.0.36-src/java/org/apache/catalina/connector/Request.java
@@ -2864,13 +2864,29 @@ public class Request implements HttpServ
             upload.setFileItemFactory(factory);
             upload.setFileSizeMax(mce.getMaxFileSize());
             upload.setSizeMax(mce.getMaxRequestSize());
-            if (maxParameterCount > -1) {
-                // There is a limit. The limit for parts needs to be reduced by
-                // the number of parameters we have already parsed.
-                // Must be under the limit else parsing parameters would have
-                // triggered an exception.
-                upload.setFileCountMax(maxParameterCount - parameters.size());
+            upload.setPartHeaderSizeMax(connector.getMaxPartHeaderSize());
+            /*
+             * There are two independent limits on the number of parts.
+             *
+             * 1. The limit based on parameters. This is maxParameterCount less the number of parameters already processed.
+             *
+             * 2. The limit based on parts. This is maxPartCount.
+             *
+             * The lower of these two limits will be applied to this request.
+             *
+             * Note: Either of both limits may be set to -1 (unlimited).
+             */
+            int partLimit = maxParameterCount;
+            if (partLimit > -1) {
+                partLimit = partLimit - parameters.size();
             }
+            int maxPartCount = connector.getMaxPartCount();
+            if (maxPartCount > -1) {
+                if (partLimit < 0 || partLimit > maxPartCount) {
+                    partLimit = maxPartCount;
+                }
+            }
+            upload.setFileCountMax(partLimit);
 
             parts = new ArrayList<>();
             try {
Index: apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
+++ apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 
 import org.apache.tomcat.util.http.fileupload.impl.FileCountLimitExceededException;
 import org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl;
@@ -99,6 +100,13 @@ public abstract class FileUploadBase {
     public static final String FORM_DATA = "form-data";
 
     /**
+     * Default per part header size limit in bytes.
+     *
+     * @since FileUpload 1.6.0
+     */
+    public static final int DEFAULT_PART_HEADER_SIZE_MAX = 512;
+
+    /**
      * Content-disposition value for file attachment.
      */
     public static final String ATTACHMENT = "attachment";
@@ -139,6 +147,11 @@ public abstract class FileUploadBase {
     private long fileCountMax = -1;
 
     /**
+     * The maximum permitted size of the headers provided with a single part in bytes.
+     */
+    private int partHeaderSizeMax = DEFAULT_PART_HEADER_SIZE_MAX;
+
+    /**
      * The content encoding to use when reading part headers.
      */
     private String headerEncoding;
@@ -302,11 +315,9 @@ public abstract class FileUploadBase {
         boolean successful = false;
         try {
             FileItemIterator iter = getItemIterator(ctx);
-            FileItemFactory fac = getFileItemFactory();
+            final FileItemFactory fileItemFactory = getFileItemFactory();
+            Objects.requireNonNull(fileItemFactory, "getFileItemFactory()");
             final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE];
-            if (fac == null) {
-                throw new NullPointerException("No FileItemFactory has been set.");
-            }
             while (iter.hasNext()) {
                 if (items.size() == fileCountMax) {
                     // The next item will exceed the limit.
@@ -315,7 +326,7 @@ public abstract class FileUploadBase {
                 final FileItemStream item = iter.next();
                 // Don't use getName() here to prevent an InvalidFileNameException.
                 final String fileName = ((FileItemStreamImpl) item).getName();
-                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
+                FileItem fileItem = fileItemFactory.createItem(item.getFieldName(), item.getContentType(),
                                                    item.isFormField(), fileName);
                 items.add(fileItem);
                 try {
@@ -577,6 +588,17 @@ public abstract class FileUploadBase {
     }
 
     /**
+     * Obtain the per part size limit for headers.
+     *
+     * @return The maximum size of the headers for a single part in bytes.
+     *
+     * @since FileUpload 1.6.0
+     */
+    public int getPartHeaderSizeMax() {
+        return partHeaderSizeMax;
+    }
+
+    /**
      * Returns the progress listener.
      *
      * @return The progress listener, if any, or null.
@@ -586,6 +608,17 @@ public abstract class FileUploadBase {
     }
 
     /**
+     * Sets the per part size limit for headers.
+     *
+     * @param partHeaderSizeMax The maximum size of the headers in bytes.
+     *
+     * @since FileUpload 1.6.0
+     */
+    public void setPartHeaderSizeMax(final int partHeaderSizeMax) {
+        this.partHeaderSizeMax = partHeaderSizeMax;
+    }
+
+    /**
      * Sets the progress listener.
      *
      * @param pListener The progress listener, if any. Defaults to null.
Index: apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
+++ apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
@@ -23,6 +23,7 @@ import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
 import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
+import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
 import org.apache.tomcat.util.http.fileupload.util.Closeable;
 import org.apache.tomcat.util.http.fileupload.util.Streams;
 
@@ -172,7 +173,10 @@ public class MultipartStream {
     /**
      * The maximum length of <code>header-part</code> that will be
      * processed (10 kilobytes = 10240 bytes.).
+     *
+     * @deprecated Unused. Replaced by {@link #getPartHeaderSizeMax()}.
      */
+    @Deprecated
     public static final int HEADER_PART_SIZE_MAX = 10240;
 
     /**
@@ -268,6 +272,11 @@ public class MultipartStream {
     // ----------------------------------------------------------- Constructors
 
     /**
+     * The maximum permitted size of the headers provided with a single part in bytes.
+     */
+    private int partHeaderSizeMax = FileUploadBase.DEFAULT_PART_HEADER_SIZE_MAX;
+
+    /**
      * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
      *
      * <p> Note that the buffer must be at least big enough to contain the
@@ -496,9 +505,6 @@ public class MultipartStream {
      * trailing <code>CRLF</code> marker. Parsing is left to the
      * application.
      *
-     * <p><strong>TODO</strong> allow limiting maximum header size to
-     * protect against abuse.
-     *
      * @return The <code>header-part</code> of the current encapsulation.
      *
      * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits.
@@ -519,10 +525,11 @@ public class MultipartStream {
             } catch (IOException e) {
                 throw new MalformedStreamException("Stream ended unexpectedly");
             }
-            if (++size > HEADER_PART_SIZE_MAX) {
-                throw new MalformedStreamException(String.format(
-                        "Header section has more than %s bytes (maybe it is not properly terminated)",
-                        Integer.valueOf(HEADER_PART_SIZE_MAX)));
+            size++;
+            if (getPartHeaderSizeMax() != -1 && size > getPartHeaderSizeMax()) {
+                throw new FileUploadIOException(new SizeLimitExceededException(
+                        String.format("Header section has more than %s bytes (maybe it is not properly terminated)", Integer.valueOf(getPartHeaderSizeMax())),
+                                size, getPartHeaderSizeMax()));
             }
             if (b == HEADER_SEPARATOR[i]) {
                 i++;
@@ -573,6 +580,17 @@ public class MultipartStream {
     }
 
     /**
+     * Obtain the per part size limit for headers.
+     *
+     * @return The maximum size of the headers for a single part in bytes.
+     *
+     * @since 1.6.0
+     */
+    public int getPartHeaderSizeMax() {
+        return partHeaderSizeMax;
+    }
+
+    /**
      * Creates a new {@link ItemInputStream}.
      * @return A new instance of {@link ItemInputStream}.
      */
@@ -759,6 +777,17 @@ public class MultipartStream {
     }
 
     /**
+     * Sets the per part size limit for headers.
+     *
+     * @param partHeaderSizeMax The maximum size of the headers in bytes.
+     *
+     * @since 1.6.0
+     */
+    public void setPartHeaderSizeMax(final int partHeaderSizeMax) {
+        this.partHeaderSizeMax = partHeaderSizeMax;
+    }
+
+    /**
      * An {@link InputStream} for reading an items contents.
      */
     public class ItemInputStream extends InputStream implements Closeable {
Index: apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
+++ apache-tomcat-9.0.36-src/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
@@ -187,6 +187,7 @@ public class FileItemIteratorImpl implem
                     String.format("The boundary specified in the %s header is too long", FileUploadBase.CONTENT_TYPE), iae);
         }
         multiPartStream.setHeaderEncoding(charEncoding);
+        multiPartStream.setPartHeaderSizeMax(fileUploadBase.getPartHeaderSizeMax());
     }
 
     public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
Index: apache-tomcat-9.0.36-src/webapps/docs/changelog.xml
===================================================================
--- apache-tomcat-9.0.36-src.orig/webapps/docs/changelog.xml
+++ apache-tomcat-9.0.36-src/webapps/docs/changelog.xml
@@ -213,6 +213,13 @@
       <add>
         Add support for TLS 1.3 client initiated re-keying. (markt)
       </add>
+      <add>
+        Provide finer grained control of multi-part request processing via two
+        new attributes on the <code>Connector</code> element.
+        <code>maxPartCount</code> limits the total number of parts in a
+        multi-part request and <code>maxPartHeaderSize</code> limits the size of
+        the headers provided with each part. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
@@ -321,8 +328,8 @@
   <subsection name="Other">
     <changelog>
       <update>
-        Update the internal fork of Apache Commons FileUpload to 34eb241
-        (2023-01-03, 2.0-SNAPSHOT). (markt)
+        Update the internal fork of Apache Commons FileUpload to f102840
+        (2023-06-05, 1.x-SNAPSHOT). (markt)
       </update>
       <fix>
         Make counting of active HTTP/2 streams per connection more robust.
Index: apache-tomcat-9.0.36-src/webapps/docs/config/ajp.xml
===================================================================
--- apache-tomcat-9.0.36-src.orig/webapps/docs/config/ajp.xml
+++ apache-tomcat-9.0.36-src/webapps/docs/config/ajp.xml
@@ -147,6 +147,21 @@
       exceed the limit.</p>
     </attribute>
 
+    <attribute name="maxPartCount" required="false">
+      <p>The maximum total number of parts permitted in a request where the
+      content type is <code>multipart/form-data</code>. This limit is in
+      addition to <code>maxParameterCount</code>. Requests that exceed this
+      limit will be rejected. A value of less than 0 means no limit. If not
+      specified, a default of 10 is used.</p>
+    </attribute>
+
+    <attribute name="maxPartHeaderSize" required="false">
+      <p>The maximum number of header bytes permitted per part in a request
+      where the content type is <code>multipart/form-data</code>. Requests that
+      exceed this limit will be rejected. A value of less than 0 means no limit.
+      If not specified, a default of 512 is used.</p>
+    </attribute>
+
     <attribute name="maxPostSize" required="false">
       <p>The maximum size in bytes of the POST which will be handled by
       the container FORM URL parameter parsing. The limit can be disabled by
Index: apache-tomcat-9.0.36-src/webapps/docs/config/http.xml
===================================================================
--- apache-tomcat-9.0.36-src.orig/webapps/docs/config/http.xml
+++ apache-tomcat-9.0.36-src/webapps/docs/config/http.xml
@@ -148,6 +148,21 @@
       exceed the limit.</p>
     </attribute>
 
+    <attribute name="maxPartCount" required="false">
+      <p>The maximum total number of parts permitted in a request where the
+      content type is <code>multipart/form-data</code>. This limit is in
+      addition to <code>maxParameterCount</code>. Requests that exceed this
+      limit will be rejected. A value of less than 0 means no limit. If not
+      specified, a default of 10 is used.</p>
+    </attribute>
+
+    <attribute name="maxPartHeaderSize" required="false">
+      <p>The maximum number of header bytes permitted per part in a request
+      where the content type is <code>multipart/form-data</code>. Requests that
+      exceed this limit will be rejected. A value of less than 0 means no limit.
+      If not specified, a default of 512 is used.</p>
+    </attribute>
+
     <attribute name="maxPostSize" required="false">
       <p>The maximum size in bytes of the POST which will be handled by
       the container FORM URL parameter parsing. The limit can be disabled by
Index: apache-tomcat-9.0.36-src/webapps/docs/config/http2.xml
===================================================================
--- apache-tomcat-9.0.36-src.orig/webapps/docs/config/http2.xml
+++ apache-tomcat-9.0.36-src/webapps/docs/config/http2.xml
@@ -296,6 +296,8 @@
   <ul>
     <li>maxCookieCount</li>
     <li>maxParameterCount</li>
+    <li>maxPartCount</li>
+    <li>maxPartHeaderSize</li>
     <li>maxPostSize</li>
     <li>maxSavePostSize</li>
   </ul>
openSUSE Build Service is sponsored by