+Date: Thu, 5 Jun 2025 14:23:16 +0100
+Subject: [PATCH] More updates towards a Commons FileUpload 1.6.0 RC/release
+
+Origin: https://github.com/apache/tomcat/commit/97790a35a27d236fa053e660676c3f8196284d93
+
+---
+ .../util/http/fileupload/FileUploadBase.java | 38 +++++++++++++++-
+ .../util/http/fileupload/MultipartStream.java | 43 ++++++++++++++++---
+ .../fileupload/impl/FileItemIteratorImpl.java | 1 +
+ 3 files changed, 73 insertions(+), 9 deletions(-)
+
+diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
+index b9f5cfe..031ff99 100644
+--- a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
++++ b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
+@@ -89,6 +89,13 @@ public abstract class FileUploadBase {
+ * HTTP content type header for multiple uploads.
+ */
+ public static final String MULTIPART_MIXED = "multipart/mixed";
++
++ /**
++ * Default per part header size limit in bytes.
++ *
++ * @since FileUpload 1.6.0
++ */
++ public static final int DEFAULT_PART_HEADER_SIZE_MAX = 512;
+
+ // ----------------------------------------------------------- Data members
+
+@@ -110,6 +117,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.
+ */
+@@ -274,8 +286,8 @@ public abstract class FileUploadBase {
+ boolean successful = false;
+ try {
+ final FileItemIterator iter = getItemIterator(ctx);
+- final FileItemFactory fileItemFactory = Objects.requireNonNull(getFileItemFactory(),
+- "No FileItemFactory has been set.");
++ final FileItemFactory fileItemFactory = getFileItemFactory();
++ Objects.requireNonNull(fileItemFactory, "getFileItemFactory()");
+ final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE];
+ while (iter.hasNext()) {
+ if (items.size() == fileCountMax) {
+@@ -511,6 +523,17 @@ public abstract class FileUploadBase {
+ headers.addHeader(headerName, headerValue);
+ }
+
++ /**
++ * 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.
+ *
+@@ -520,6 +543,17 @@ public abstract class FileUploadBase {
+ return listener;
+ }
+
++ /**
++ * 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.
+ *
+diff --git a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
+index 0c76e66..3b2fda0 100644
+--- a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
++++ b/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;
+
+@@ -171,7 +172,10 @@ public class MultipartStream {
+ /**
+ * The maximum length of {@code header-part} that will be
+ * processed (10 kilobytes = 10240 bytes.).
++ *
++ * @deprecated Unused. Replaced by {@link #getPartHeaderSizeMax()}.
+ */
++ @Deprecated
+ public static final int HEADER_PART_SIZE_MAX = 10240;
+
+ /**
+@@ -264,6 +268,11 @@ public class MultipartStream {
+ */
+ private final ProgressNotifier notifier;
+
++ /**
++ * The maximum permitted size of the headers provided with a single part in bytes.
++ */
++ private int partHeaderSizeMax = FileUploadBase.DEFAULT_PART_HEADER_SIZE_MAX;
++
+ // ----------------------------------------------------------- Constructors
+
+ /**
+@@ -350,6 +359,17 @@ public class MultipartStream {
+ return headerEncoding;
+ }
+
++ /**
++ * 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;
++ }
++
+ /**
+ * Specifies the character encoding to be used when reading the headers of
+ * individual parts. When not specified, or {@code null}, the platform
+@@ -495,9 +515,6 @@ public class MultipartStream {
+ * trailing {@code CRLF} marker. Parsing is left to the
+ * application.
+ *
+- * TODO allow limiting maximum header size to
+- * protect against abuse.
+- *
+ * @return The {@code header-part} of the current encapsulation.
+ *
+ * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits.
+@@ -518,10 +535,11 @@ public class MultipartStream {
+ } catch (final 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++;
+@@ -595,6 +613,17 @@ public class MultipartStream {
+ return readBodyData(null);
+ }
+
++ /**
++ * 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;
++ }
++
+ /**
+ * Finds the beginning of the first {@code encapsulation}.
+ *
+diff --git a/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
+index 29e89f6..7c8d075 100644
+--- a/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
++++ b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
+@@ -198,6 +198,7 @@ public class FileItemIteratorImpl implements FileItemIterator {
+ 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 {
+--
+2.49.0
+
diff --git a/CVE-2025-49125.patch b/CVE-2025-49125.patch
new file mode 100644
index 0000000..b3d745e
--- /dev/null
+++ b/CVE-2025-49125.patch
@@ -0,0 +1,165 @@
+From 9418e3ff9f1f4c006b4661311ae9376c52d162b9 Mon Sep 17 00:00:00 2001
+From: Mark Thomas
+Date: Wed, 4 Jun 2025 11:28:24 +0100
+Subject: [PATCH] Expand checks for webAppMount
+
+Origin: https://github.com/apache/tomcat/commit/9418e3ff9f1f4c006b4661311ae9376c52d162b9
+
+---
+ .../webresources/AbstractArchiveResourceSet.java | 7 ++++---
+ .../webresources/AbstractResourceSet.java | 12 ++++++++++++
+ .../catalina/webresources/DirResourceSet.java | 10 +++++-----
+ .../webresources/AbstractTestResourceSet.java | 16 ++++++++++++++--
+ 5 files changed, 35 insertions(+), 10 deletions(-)
+
+diff --git a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
+index f78a45888ff9..cfcb490e35af 100644
+--- a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
++++ b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
+@@ -68,7 +68,7 @@ public final String[] list(String path) {
+ String webAppMount = getWebAppMount();
+
+ ArrayList result = new ArrayList<>();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ String pathInJar = getInternalPath() + path.substring(webAppMount.length());
+ // Always strip off the leading '/' to get the JAR path
+ if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
+@@ -108,13 +108,14 @@ public final String[] list(String path) {
+ return result.toArray(new String[0]);
+ }
+
++
+ @Override
+ public final Set listWebAppPaths(String path) {
+ checkPath(path);
+ String webAppMount = getWebAppMount();
+
+ ResourceSet result = new ResourceSet<>();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ String pathInJar = getInternalPath() + path.substring(webAppMount.length());
+ // Always strip off the leading '/' to get the JAR path and make
+ // sure it ends in '/'
+@@ -225,7 +226,7 @@ public final WebResource getResource(String path) {
+ // If the JAR has been mounted below the web application root, return
+ // an empty resource for requests outside of the mount point.
+
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ String pathInJar = getInternalPath() + path.substring(webAppMount.length());
+ // Always strip off the leading '/' to get the JAR path
+ if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
+diff --git a/java/org/apache/catalina/webresources/AbstractResourceSet.java b/java/org/apache/catalina/webresources/AbstractResourceSet.java
+index bd7d0c55e789..80961d738ae9 100644
+--- a/java/org/apache/catalina/webresources/AbstractResourceSet.java
++++ b/java/org/apache/catalina/webresources/AbstractResourceSet.java
+@@ -83,6 +83,18 @@ protected final String getWebAppMount() {
+ return webAppMount;
+ }
+
++ protected boolean isPathMounted(String path, String webAppMount) {
++ // Doesn't call getWebAppMount() as value might have changed
++ if (path.startsWith(webAppMount)) {
++ if (path.length() != webAppMount.length() && path.charAt(webAppMount.length()) != '/') {
++ return false;
++ }
++ return true;
++ }
++ return false;
++ }
++
++
+ public final void setBase(String base) {
+ this.base = base;
+ }
+diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java
+index 41b5b030df6d..ffb9782fc022 100644
+--- a/java/org/apache/catalina/webresources/DirResourceSet.java
++++ b/java/org/apache/catalina/webresources/DirResourceSet.java
+@@ -102,7 +102,7 @@ public WebResource getResource(String path) {
+ String webAppMount = getWebAppMount();
+ WebResourceRoot root = getRoot();
+ boolean readOnly = isReadOnly();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ /*
+ * Lock the path for reading until the WebResource has been constructed. The lock prevents concurrent reads
+ * and writes (e.g. HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource
+@@ -136,7 +136,7 @@ public WebResource getResource(String path) {
+ public String[] list(String path) {
+ checkPath(path);
+ String webAppMount = getWebAppMount();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ File f = file(path.substring(webAppMount.length()), true);
+ if (f == null) {
+ return EMPTY_STRING_ARRAY;
+@@ -168,7 +168,7 @@ public Set listWebAppPaths(String path) {
+ checkPath(path);
+ String webAppMount = getWebAppMount();
+ ResourceSet result = new ResourceSet<>();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ File f = file(path.substring(webAppMount.length()), true);
+ if (f != null) {
+ File[] list = f.listFiles();
+@@ -245,7 +245,7 @@ public boolean mkdir(String path) {
+ return false;
+ }
+ String webAppMount = getWebAppMount();
+- if (path.startsWith(webAppMount)) {
++ if (isPathMounted(path, webAppMount)) {
+ File f = file(path.substring(webAppMount.length()), false);
+ if (f == null) {
+ return false;
+@@ -275,7 +275,7 @@ public boolean write(String path, InputStream is, boolean overwrite) {
+ }
+
+ String webAppMount = getWebAppMount();
+- if (!path.startsWith(webAppMount)) {
++ if (!isPathMounted(path, webAppMount)) {
+ return false;
+ }
+
+diff --git a/test/org/apache/catalina/webresources/AbstractTestResourceSet.java b/test/org/apache/catalina/webresources/AbstractTestResourceSet.java
+index ce4c31b28c38..7e07ef1e263a 100644
+--- a/test/org/apache/catalina/webresources/AbstractTestResourceSet.java
++++ b/test/org/apache/catalina/webresources/AbstractTestResourceSet.java
+@@ -97,7 +97,7 @@ private void doTestGetResourceRoot(boolean slash) {
+ }
+
+ @Test
+- public final void testGetResourceDirA() {
++ public final void testGetResourceDirWithoutTrailingFileSeperator() {
+ WebResource webResource = resourceRoot.getResource(getMount() + "/d1");
+ Assert.assertTrue(webResource.isDirectory());
+ Assert.assertEquals("d1", webResource.getName());
+@@ -108,7 +108,7 @@ public final void testGetResourceDirA() {
+ }
+
+ @Test
+- public final void testGetResourceDirB() {
++ public final void testGetResourceDirWithTrailingFileSeperator() {
+ WebResource webResource = resourceRoot.getResource(getMount() + "/d1/");
+ Assert.assertTrue(webResource.isDirectory());
+ Assert.assertEquals("d1", webResource.getName());
+@@ -118,6 +118,18 @@ public final void testGetResourceDirB() {
+ Assert.assertNull(webResource.getInputStream());
+ }
+
++ @Test
++ public final void testGetResourceDirWithoutLeadingFileSeperator() {
++ String mount = getMount();
++ if (mount.isEmpty()) {
++ // Test is only meaningful when resource is mounted below web application root.
++ return;
++ }
++ WebResource webResource = resourceRoot.getResource(mount + "d1");
++ Assert.assertFalse(webResource.exists());
++ Assert.assertEquals(mount + "d1", webResource.getWebappPath());
++ }
++
+ @Test
+ public final void testGetResourceFile() {
+ WebResource webResource =
diff --git a/tomcat.spec b/tomcat.spec
index 2d9dce6..63af1f7 100644
--- a/tomcat.spec
+++ b/tomcat.spec
@@ -23,7 +23,7 @@
Name: tomcat
Epoch: 1
Version: %{major_version}.%{minor_version}.%{micro_version}
-Release: 3
+Release: 4
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
License: Apache-2.0
@@ -58,6 +58,9 @@ Patch11: CVE-2025-31651-1.patch
Patch12: CVE-2025-31651-2.patch
Patch13: CVE-2025-46701-1.patch
Patch14: CVE-2025-46701-2.patch
+Patch15: CVE-2025-48988.patch
+Patch16: CVE-2025-49125-pre.patch
+Patch17: CVE-2025-49125.patch
BuildArch: noarch
@@ -424,7 +427,10 @@ fi
%{appdir}/docs
%changelog
-* Wed Jun 04 2025 yaoxin <1024769339@qq.com> 1:9.0.100-3
+* Wed Jun 18 2025 wangkai <13474090681@163.com> - 1:9.0.100-4
+- Fix CVE-2025-48988, CVE-2025-49125
+
+* Wed Jun 04 2025 yaoxin <1024769339@qq.com> - 1:9.0.100-3
- Fix CVE-2025-46701
* Tue Apr 29 2025 wangkai <13474090681@163.com> - 1:9.0.100-2
--
Gitee