Projects
openEuler:24.03:SP1:Everything
netty
_service:tar_scm:CVE-2024-29025.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:CVE-2024-29025.patch of Package netty
From: Markus Koschany <apo@debian.org> Date: Sat, 11 May 2024 21:52:15 +0200 Subject: CVE-2024-29025 Bug-Debian: https://bugs.debian.org/1068110 Origin: https://github.com/netty/netty/commit/0d0c6ed782d13d423586ad0c71737b2c7d02058c --- .../HttpPostMultipartRequestDecoder.java | 41 +++++++ .../multipart/HttpPostRequestDecoder.java | 70 ++++++++++++ .../HttpPostStandardRequestDecoder.java | 44 ++++++++ .../multipart/HttpPostRequestDecoderTest.java | 103 ++++++++++++++++++ 4 files changed, 258 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 17c3e64..7cac6a0 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 @@ -61,6 +61,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 */ @@ -172,9 +182,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)); @@ -333,6 +368,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(); } @@ -417,6 +455,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 0c10626..d57b63e 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 @@ -25,6 +25,7 @@ import io.netty.util.internal.StringUtil; import java.nio.charset.Charset; import java.util.List; +import io.netty.util.internal.ObjectUtil; /** * This decoder will decode Body and can handle POST BODY. @@ -36,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; /** @@ -52,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 @@ -100,6 +124,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 @@ -342,4 +398,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 ece64d8..65a9e16 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 java.io.IOException; import java.nio.charset.Charset; @@ -60,6 +63,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 */ @@ -145,9 +158,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; if (request instanceof HttpContent) { // Offer automatically if the given request is als type of HttpContent // See #1089 @@ -287,6 +325,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(); } @@ -367,6 +408,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 1334107..bdc5fd7 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; @@ -491,4 +492,106 @@ public class HttpPostRequestDecoderTest { content.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))); + } } -- 2.47.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2