From 9ef1cc245994ab175acebacf39ea51f95779c1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E9=91=AB?= Date: Mon, 26 May 2025 17:37:28 +0800 Subject: [PATCH] fix cve-2024-13009 --- backport-fix-001-CVE-2024-13009.patch | 103 +++++++++++++++++ backport-fix-002-CVE-2024-13009.patch | 152 ++++++++++++++++++++++++++ jetty.spec | 13 ++- 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 backport-fix-001-CVE-2024-13009.patch create mode 100644 backport-fix-002-CVE-2024-13009.patch diff --git a/backport-fix-001-CVE-2024-13009.patch b/backport-fix-001-CVE-2024-13009.patch new file mode 100644 index 00000000..cf5aa3e7 --- /dev/null +++ b/backport-fix-001-CVE-2024-13009.patch @@ -0,0 +1,103 @@ +From dd2c253fce51827789edaada920a3f1b3485e643 Mon Sep 17 00:00:00 2001 +From: Greg Wilkins +Date: Thu, 28 Nov 2024 07:56:07 +1100 +Subject: [PATCH] Allow BadMessageException to propagate unwrapped through + HttpInput (#12571) + +Allow BadMessageException to propagate unwrapped through HttpInput. Whilst Jetty handling will correctly unwrap it, 3rd party handling may not. +--- + .../java/org/eclipse/jetty/server/HttpInput.java | 6 ++++++ + .../jetty/test/HttpInputInterceptorTest.java | 14 ++++++++------ + 2 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +index 4ab04aaa79b..cd94a4b6055 100644 +--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java ++++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +@@ -499,6 +499,10 @@ public class HttpInput extends ServletInputStream implements Runnable + { + return _interceptor.readFrom(content); + } ++ catch (RuntimeException | Error x) ++ { ++ throw x; ++ } + catch (Throwable x) + { + IOException failure = new IOException("Bad content", x); +@@ -1146,6 +1150,8 @@ public class HttpInput extends ServletInputStream implements Runnable + { + if (_error instanceof IOException) + throw (IOException)_error; ++ if (_error instanceof RuntimeException) ++ throw (RuntimeException)_error; + throw new IOException(_error); + } + +diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputInterceptorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputInterceptorTest.java +index 6c76cac81f6..628702adff3 100644 +--- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputInterceptorTest.java ++++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputInterceptorTest.java +@@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletResponse; + import org.eclipse.jetty.client.HttpClient; + import org.eclipse.jetty.client.api.ContentResponse; + import org.eclipse.jetty.client.util.BytesContentProvider; ++import org.eclipse.jetty.http.BadMessageException; + import org.eclipse.jetty.http.HttpMethod; + import org.eclipse.jetty.http.HttpStatus; + import org.eclipse.jetty.http.HttpTester; +@@ -63,9 +64,10 @@ import org.junit.jupiter.params.ParameterizedTest; + import org.junit.jupiter.params.provider.ValueSource; + + import static org.hamcrest.MatcherAssert.assertThat; ++import static org.hamcrest.Matchers.anyOf; ++import static org.hamcrest.Matchers.sameInstance; + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNotNull; +-import static org.junit.jupiter.api.Assertions.assertSame; + import static org.junit.jupiter.api.Assertions.assertThrows; + import static org.junit.jupiter.api.Assertions.assertTrue; + +@@ -110,10 +112,10 @@ public class HttpInputInterceptorTest + // Throw immediately from the interceptor. + jettyRequest.getHttpInput().addInterceptor(content -> + { +- throw new RuntimeException(); ++ throw new BadMessageException(); + }); + +- assertThrows(IOException.class, () -> IO.readBytes(request.getInputStream())); ++ assertThrows(BadMessageException.class, () -> IO.readBytes(request.getInputStream())); + serverLatch.countDown(); + response.setStatus(HttpStatus.NO_CONTENT_204); + } +@@ -159,7 +161,7 @@ public class HttpInputInterceptorTest + throw new RuntimeException(); + }); + +- assertThrows(IOException.class, () -> IO.readBytes(request.getInputStream())); ++ assertThrows(RuntimeException.class, () -> IO.readBytes(request.getInputStream())); + serverLatch.countDown(); + response.setStatus(HttpStatus.NO_CONTENT_204); + } +@@ -362,7 +364,7 @@ public class HttpInputInterceptorTest + // Now the interceptor should throw, but isReady() should not. + if (input.isReady()) + { +- assertThrows(IOException.class, () -> assertEquals(bytes[0], input.read())); ++ assertThrows(RuntimeException.class, () -> assertEquals(bytes[0], input.read())); + readFailureLatch.countDown(); + response.setStatus(HttpStatus.NO_CONTENT_204); + asyncContext.complete(); +@@ -431,7 +433,7 @@ public class HttpInputInterceptorTest + @Override + public void onError(Throwable error) + { +- assertSame(failure, error.getCause()); ++ assertThat(failure, anyOf(sameInstance(error), sameInstance(error.getCause()))); + response.setStatus(HttpStatus.NO_CONTENT_204); + asyncContext.complete(); + } +-- +2.43.5 + diff --git a/backport-fix-002-CVE-2024-13009.patch b/backport-fix-002-CVE-2024-13009.patch new file mode 100644 index 00000000..0f934f63 --- /dev/null +++ b/backport-fix-002-CVE-2024-13009.patch @@ -0,0 +1,152 @@ +From e3fa9466633db6bf36e0eb0d17e3de166c788ede Mon Sep 17 00:00:00 2001 +From: Greg Wilkins +Date: Thu, 19 Dec 2024 14:35:45 +1100 +Subject: [PATCH] Better handling of bad gzip content. (#12648) + +* Better handling of bad gzip content. + +Better handling of bad gzip content, with earlier throw rather than looping + +* Ensure content buffer is null and not reused when bad gzip is received. + +Ensure content buffer is null and not reused when bad gzip is received. + +Co-authored-by: Jan Bartel + +* archive jetty-home foor testing + +Signed-off-by: Olivier Lamy + +--------- + +Signed-off-by: Olivier Lamy +Co-authored-by: Jan Bartel +Co-authored-by: Olivier Lamy +--- + Jenkinsfile | 1 + + .../org/eclipse/jetty/server/HttpInput.java | 30 ++++--------------- + .../jetty/servlet/GzipHandlerTest.java | 30 +++++++++++++++++++ + 3 files changed, 37 insertions(+), 24 deletions(-) + +diff --git a/Jenkinsfile b/Jenkinsfile +index 700b1e6c7690..09f1e20ed6a0 100644 +--- a/Jenkinsfile ++++ b/Jenkinsfile +@@ -117,6 +117,7 @@ def mavenBuild(jdk, cmdline, mvnName) { + finally + { + junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml', allowEmptyResults: true ++ archiveArtifacts artifacts: ".repository/org/eclipse/jetty/jetty-home/**/jetty-home-*", allowEmptyArchive: true, onlyIfSuccessful: false + } + } + } +diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +index cd94a4b60552..b3c851313fe0 100644 +--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java ++++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +@@ -184,6 +184,8 @@ private Throwable fail(Content content, Throwable failure) + if (failure == null) + failure = new IOException("unconsumed input"); + content.failed(failure); ++ if (_content == content) ++ _content = null; + } + return failure; + } +@@ -377,7 +379,7 @@ public void asyncReadProduce() throws IOException + protected Content nextContent() throws IOException + { + Content content = nextNonSentinelContent(); +- if (content == null && !isFinished()) ++ if (content == null && !isFinished() && !isError()) + { + produceContent(); + content = nextNonSentinelContent(); +@@ -461,7 +463,7 @@ protected Content nextInterceptedContent() throws IOException + // Intercept the current content. + // The interceptor may be called several + // times for the same content. +- _intercepted = intercept(_content); ++ _intercepted = _interceptor.readFrom(_content); + + // If interception produced new content + if (_intercepted != null && _intercepted != _content) +@@ -493,28 +495,6 @@ protected Content nextInterceptedContent() throws IOException + return null; + } + +- private Content intercept(Content content) throws IOException +- { +- try +- { +- return _interceptor.readFrom(content); +- } +- catch (RuntimeException | Error x) +- { +- throw x; +- } +- catch (Throwable x) +- { +- IOException failure = new IOException("Bad content", x); +- content.failed(failure); +- HttpChannel channel = _channelState.getHttpChannel(); +- Response response = channel.getResponse(); +- if (response.isCommitted()) +- channel.abort(failure); +- throw failure; +- } +- } +- + private void consume(Content content) + { + if (!isError() && content instanceof EofContent) +@@ -614,6 +594,8 @@ public boolean addContent(Content content) + { + Throwable failure = isError() ? _state.getError() : new EOFException("Content after EOF"); + content.failed(failure); ++ if (_content == content) ++ _content = null; + return false; + } + else +diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +index 149c674121dc..fe4a5a12fca6 100644 +--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java ++++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +@@ -712,6 +712,36 @@ public void testGzipRequest() throws Exception + assertThat(response.getContent(), is(data)); + } + ++ @Test ++ public void testBadGzip() throws Exception ++ { ++ String data = "Hello Nice World! "; ++ for (int i = 0; i < 10; ++i) ++ { ++ data += data; ++ } ++ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ++ baos.write(data.getBytes(StandardCharsets.UTF_8)); ++ baos.close(); ++ byte[] bytes = baos.toByteArray(); ++ ++ // generated and parsed test ++ HttpTester.Request request = HttpTester.newRequest(); ++ HttpTester.Response response; ++ ++ request.setMethod("POST"); ++ request.setURI("/ctx/echo"); ++ request.setVersion("HTTP/1.1"); ++ request.setHeader("Host", "tester"); ++ request.setHeader("Content-Type", "text/plain"); ++ request.setHeader("Content-Encoding", "gzip"); ++ request.setContent(bytes); ++ ++ response = HttpTester.parseResponse(_connector.getResponse(request.generate())); ++ ++ assertThat(response.getStatus(), is(500)); ++ } ++ + @Test + public void testGzipRequestChunked() throws Exception + { diff --git a/jetty.spec b/jetty.spec index 73ed1389..a7273970 100644 --- a/jetty.spec +++ b/jetty.spec @@ -1,4 +1,4 @@ -%define anolis_release 2 +%define anolis_release 3 %bcond_without bootstrap @@ -72,6 +72,11 @@ Patch2: 0002-Port-to-servlet-api-4-5.patch #https://github.com/jetty/jetty.project/pull/11269/commits/59cd49c63de16481838ac0edfb05a4f9444365ee Patch3: 0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch +# https://github.com/jetty/jetty.project/commit/dd2c253fce51827789edaada920a3f1b3485e643 +Patch0004: backport-fix-001-CVE-2024-13009.patch +# https://github.com/jetty/jetty.project/commit/e3fa9466633db6bf36e0eb0d17e3de166c788ede +Patch0005: backport-fix-002-CVE-2024-13009.patch + %if %{with bootstrap} BuildRequires: javapackages-bootstrap %else @@ -599,6 +604,8 @@ License: (ASL 2.0 or EPL-1.0) and MIT %patch1 -p1 %patch2 -p1 +%patch4 -p1 +%patch5 -p1 find . -name "*.?ar" -exec rm {} \; find . -name "*.class" -exec rm {} \; @@ -982,8 +989,12 @@ exit 0 %license LICENSE NOTICE.txt LICENSE-MIT %changelog +* Mon May 26 2025 wenxin - 9.4.43-3 +- add patch to fix CVE-2024-13009 + * Fri Aug 09 2024 Kaiqiang Wang - 9.4.43-2 - fix CVE-2024-22201 + * Wed Jan 10 2024 mgb01105731 - 9.4.43-1 - update to version 9.4.43 -- Gitee