[Git][java-team/netty][master] 3 commits: Add CVE-2024-29025.patch.
Markus Koschany (@apo)
gitlab at salsa.debian.org
Sun May 12 21:14:48 BST 2024
Markus Koschany pushed to branch master at Debian Java Maintainers / netty
Commits:
78b3d244 by Markus Koschany at 2024-05-12T21:20:07+02:00
Add CVE-2024-29025.patch.
- - - - -
d802cfb3 by Markus Koschany at 2024-05-12T21:22:56+02:00
Update changelog
- - - - -
93db9f23 by Markus Koschany at 2024-05-12T22:02:52+02:00
Update changelog. Release.
- - - - -
3 changed files:
- debian/changelog
- + debian/patches/CVE-2024-29025.patch
- debian/patches/series
Changes:
=====================================
debian/changelog
=====================================
@@ -1,3 +1,15 @@
+netty (1:4.1.48-10) unstable; urgency=high
+
+ * Team upload.
+ * Fix CVE-2024-29025:
+ Julien Viet discovered that Netty, a Java NIO client/server socket
+ framework, was vulnerable to allocation of resources without limits or
+ throttling due to the accumulation of data in the HttpPostRequestDecoder.
+ This would allow an attacker to cause a denial of service.
+ Thanks to Salvatore Bonaccorso for the report. (Closes: #1068110)
+
+ -- Markus Koschany <apo at debian.org> Sun, 12 May 2024 21:20:10 +0200
+
netty (1:4.1.48-9) unstable; urgency=medium
* Team upload.
=====================================
debian/patches/CVE-2024-29025.patch
=====================================
@@ -0,0 +1,400 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 12 May 2024 21:17:23 +0200
+Subject: CVE-2024-29025
+
+Upstream-Advisory: https://github.com/netty/netty/security/advisories/GHSA-5jpm-x58v-624v
+Origin: https://github.com/netty/netty/commit/0d0c6ed782d13d423586ad0c71737b2c7d02058c
+---
+ .../multipart/HttpPostMultipartRequestDecoder.java | 41 ++++++++
+ .../http/multipart/HttpPostRequestDecoder.java | 69 ++++++++++++++
+ .../multipart/HttpPostStandardRequestDecoder.java | 44 +++++++++
+ .../http/multipart/HttpPostRequestDecoderTest.java | 103 +++++++++++++++++++++
+ 4 files changed, 257 insertions(+)
+
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java
+index 4d59e4d..8a7d679 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java
+@@ -62,6 +62,16 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
+ */
+ private final HttpRequest request;
+
++ /**
++ * The maximum number of fields allows by the form
++ */
++ private final int maxFields;
++
++ /**
++ * The maximum number of accumulated bytes when decoding a field
++ */
++ private final int maxBufferedBytes;
++
+ /**
+ * Default charset to use
+ */
+@@ -173,9 +183,34 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
+ * errors
+ */
+ public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) {
++ this(factory, request, charset, HttpPostRequestDecoder.DEFAULT_MAX_FIELDS, HttpPostRequestDecoder.DEFAULT_MAX_BUFFERED_BYTES);
++ }
++
++ /**
++ *
++ * @param factory
++ * the factory used to create InterfaceHttpData
++ * @param request
++ * the request to decode
++ * @param charset
++ * the charset to use as default
++ * @param maxFields
++ * the maximum number of fields the form can have, {@code -1} to disable
++ * @param maxBufferedBytes
++ * the maximum number of bytes the decoder can buffer when decoding a field, {@code -1} to disable
++ * @throws NullPointerException
++ * for request or charset or factory
++ * @throws ErrorDataDecoderException
++ * if the default charset was wrong when decoding or other
++ * errors
++ */
++ public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset,
++ int maxFields, int maxBufferedBytes) {
+ this.request = checkNotNull(request, "request");
+ this.charset = checkNotNull(charset, "charset");
+ this.factory = checkNotNull(factory, "factory");
++ this.maxFields = maxFields;
++ this.maxBufferedBytes = maxBufferedBytes;
+ // Fill default values
+
+ setMultipart(this.request.headers().get(HttpHeaderNames.CONTENT_TYPE));
+@@ -334,6 +369,9 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
+ isLastChunk = true;
+ }
+ parseBody();
++ if (maxBufferedBytes > 0 && undecodedChunk != null && undecodedChunk.readableBytes() > maxBufferedBytes) {
++ throw new HttpPostRequestDecoder.TooLongFormFieldException();
++ }
+ if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) {
+ undecodedChunk.discardReadBytes();
+ }
+@@ -418,6 +456,9 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
+ if (data == null) {
+ return;
+ }
++ if (maxFields > 0 && bodyListHttpData.size() >= maxFields) {
++ throw new HttpPostRequestDecoder.TooManyFormFieldsException();
++ }
+ List<InterfaceHttpData> datas = bodyMapHttpData.get(data.getName());
+ if (datas == null) {
+ datas = new ArrayList<InterfaceHttpData>(1);
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
+index 0183b23..7812f3e 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
+@@ -37,6 +37,10 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
+
+ static final int DEFAULT_DISCARD_THRESHOLD = 10 * 1024 * 1024;
+
++ static final int DEFAULT_MAX_FIELDS = 128;
++
++ static final int DEFAULT_MAX_BUFFERED_BYTES = 1024;
++
+ private final InterfaceHttpPostRequestDecoder decoder;
+
+ /**
+@@ -53,6 +57,25 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
+ this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET);
+ }
+
++ /**
++ *
++ * @param request
++ * the request to decode
++ * @param maxFields
++ * the maximum number of fields the form can have, {@code -1} to disable
++ * @param maxBufferedBytes
++ * the maximum number of bytes the decoder can buffer when decoding a field, {@code -1} to disable
++ * @throws NullPointerException
++ * for request
++ * @throws ErrorDataDecoderException
++ * if the default charset was wrong when decoding or other
++ * errors
++ */
++ public HttpPostRequestDecoder(HttpRequest request, int maxFields, int maxBufferedBytes) {
++ this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET,
++ maxFields, maxBufferedBytes);
++ }
++
+ /**
+ *
+ * @param factory
+@@ -96,6 +119,38 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
+ }
+ }
+
++ /**
++ *
++ * @param factory
++ * the factory used to create InterfaceHttpData
++ * @param request
++ * the request to decode
++ * @param charset
++ * the charset to use as default
++ * @param maxFields
++ * the maximum number of fields the form can have, {@code -1} to disable
++ * @param maxBufferedBytes
++ * the maximum number of bytes the decoder can buffer when decoding a field, {@code -1} to disable
++ * @throws NullPointerException
++ * for request or charset or factory
++ * @throws ErrorDataDecoderException
++ * if the default charset was wrong when decoding or other
++ * errors
++ */
++ public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset,
++ int maxFields, int maxBufferedBytes) {
++ ObjectUtil.checkNotNull(factory, "factory");
++ ObjectUtil.checkNotNull(request, "request");
++ ObjectUtil.checkNotNull(charset, "charset");
++
++ // Fill default values
++ if (isMultipart(request)) {
++ decoder = new HttpPostMultipartRequestDecoder(factory, request, charset, maxFields, maxBufferedBytes);
++ } else {
++ decoder = new HttpPostStandardRequestDecoder(factory, request, charset, maxFields, maxBufferedBytes);
++ }
++ }
++
+ /**
+ * states follow NOTSTARTED PREAMBLE ( (HEADERDELIMITER DISPOSITION (FIELD |
+ * FILEUPLOAD))* (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE (MIXEDDELIMITER
+@@ -338,4 +393,18 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
+ super(msg, cause);
+ }
+ }
++
++ /**
++ * Exception when the maximum number of fields for a given form is reached
++ */
++ public static final class TooManyFormFieldsException extends DecoderException {
++ private static final long serialVersionUID = 1336267941020800769L;
++ }
++
++ /**
++ * Exception when a field content is too long
++ */
++ public static final class TooLongFormFieldException extends DecoderException {
++ private static final long serialVersionUID = 1336267941020800769L;
++ }
+ }
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java
+index 7b94a7c..47f9315 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java
+@@ -16,6 +16,7 @@
+ package io.netty.handler.codec.http.multipart;
+
+ import io.netty.buffer.ByteBuf;
++import io.netty.handler.codec.DecoderException;
+ import io.netty.handler.codec.http.HttpConstants;
+ import io.netty.handler.codec.http.HttpContent;
+ import io.netty.handler.codec.http.HttpRequest;
+@@ -26,6 +27,8 @@ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDec
+ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
+ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus;
+ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException;
++import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.TooManyFormFieldsException;
++import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.TooLongFormFieldException;
+ import io.netty.util.internal.PlatformDependent;
+
+ import java.io.IOException;
+@@ -61,6 +64,16 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
+ */
+ private final Charset charset;
+
++ /**
++ * The maximum number of fields allows by the form
++ */
++ private final int maxFields;
++
++ /**
++ * The maximum number of accumulated bytes when decoding a field
++ */
++ private final int maxBufferedBytes;
++
+ /**
+ * Does the last chunk already received
+ */
+@@ -146,9 +159,34 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
+ * errors
+ */
+ public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) {
++ this(factory, request, charset, HttpPostRequestDecoder.DEFAULT_MAX_FIELDS, HttpPostRequestDecoder.DEFAULT_MAX_BUFFERED_BYTES);
++ }
++
++ /**
++ *
++ * @param factory
++ * the factory used to create InterfaceHttpData
++ * @param request
++ * the request to decode
++ * @param charset
++ * the charset to use as default
++ * @param maxFields
++ * the maximum number of fields the form can have, {@code -1} to disable
++ * @param maxBufferedBytes
++ * the maximum number of bytes the decoder can buffer when decoding a field, {@code -1} to disable
++ * @throws NullPointerException
++ * for request or charset or factory
++ * @throws ErrorDataDecoderException
++ * if the default charset was wrong when decoding or other
++ * errors
++ */
++ public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset,
++ int maxFields, int maxBufferedBytes) {
+ this.request = checkNotNull(request, "request");
+ this.charset = checkNotNull(charset, "charset");
+ this.factory = checkNotNull(factory, "factory");
++ this.maxFields = maxFields;
++ this.maxBufferedBytes = maxBufferedBytes;
+ try {
+ if (request instanceof HttpContent) {
+ // Offer automatically if the given request is als type of HttpContent
+@@ -293,6 +331,9 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
+ isLastChunk = true;
+ }
+ parseBody();
++ if (maxBufferedBytes > 0 && undecodedChunk != null && undecodedChunk.readableBytes() > maxBufferedBytes) {
++ throw new TooLongFormFieldException();
++ }
+ if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) {
+ undecodedChunk.discardReadBytes();
+ }
+@@ -373,6 +414,9 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
+ if (data == null) {
+ return;
+ }
++ if (maxFields > 0 && bodyListHttpData.size() >= maxFields) {
++ throw new TooManyFormFieldsException();
++ }
+ List<InterfaceHttpData> datas = bodyMapHttpData.get(data.getName());
+ if (datas == null) {
+ datas = new ArrayList<InterfaceHttpData>(1);
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java
+index 40771e0..bbd43be 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java
+@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
+ import io.netty.buffer.ByteBufAllocator;
+ import io.netty.buffer.Unpooled;
+ import io.netty.buffer.UnpooledByteBufAllocator;
++import io.netty.handler.codec.DecoderException;
+ import io.netty.handler.codec.DecoderResult;
+ import io.netty.handler.codec.http.DefaultFullHttpRequest;
+ import io.netty.handler.codec.http.DefaultHttpContent;
+@@ -803,4 +804,106 @@ public class HttpPostRequestDecoderTest {
+ decoder.destroy();
+ req.release();
+ }
++
++ @Test
++ public void testTooManyFormFieldsPostStandardDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, 1024, -1);
++
++ int num = 0;
++ while (true) {
++ try {
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer("foo=bar&".getBytes())));
++ } catch (DecoderException e) {
++ assertEquals(HttpPostRequestDecoder.TooManyFormFieldsException.class, e.getClass());
++ break;
++ }
++ assertTrue(num++ < 1024);
++ }
++ assertEquals(1024, num);
++ }
++
++ @Test
++ public void testTooManyFormFieldsPostMultipartDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++ req.headers().add("Content-Type", "multipart/form-data;boundary=be38b42a9ad2713f");
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, 1024, -1);
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer("--be38b42a9ad2713f\n".getBytes())));
++
++ int num = 0;
++ while (true) {
++ try {
++ byte[] bodyBytes = ("content-disposition: form-data; name=\"title\"\n" +
++ "content-length: 10\n" +
++ "content-type: text/plain; charset=UTF-8\n" +
++ "\n" +
++ "bar-stream\n" +
++ "--be38b42a9ad2713f\n").getBytes();
++ ByteBuf content = Unpooled.wrappedBuffer(bodyBytes);
++ decoder.offer(new DefaultHttpContent(content));
++ } catch (DecoderException e) {
++ assertEquals(HttpPostRequestDecoder.TooManyFormFieldsException.class, e.getClass());
++ break;
++ }
++ assertTrue(num++ < 1024);
++ }
++ assertEquals(1024, num);
++ }
++
++ @Test
++ public void testTooLongFormFieldStandardDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, -1, 16 * 1024);
++
++ try {
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[16 * 1024 + 1])));
++ fail();
++ } catch (DecoderException e) {
++ assertEquals(HttpPostRequestDecoder.TooLongFormFieldException.class, e.getClass());
++ }
++ }
++
++ @Test
++ public void testFieldGreaterThanMaxBufferedBytesStandardDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, -1, 6);
++
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer("foo=bar".getBytes())));
++ }
++
++ @Test
++ public void testTooLongFormFieldMultipartDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++ req.headers().add("Content-Type", "multipart/form-data;boundary=be38b42a9ad2713f");
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, -1, 16 * 1024);
++
++ try {
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[16 * 1024 + 1])));
++ fail();
++ } catch (DecoderException e) {
++ assertEquals(HttpPostRequestDecoder.TooLongFormFieldException.class, e.getClass());
++ }
++ }
++
++ @Test
++ public void testFieldGreaterThanMaxBufferedBytesMultipartDecoder() {
++ HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
++ req.headers().add("Content-Type", "multipart/form-data;boundary=be38b42a9ad2713f");
++
++ byte[] bodyBytes = ("content-disposition: form-data; name=\"title\"\n" +
++ "content-length: 10\n" +
++ "content-type: text/plain; charset=UTF-8\n" +
++ "\n" +
++ "bar-stream\n" +
++ "--be38b42a9ad2713f\n").getBytes();
++
++ HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req, -1, bodyBytes.length - 1);
++
++ decoder.offer(new DefaultHttpContent(Unpooled.wrappedBuffer(bodyBytes)));
++ }
+ }
=====================================
debian/patches/series
=====================================
@@ -24,3 +24,4 @@ CVE-2022-41915.patch
CVE-2023-34462.patch
CVE-2023-44487.patch
22-java-21.patch
+CVE-2024-29025.patch
View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/bfa84aaedde0d26ca962c5c3f18c530021e2b53a...93db9f23610aa26c50d47fa622264f200b699c7f
--
View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/bfa84aaedde0d26ca962c5c3f18c530021e2b53a...93db9f23610aa26c50d47fa622264f200b699c7f
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/20240512/a8dd2464/attachment.htm>
More information about the pkg-java-commits
mailing list