[Git][java-team/netty][master] Import Debian changes 1:4.1.48-6
Markus Koschany (@apo)
gitlab at salsa.debian.org
Sun Jan 1 22:11:16 GMT 2023
Markus Koschany pushed to branch master at Debian Java Maintainers / netty
Commits:
5406c252 by Markus Koschany at 2023-01-01T23:10:05+01:00
Import Debian changes 1:4.1.48-6
netty (1:4.1.48-6) unstable; urgency=high
..
* Team upload.
* Fix CVE-2021-37136, CVE-2021-37137, CVE-2021-43797, CVE-2022-41881,
and CVE-2022-41915. Several out-of-memory, stack overflow or HTTP request
smuggling vulnerabilities have been discovered in Netty which may allow
attackers to cause a denial of service or bypass restrictions when used as
a proxy. (Closes: #1027180, #1014769, #1001437)
- - - - -
7 changed files:
- debian/changelog
- + debian/patches/CVE-2021-37136.patch
- + debian/patches/CVE-2021-37137.patch
- + debian/patches/CVE-2021-43797.patch
- + debian/patches/CVE-2022-41881.patch
- + debian/patches/CVE-2022-41915.patch
- debian/patches/series
Changes:
=====================================
debian/changelog
=====================================
@@ -1,3 +1,14 @@
+netty (1:4.1.48-6) unstable; urgency=high
+
+ * Team upload.
+ * Fix CVE-2021-37136, CVE-2021-37137, CVE-2021-43797, CVE-2022-41881,
+ and CVE-2022-41915. Several out-of-memory, stack overflow or HTTP request
+ smuggling vulnerabilities have been discovered in Netty which may allow
+ attackers to cause a denial of service or bypass restrictions when used as
+ a proxy. (Closes: #1027180, #1014769, #1001437)
+
+ -- Markus Koschany <apo at debian.org> Sun, 01 Jan 2023 19:17:21 +0100
+
netty (1:4.1.48-5) unstable; urgency=medium
* Team upload.
=====================================
debian/patches/CVE-2021-37136.patch
=====================================
@@ -0,0 +1,80 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 1 Jan 2023 17:07:49 +0100
+Subject: CVE-2021-37136
+
+Bug-Debian: https://bugs.debian.org/1014769
+Origin: https://github.com/netty/netty/commit/41d3d61a61608f2223bb364955ab2045dd5e4020
+---
+ .../handler/codec/compression/Bzip2BlockDecompressor.java | 5 +++++
+ .../netty/handler/codec/compression/Bzip2Constants.java | 2 ++
+ .../io/netty/handler/codec/compression/Bzip2Decoder.java | 15 ++++++++-------
+ 3 files changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java
+index ae05568..5a0b497 100644
+--- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java
++++ b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java
+@@ -228,6 +228,11 @@ final class Bzip2BlockDecompressor {
+ bwtBlock[bwtBlockLength++] = nextByte;
+ }
+ }
++ if (bwtBlockLength > MAX_BLOCK_LENGTH) {
++ throw new DecompressionException("block length exceeds max block length: "
++ + bwtBlockLength + " > " + MAX_BLOCK_LENGTH);
++ }
++
+ this.bwtBlockLength = bwtBlockLength;
+ initialiseInverseBWT();
+ return true;
+diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java
+index c0283a7..21b9a2b 100644
+--- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java
++++ b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java
+@@ -49,6 +49,8 @@ final class Bzip2Constants {
+ static final int MIN_BLOCK_SIZE = 1;
+ static final int MAX_BLOCK_SIZE = 9;
+
++ static final int MAX_BLOCK_LENGTH = MAX_BLOCK_SIZE * BASE_BLOCK_SIZE;
++
+ /**
+ * Maximum possible Huffman alphabet size.
+ */
+diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java
+index b66ff59..3fc1001 100644
+--- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java
++++ b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java
+@@ -291,26 +291,27 @@ public class Bzip2Decoder extends ByteToMessageDecoder {
+ }
+
+ final int blockLength = blockDecompressor.blockLength();
+- final ByteBuf uncompressed = ctx.alloc().buffer(blockLength);
+- boolean success = false;
++ ByteBuf uncompressed = ctx.alloc().buffer(blockLength);
+ try {
+ int uncByte;
+ while ((uncByte = blockDecompressor.read()) >= 0) {
+ uncompressed.writeByte(uncByte);
+ }
+-
++ // We did read all the data, lets reset the state and do the CRC check.
++ currentState = State.INIT_BLOCK;
+ int currentBlockCRC = blockDecompressor.checkCRC();
+ streamCRC = (streamCRC << 1 | streamCRC >>> 31) ^ currentBlockCRC;
+
+ out.add(uncompressed);
+- success = true;
++ uncompressed = null;
+ } finally {
+- if (!success) {
++ if (uncompressed != null) {
+ uncompressed.release();
+ }
+ }
+- currentState = State.INIT_BLOCK;
+- break;
++ // Return here so the ByteBuf that was put in the List will be forwarded to the user and so can be
++ // released as soon as possible.
++ return;
+ case EOF:
+ in.skipBytes(in.readableBytes());
+ return;
=====================================
debian/patches/CVE-2021-37137.patch
=====================================
@@ -0,0 +1,183 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 1 Jan 2023 18:28:34 +0100
+Subject: CVE-2021-37137
+
+Bug-Debian: https://bugs.debian.org/1014769
+Origin: https://github.com/netty/netty/commit/6da4956b31023ae967451e1d94ff51a746a9194f
+---
+ .../io/netty/handler/codec/compression/Snappy.java | 30 ++++++++++----
+ .../codec/compression/SnappyFrameDecoder.java | 46 ++++++++++++++++++----
+ 2 files changed, 62 insertions(+), 14 deletions(-)
+
+diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java
+index 8e1825d..c912ed2 100644
+--- a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java
++++ b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java
+@@ -38,12 +38,11 @@ public final class Snappy {
+ private static final int COPY_2_BYTE_OFFSET = 2;
+ private static final int COPY_4_BYTE_OFFSET = 3;
+
+- private State state = State.READY;
++ private State state = State.READING_PREAMBLE;
+ private byte tag;
+ private int written;
+
+ private enum State {
+- READY,
+ READING_PREAMBLE,
+ READING_TAG,
+ READING_LITERAL,
+@@ -51,7 +50,7 @@ public final class Snappy {
+ }
+
+ public void reset() {
+- state = State.READY;
++ state = State.READING_PREAMBLE;
+ tag = 0;
+ written = 0;
+ }
+@@ -270,9 +269,6 @@ public final class Snappy {
+ public void decode(ByteBuf in, ByteBuf out) {
+ while (in.isReadable()) {
+ switch (state) {
+- case READY:
+- state = State.READING_PREAMBLE;
+- // fall through
+ case READING_PREAMBLE:
+ int uncompressedLength = readPreamble(in);
+ if (uncompressedLength == PREAMBLE_NOT_FULL) {
+@@ -281,7 +277,6 @@ public final class Snappy {
+ }
+ if (uncompressedLength == 0) {
+ // Should never happen, but it does mean we have nothing further to do
+- state = State.READY;
+ return;
+ }
+ out.ensureWritable(uncompressedLength);
+@@ -378,6 +373,27 @@ public final class Snappy {
+ return 0;
+ }
+
++ /**
++ * Get the length varint (a series of bytes, where the lower 7 bits
++ * are data and the upper bit is a flag to indicate more bytes to be
++ * read).
++ *
++ * @param in The input buffer to get the preamble from
++ * @return The calculated length based on the input buffer, or 0 if
++ * no preamble is able to be calculated
++ */
++ int getPreamble(ByteBuf in) {
++ if (state == State.READING_PREAMBLE) {
++ int readerIndex = in.readerIndex();
++ try {
++ return readPreamble(in);
++ } finally {
++ in.readerIndex(readerIndex);
++ }
++ }
++ return 0;
++ }
++
+ /**
+ * Reads a literal from the input buffer directly to the output buffer.
+ * A "literal" is an uncompressed segment of data stored directly in the
+diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java
+index 4762e72..59fdc68 100644
+--- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java
++++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java
+@@ -45,13 +45,19 @@ public class SnappyFrameDecoder extends ByteToMessageDecoder {
+ }
+
+ private static final int SNAPPY_IDENTIFIER_LEN = 6;
++ // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L95
+ private static final int MAX_UNCOMPRESSED_DATA_SIZE = 65536 + 4;
++ // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L82
++ private static final int MAX_DECOMPRESSED_DATA_SIZE = 65536;
++ // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L82
++ private static final int MAX_COMPRESSED_CHUNK_SIZE = 16777216 - 1;
+
+ private final Snappy snappy = new Snappy();
+ private final boolean validateChecksums;
+
+ private boolean started;
+ private boolean corrupted;
++ private int numBytesToSkip;
+
+ /**
+ * Creates a new snappy-framed decoder with validation of checksums
+@@ -82,6 +88,16 @@ public class SnappyFrameDecoder extends ByteToMessageDecoder {
+ return;
+ }
+
++ if (numBytesToSkip != 0) {
++ // The last chunkType we detected was RESERVED_SKIPPABLE and we still have some bytes to skip.
++ int skipBytes = Math.min(numBytesToSkip, in.readableBytes());
++ in.skipBytes(skipBytes);
++ numBytesToSkip -= skipBytes;
++
++ // Let's return and try again.
++ return;
++ }
++
+ try {
+ int idx = in.readerIndex();
+ final int inSize = in.readableBytes();
+@@ -123,12 +139,15 @@ public class SnappyFrameDecoder extends ByteToMessageDecoder {
+ throw new DecompressionException("Received RESERVED_SKIPPABLE tag before STREAM_IDENTIFIER");
+ }
+
+- if (inSize < 4 + chunkLength) {
+- // TODO: Don't keep skippable bytes
+- return;
+- }
++ in.skipBytes(4);
+
+- in.skipBytes(4 + chunkLength);
++ int skipBytes = Math.min(chunkLength, in.readableBytes());
++ in.skipBytes(skipBytes);
++ if (skipBytes != chunkLength) {
++ // We could skip all bytes, let's store the remaining so we can do so once we receive more
++ // data.
++ numBytesToSkip = chunkLength - skipBytes;
++ }
+ break;
+ case RESERVED_UNSKIPPABLE:
+ // The spec mandates that reserved unskippable chunks must immediately
+@@ -141,7 +160,8 @@ public class SnappyFrameDecoder extends ByteToMessageDecoder {
+ throw new DecompressionException("Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER");
+ }
+ if (chunkLength > MAX_UNCOMPRESSED_DATA_SIZE) {
+- throw new DecompressionException("Received UNCOMPRESSED_DATA larger than 65540 bytes");
++ throw new DecompressionException("Received UNCOMPRESSED_DATA larger than " +
++ MAX_UNCOMPRESSED_DATA_SIZE + " bytes");
+ }
+
+ if (inSize < 4 + chunkLength) {
+@@ -162,13 +182,25 @@ public class SnappyFrameDecoder extends ByteToMessageDecoder {
+ throw new DecompressionException("Received COMPRESSED_DATA tag before STREAM_IDENTIFIER");
+ }
+
++ if (chunkLength > MAX_COMPRESSED_CHUNK_SIZE) {
++ throw new DecompressionException("Received COMPRESSED_DATA that contains" +
++ " chunk that exceeds " + MAX_COMPRESSED_CHUNK_SIZE + " bytes");
++ }
++
+ if (inSize < 4 + chunkLength) {
+ return;
+ }
+
+ in.skipBytes(4);
+ int checksum = in.readIntLE();
+- ByteBuf uncompressed = ctx.alloc().buffer();
++
++ int uncompressedSize = snappy.getPreamble(in);
++ if (uncompressedSize > MAX_DECOMPRESSED_DATA_SIZE) {
++ throw new DecompressionException("Received COMPRESSED_DATA that contains" +
++ " uncompressed data that exceeds " + MAX_DECOMPRESSED_DATA_SIZE + " bytes");
++ }
++
++ ByteBuf uncompressed = ctx.alloc().buffer(uncompressedSize, MAX_DECOMPRESSED_DATA_SIZE);
+ try {
+ if (validateChecksums) {
+ int oldWriterIndex = in.writerIndex();
=====================================
debian/patches/CVE-2021-43797.patch
=====================================
@@ -0,0 +1,298 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 1 Jan 2023 18:42:42 +0100
+Subject: CVE-2021-43797
+
+Bug-Debian: https://bugs.debian.org/1001437
+Origin: https://github.com/netty/netty/commit/07aa6b5938a8b6ed7a6586e066400e2643897323
+---
+ .../handler/codec/http/DefaultHttpHeaders.java | 8 ++
+ .../handler/codec/http/HttpObjectDecoder.java | 8 +-
+ .../handler/codec/http/HttpRequestDecoderTest.java | 87 ++++++++++++++++++++--
+ .../codec/http/HttpResponseDecoderTest.java | 78 +++++++++++++++++++
+ 4 files changed, 171 insertions(+), 10 deletions(-)
+
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java
+index 675d513..33064f6 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java
+@@ -367,6 +367,10 @@ public class DefaultHttpHeaders extends HttpHeaders {
+
+ private static void validateHeaderNameElement(byte value) {
+ switch (value) {
++ case 0x1c:
++ case 0x1d:
++ case 0x1e:
++ case 0x1f:
+ case 0x00:
+ case '\t':
+ case '\n':
+@@ -391,6 +395,10 @@ public class DefaultHttpHeaders extends HttpHeaders {
+
+ private static void validateHeaderNameElement(char value) {
+ switch (value) {
++ case 0x1c:
++ case 0x1d:
++ case 0x1e:
++ case 0x1f:
+ case 0x00:
+ case '\t':
+ case '\n':
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
+index 1384df7..4aa864a 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
+@@ -759,7 +759,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+ int valueStart;
+ int valueEnd;
+
+- nameStart = findNonWhitespace(sb, 0, false);
++ nameStart = findNonWhitespace(sb, 0);
+ for (nameEnd = nameStart; nameEnd < length; nameEnd ++) {
+ char ch = sb.charAtUnsafe(nameEnd);
+ // https://tools.ietf.org/html/rfc7230#section-3.2.4
+@@ -794,7 +794,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+ }
+
+ name = sb.subStringUnsafe(nameStart, nameEnd);
+- valueStart = findNonWhitespace(sb, colonEnd, true);
++ valueStart = findNonWhitespace(sb, colonEnd);
+ if (valueStart == length) {
+ value = EMPTY_VALUE;
+ } else {
+@@ -833,12 +833,12 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+ return c == ' ' || c == (char) 0x09 || c == (char) 0x0B || c == (char) 0x0C || c == (char) 0x0D;
+ }
+
+- private static int findNonWhitespace(AppendableCharSequence sb, int offset, boolean validateOWS) {
++ private static int findNonWhitespace(AppendableCharSequence sb, int offset) {
+ for (int result = offset; result < sb.length(); ++result) {
+ char c = sb.charAtUnsafe(result);
+ if (!Character.isWhitespace(c)) {
+ return result;
+- } else if (validateOWS && !isOWS(c)) {
++ } else if (!isOWS(c)) {
+ // Only OWS is supported for whitespace
+ throw new IllegalArgumentException("Invalid separator, only a single space or horizontal tab allowed," +
+ " but received a '" + c + "'");
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java
+index 4f83d82..753cf35 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java
+@@ -15,6 +15,7 @@
+ */
+ package io.netty.handler.codec.http;
+
++import io.netty.buffer.ByteBuf;
+ import io.netty.buffer.Unpooled;
+ import io.netty.channel.embedded.EmbeddedChannel;
+ import io.netty.handler.codec.TooLongFrameException;
+@@ -357,6 +358,75 @@ public class HttpRequestDecoderTest {
+ assertFalse(channel.finish());
+ }
+
++ @Test
++ public void testHeaderNameStartsWithControlChar1c() {
++ testHeaderNameStartsWithControlChar(0x1c);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1d() {
++ testHeaderNameStartsWithControlChar(0x1d);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1e() {
++ testHeaderNameStartsWithControlChar(0x1e);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1f() {
++ testHeaderNameStartsWithControlChar(0x1f);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar0c() {
++ testHeaderNameStartsWithControlChar(0x0c);
++ }
++
++ private void testHeaderNameStartsWithControlChar(int controlChar) {
++ ByteBuf requestBuffer = Unpooled.buffer();
++ requestBuffer.writeCharSequence("GET /some/path HTTP/1.1\r\n" +
++ "Host: netty.io\r\n", CharsetUtil.US_ASCII);
++ requestBuffer.writeByte(controlChar);
++ requestBuffer.writeCharSequence("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.US_ASCII);
++ testInvalidHeaders0(requestBuffer);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1c() {
++ testHeaderNameEndsWithControlChar(0x1c);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1d() {
++ testHeaderNameEndsWithControlChar(0x1d);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1e() {
++ testHeaderNameEndsWithControlChar(0x1e);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1f() {
++ testHeaderNameEndsWithControlChar(0x1f);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar0c() {
++ testHeaderNameEndsWithControlChar(0x0c);
++ }
++
++ private void testHeaderNameEndsWithControlChar(int controlChar) {
++ ByteBuf requestBuffer = Unpooled.buffer();
++ requestBuffer.writeCharSequence("GET /some/path HTTP/1.1\r\n" +
++ "Host: netty.io\r\n", CharsetUtil.US_ASCII);
++ requestBuffer.writeCharSequence("Transfer-Encoding", CharsetUtil.US_ASCII);
++ requestBuffer.writeByte(controlChar);
++ requestBuffer.writeCharSequence(": chunked\r\n\r\n", CharsetUtil.US_ASCII);
++ testInvalidHeaders0(requestBuffer);
++ }
++
+ @Test
+ public void testWhitespace() {
+ String requestStr = "GET /some/path HTTP/1.1\r\n" +
+@@ -366,9 +436,9 @@ public class HttpRequestDecoderTest {
+ }
+
+ @Test
+- public void testWhitespaceBeforeTransferEncoding01() {
++ public void testWhitespaceInTransferEncoding01() {
+ String requestStr = "GET /some/path HTTP/1.1\r\n" +
+- " Transfer-Encoding : chunked\r\n" +
++ "Transfer-Encoding : chunked\r\n" +
+ "Content-Length: 1\r\n" +
+ "Host: netty.io\r\n\r\n" +
+ "a";
+@@ -376,9 +446,9 @@ public class HttpRequestDecoderTest {
+ }
+
+ @Test
+- public void testWhitespaceBeforeTransferEncoding02() {
++ public void testWhitespaceInTransferEncoding02() {
+ String requestStr = "POST / HTTP/1.1" +
+- " Transfer-Encoding : chunked\r\n" +
++ "Transfer-Encoding : chunked\r\n" +
+ "Host: target.com" +
+ "Content-Length: 65\r\n\r\n" +
+ "0\r\n\r\n" +
+@@ -475,15 +545,20 @@ public class HttpRequestDecoderTest {
+ assertTrue(request.headers().contains("Transfer-Encoding", "chunked", false));
+ assertFalse(request.headers().contains("Content-Length"));
+ LastHttpContent c = channel.readInbound();
++ c.release();
+ assertFalse(channel.finish());
+ }
+
+ private static void testInvalidHeaders0(String requestStr) {
++ testInvalidHeaders0(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII));
++ }
++
++ private static void testInvalidHeaders0(ByteBuf requestBuffer) {
+ EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder());
+- assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)));
++ assertTrue(channel.writeInbound(requestBuffer));
+ HttpRequest request = channel.readInbound();
++ assertThat(request.decoderResult().cause(), instanceOf(IllegalArgumentException.class));
+ assertTrue(request.decoderResult().isFailure());
+- assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException);
+ assertFalse(channel.finish());
+ }
+ }
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java
+index b0ccd0c..a7418b0 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java
+@@ -727,4 +727,82 @@ public class HttpResponseDecoderTest {
+ assertEquals("netty.io", response.headers().get(HttpHeaderNames.HOST));
+ assertFalse(channel.finish());
+ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1c() {
++ testHeaderNameStartsWithControlChar(0x1c);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1d() {
++ testHeaderNameStartsWithControlChar(0x1d);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1e() {
++ testHeaderNameStartsWithControlChar(0x1e);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar1f() {
++ testHeaderNameStartsWithControlChar(0x1f);
++ }
++
++ @Test
++ public void testHeaderNameStartsWithControlChar0c() {
++ testHeaderNameStartsWithControlChar(0x0c);
++ }
++
++ private void testHeaderNameStartsWithControlChar(int controlChar) {
++ ByteBuf responseBuffer = Unpooled.buffer();
++ responseBuffer.writeCharSequence("HTTP/1.1 200 OK\r\n" +
++ "Host: netty.io\r\n", CharsetUtil.US_ASCII);
++ responseBuffer.writeByte(controlChar);
++ responseBuffer.writeCharSequence("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.US_ASCII);
++ testInvalidHeaders0(responseBuffer);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1c() {
++ testHeaderNameEndsWithControlChar(0x1c);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1d() {
++ testHeaderNameEndsWithControlChar(0x1d);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1e() {
++ testHeaderNameEndsWithControlChar(0x1e);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar1f() {
++ testHeaderNameEndsWithControlChar(0x1f);
++ }
++
++ @Test
++ public void testHeaderNameEndsWithControlChar0c() {
++ testHeaderNameEndsWithControlChar(0x0c);
++ }
++
++ private void testHeaderNameEndsWithControlChar(int controlChar) {
++ ByteBuf responseBuffer = Unpooled.buffer();
++ responseBuffer.writeCharSequence("HTTP/1.1 200 OK\r\n" +
++ "Host: netty.io\r\n", CharsetUtil.US_ASCII);
++ responseBuffer.writeCharSequence("Transfer-Encoding", CharsetUtil.US_ASCII);
++ responseBuffer.writeByte(controlChar);
++ responseBuffer.writeCharSequence(": chunked\r\n\r\n", CharsetUtil.US_ASCII);
++ testInvalidHeaders0(responseBuffer);
++ }
++
++ private static void testInvalidHeaders0(ByteBuf responseBuffer) {
++ EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder());
++ assertTrue(channel.writeInbound(responseBuffer));
++ HttpResponse response = channel.readInbound();
++ assertThat(response.decoderResult().cause(), instanceOf(IllegalArgumentException.class));
++ assertTrue(response.decoderResult().isFailure());
++ assertFalse(channel.finish());
++ }
+ }
=====================================
debian/patches/CVE-2022-41881.patch
=====================================
@@ -0,0 +1,150 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 1 Jan 2023 19:08:24 +0100
+Subject: CVE-2022-41881
+
+Bug-Debian: https://bugs.debian.org/1027180
+Origin: https://github.com/netty/netty/commit/cd91cf3c99123bd1e53fd6a1de0e3d1922f05bb2
+---
+ .../handler/codec/haproxy/HAProxyMessage.java | 16 ++++--
+ .../codec/haproxy/HAProxyMessageDecoderTest.java | 65 ++++++++++++++++++++++
+ 2 files changed, 76 insertions(+), 5 deletions(-)
+
+diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java
+index 1777e67..45d24a2 100644
+--- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java
++++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java
+@@ -34,6 +34,9 @@ import java.util.List;
+ * Message container for decoded HAProxy proxy protocol parameters
+ */
+ public final class HAProxyMessage extends AbstractReferenceCounted {
++
++ // Let's pick some conservative limit here.
++ private static final int MAX_NESTING_LEVEL = 128;
+ private static final ResourceLeakDetector<HAProxyMessage> leakDetector =
+ ResourceLeakDetectorFactory.instance().newResourceLeakDetector(HAProxyMessage.class);
+
+@@ -223,7 +226,7 @@ public final class HAProxyMessage extends AbstractReferenceCounted {
+ }
+
+ private static List<HAProxyTLV> readTlvs(final ByteBuf header) {
+- HAProxyTLV haProxyTLV = readNextTLV(header);
++ HAProxyTLV haProxyTLV = readNextTLV(header, 0);
+ if (haProxyTLV == null) {
+ return Collections.emptyList();
+ }
+@@ -235,12 +238,15 @@ public final class HAProxyMessage extends AbstractReferenceCounted {
+ if (haProxyTLV instanceof HAProxySSLTLV) {
+ haProxyTLVs.addAll(((HAProxySSLTLV) haProxyTLV).encapsulatedTLVs());
+ }
+- } while ((haProxyTLV = readNextTLV(header)) != null);
++ } while ((haProxyTLV = readNextTLV(header, 0)) != null);
+ return haProxyTLVs;
+ }
+
+- private static HAProxyTLV readNextTLV(final ByteBuf header) {
+-
++ private static HAProxyTLV readNextTLV(final ByteBuf header, int nestingLevel) {
++ if (nestingLevel > MAX_NESTING_LEVEL) {
++ throw new HAProxyProtocolException(
++ "Maximum TLV nesting level reached: " + nestingLevel + " (expected: < " + MAX_NESTING_LEVEL + ')');
++ }
+ // We need at least 4 bytes for a TLV
+ if (header.readableBytes() < 4) {
+ return null;
+@@ -261,7 +267,7 @@ public final class HAProxyMessage extends AbstractReferenceCounted {
+
+ final List<HAProxyTLV> encapsulatedTlvs = new ArrayList<HAProxyTLV>(4);
+ do {
+- final HAProxyTLV haProxyTLV = readNextTLV(byteBuf);
++ final HAProxyTLV haProxyTLV = readNextTLV(byteBuf, nestingLevel + 1);
+ if (haProxyTLV == null) {
+ break;
+ }
+diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java
+index 2c323ea..00c0ac2 100644
+--- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java
++++ b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java
+@@ -16,6 +16,7 @@
+ package io.netty.handler.codec.haproxy;
+
+ import io.netty.buffer.ByteBuf;
++import io.netty.buffer.Unpooled;
+ import io.netty.channel.ChannelFuture;
+ import io.netty.channel.embedded.EmbeddedChannel;
+ import io.netty.handler.codec.ProtocolDetectionResult;
+@@ -28,6 +29,9 @@ import org.junit.Rule;
+ import org.junit.Test;
+ import org.junit.rules.ExpectedException;
+
++import java.io.ByteArrayOutputStream;
++import java.nio.ByteBuffer;
++import java.nio.ByteOrder;
+ import java.util.List;
+
+ import static io.netty.buffer.Unpooled.*;
+@@ -1107,4 +1111,65 @@ public class HAProxyMessageDecoderTest {
+ assertNull(result.detectedProtocol());
+ incompleteHeader.release();
+ }
++
++ @Test
++ public void testNestedTLV() throws Exception {
++ ByteArrayOutputStream headerWriter = new ByteArrayOutputStream();
++ //src_ip = "AAAA", dst_ip = "BBBB", src_port = "CC", dst_port = "DD"
++ headerWriter.write(new byte[] {'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'D', 'D'});
++ //write TLVs
++ int countOfTLVs = 8100;
++ ByteBuffer tlvLengthBuf = ByteBuffer.allocate(2);
++ tlvLengthBuf.order(ByteOrder.BIG_ENDIAN);
++ short totalLength = (short) (countOfTLVs * (1 + 2 + 1 + 4));
++ for (int i = 0; i < countOfTLVs; i++) {
++ //write PP2_TYPE_SSL TLV
++ headerWriter.write(0x20); //PP2_TYPE_SSL
++ //notice that the TLV length cannot be bigger than 0xffff
++ totalLength -= 1 + 2; //exclude type and length themselves
++ tlvLengthBuf.clear();
++ tlvLengthBuf.putShort(totalLength);
++ //add to the header
++ headerWriter.write(tlvLengthBuf.array());
++ //write client field
++ headerWriter.write(1);
++ //write verify field
++ headerWriter.write(new byte[] {'V', 'V', 'V', 'V'});
++ //subtract the client and verify fields
++ totalLength -= 1 + 4;
++ }
++ byte[] header = headerWriter.toByteArray();
++ ByteBuffer numsWrite = ByteBuffer.allocate(2);
++ numsWrite.order(ByteOrder.BIG_ENDIAN);
++ numsWrite.putShort((short) header.length);
++
++ final ByteBuf data = Unpooled.buffer();
++ data.writeBytes(new byte[] {
++ (byte) 0x0D,
++ (byte) 0x0A,
++ (byte) 0x0D,
++ (byte) 0x0A,
++ (byte) 0x00,
++ (byte) 0x0D,
++ (byte) 0x0A,
++ (byte) 0x51,
++ (byte) 0x55,
++ (byte) 0x49,
++ (byte) 0x54,
++ (byte) 0x0A
++ });
++ //verCmd = 32
++ byte versionCmd = 0x20 | 1; //V2 | ProxyCmd
++ data.writeByte(versionCmd);
++ data.writeByte(17); //TPAF_TCP4_BYTE
++ data.writeBytes(numsWrite.array());
++ data.writeBytes(header);
++
++ assertThrows(HAProxyProtocolException.class, new Executable() {
++ @Override
++ public void execute() {
++ ch.writeInbound(data);
++ }
++ });
++ }
+ }
=====================================
debian/patches/CVE-2022-41915.patch
=====================================
@@ -0,0 +1,866 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 1 Jan 2023 19:16:23 +0100
+Subject: CVE-2022-41915
+
+Bug-Debian: https://bugs.debian.org/1027180
+Origin: https://github.com/netty/netty/commit/fe18adff1c2b333acb135ab779a3b9ba3295a1c4
+---
+ .../handler/codec/http/DefaultHttpHeadersTest.java | 25 +-
+ .../io/netty/handler/codec/DefaultHeaders.java | 368 +++++++++++++++++----
+ 2 files changed, 323 insertions(+), 70 deletions(-)
+
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java
+index d0f6c41..44984a7 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java
+@@ -20,7 +20,7 @@ import io.netty.util.AsciiString;
+ import io.netty.util.internal.StringUtil;
+ import org.junit.Test;
+
+-import java.util.Arrays;
++import java.util.Collections;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Set;
+@@ -33,6 +33,7 @@ import static org.junit.Assert.*;
+
+ public class DefaultHttpHeadersTest {
+ private static final CharSequence HEADER_NAME = "testHeader";
++ private static final CharSequence ILLEGAL_VALUE = "testHeader\r\nContent-Length:45\r\n\r\n";
+
+ @Test(expected = IllegalArgumentException.class)
+ public void nullHeaderNameNotAllowed() {
+@@ -206,6 +207,28 @@ public class DefaultHttpHeadersTest {
+ assertDefaultValues(headers, HeaderValue.THREE);
+ }
+
++ @Test
++ public void setCharSequenceValidatesValue() {
++ final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
++ assertThrows(IllegalArgumentException.class, new Executable() {
++ @Override
++ public void execute() throws Throwable {
++ headers.set(HEADER_NAME, ILLEGAL_VALUE);
++ }
++ });
++ }
++
++ @Test
++ public void setIterableValidatesValue() {
++ final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
++ assertThrows(IllegalArgumentException.class, new Executable() {
++ @Override
++ public void execute() throws Throwable {
++ headers.set(HEADER_NAME, Collections.singleton(ILLEGAL_VALUE));
++ }
++ });
++ }
++
+ @Test
+ public void toStringOnEmptyHeaders() {
+ assertEquals("DefaultHttpHeaders[]", newDefaultDefaultHttpHeaders().toString());
+diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
+index 4109037..6abe71b 100644
+--- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
++++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
+@@ -5,7 +5,7 @@
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+- * http://www.apache.org/licenses/LICENSE-2.0
++ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+@@ -52,6 +52,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ private final byte hashMask;
+ private final ValueConverter<V> valueConverter;
+ private final NameValidator<K> nameValidator;
++ private final ValueValidator<V> valueValidator;
+ private final HashingStrategy<K> hashingStrategy;
+ int size;
+
+@@ -72,6 +73,22 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ };
+ }
+
++ public interface ValueValidator<V> {
++ /**
++ * Validate the given value. If the validation fails, then an implementation specific runtime exception may be
++ * thrown.
++ *
++ * @param value The value to validate.
++ */
++ void validate(V value);
++
++ ValueValidator<?> NO_VALIDATION = new ValueValidator<Object>() {
++ @Override
++ public void validate(Object value) {
++ }
++ };
++ }
++
+ @SuppressWarnings("unchecked")
+ public DefaultHeaders(ValueConverter<V> valueConverter) {
+ this(JAVA_HASHER, valueConverter);
+@@ -102,13 +119,30 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ */
+ @SuppressWarnings("unchecked")
+ public DefaultHeaders(HashingStrategy<K> nameHashingStrategy,
+- ValueConverter<V> valueConverter, NameValidator<K> nameValidator, int arraySizeHint) {
++ ValueConverter<V> valueConverter, NameValidator<K> nameValidator, int arraySizeHint) {
++ this(nameHashingStrategy, valueConverter, nameValidator, arraySizeHint,
++ (ValueValidator<V>) ValueValidator.NO_VALIDATION);
++ }
++
++ /**
++ * Create a new instance.
++ * @param nameHashingStrategy Used to hash and equality compare names.
++ * @param valueConverter Used to convert values to/from native types.
++ * @param nameValidator Used to validate name elements.
++ * @param arraySizeHint A hint as to how large the hash data structure should be.
++ * The next positive power of two will be used. An upper bound may be enforced.
++ * @param valueValidator The validation strategy for entry values.
++ */
++ @SuppressWarnings("unchecked")
++ public DefaultHeaders(HashingStrategy<K> nameHashingStrategy, ValueConverter<V> valueConverter,
++ NameValidator<K> nameValidator, int arraySizeHint, ValueValidator<V> valueValidator) {
+ this.valueConverter = checkNotNull(valueConverter, "valueConverter");
+ this.nameValidator = checkNotNull(nameValidator, "nameValidator");
+- this.hashingStrategy = checkNotNull(nameHashingStrategy, "nameHashingStrategy");
++ hashingStrategy = checkNotNull(nameHashingStrategy, "nameHashingStrategy");
++ this.valueValidator = checkNotNull(valueValidator, "valueValidator");
+ // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is one less
+ // than the length of this array, and we want the mask to be > 0.
+- entries = new DefaultHeaders.HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))];
++ entries = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))];
+ hashMask = (byte) (entries.length - 1);
+ head = new HeaderEntry<K, V>();
+ }
+@@ -197,52 +231,52 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public boolean containsObject(K name, Object value) {
+- return contains(name, valueConverter.convertObject(checkNotNull(value, "value")));
++ return contains(name, fromObject(name, value));
+ }
+
+ @Override
+ public boolean containsBoolean(K name, boolean value) {
+- return contains(name, valueConverter.convertBoolean(value));
++ return contains(name, fromBoolean(name, value));
+ }
+
+ @Override
+ public boolean containsByte(K name, byte value) {
+- return contains(name, valueConverter.convertByte(value));
++ return contains(name, fromByte(name, value));
+ }
+
+ @Override
+ public boolean containsChar(K name, char value) {
+- return contains(name, valueConverter.convertChar(value));
++ return contains(name, fromChar(name, value));
+ }
+
+ @Override
+ public boolean containsShort(K name, short value) {
+- return contains(name, valueConverter.convertShort(value));
++ return contains(name, fromShort(name, value));
+ }
+
+ @Override
+ public boolean containsInt(K name, int value) {
+- return contains(name, valueConverter.convertInt(value));
++ return contains(name, fromInt(name, value));
+ }
+
+ @Override
+ public boolean containsLong(K name, long value) {
+- return contains(name, valueConverter.convertLong(value));
++ return contains(name, fromLong(name, value));
+ }
+
+ @Override
+ public boolean containsFloat(K name, float value) {
+- return contains(name, valueConverter.convertFloat(value));
++ return contains(name, fromFloat(name, value));
+ }
+
+ @Override
+ public boolean containsDouble(K name, double value) {
+- return contains(name, valueConverter.convertDouble(value));
++ return contains(name, fromDouble(name, value));
+ }
+
+ @Override
+ public boolean containsTimeMillis(K name, long value) {
+- return contains(name, valueConverter.convertTimeMillis(value));
++ return contains(name, fromTimeMillis(name, value));
+ }
+
+ @SuppressWarnings("unchecked")
+@@ -292,7 +326,8 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T add(K name, V value) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, true, name);
++ validateValue(valueValidator, name, value);
+ checkNotNull(value, "value");
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+@@ -302,10 +337,11 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T add(K name, Iterable<? extends V> values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, true, name);
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+ for (V v: values) {
++ validateValue(valueValidator, name, v);
+ add0(h, i, name, v);
+ }
+ return thisT();
+@@ -313,10 +349,11 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T add(K name, V... values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, true, name);
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+ for (V v: values) {
++ validateValue(valueValidator, name, v);
+ add0(h, i, name, v);
+ }
+ return thisT();
+@@ -324,7 +361,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T addObject(K name, Object value) {
+- return add(name, valueConverter.convertObject(checkNotNull(value, "value")));
++ return add(name, fromObject(name, value));
+ }
+
+ @Override
+@@ -345,47 +382,47 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T addInt(K name, int value) {
+- return add(name, valueConverter.convertInt(value));
++ return add(name, fromInt(name, value));
+ }
+
+ @Override
+ public T addLong(K name, long value) {
+- return add(name, valueConverter.convertLong(value));
++ return add(name, fromLong(name, value));
+ }
+
+ @Override
+ public T addDouble(K name, double value) {
+- return add(name, valueConverter.convertDouble(value));
++ return add(name, fromDouble(name, value));
+ }
+
+ @Override
+ public T addTimeMillis(K name, long value) {
+- return add(name, valueConverter.convertTimeMillis(value));
++ return add(name, fromTimeMillis(name, value));
+ }
+
+ @Override
+ public T addChar(K name, char value) {
+- return add(name, valueConverter.convertChar(value));
++ return add(name, fromChar(name, value));
+ }
+
+ @Override
+ public T addBoolean(K name, boolean value) {
+- return add(name, valueConverter.convertBoolean(value));
++ return add(name, fromBoolean(name, value));
+ }
+
+ @Override
+ public T addFloat(K name, float value) {
+- return add(name, valueConverter.convertFloat(value));
++ return add(name, fromFloat(name, value));
+ }
+
+ @Override
+ public T addByte(K name, byte value) {
+- return add(name, valueConverter.convertByte(value));
++ return add(name, fromByte(name, value));
+ }
+
+ @Override
+ public T addShort(K name, short value) {
+- return add(name, valueConverter.convertShort(value));
++ return add(name, fromShort(name, value));
+ }
+
+ @Override
+@@ -427,7 +464,8 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T set(K name, V value) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, false, name);
++ validateValue(valueValidator, name, value);
+ checkNotNull(value, "value");
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+@@ -438,7 +476,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T set(K name, Iterable<? extends V> values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, false, name);
+ checkNotNull(values, "values");
+
+ int h = hashingStrategy.hashCode(name);
+@@ -449,6 +487,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ if (v == null) {
+ break;
+ }
++ validateValue(valueValidator, name, v);
+ add0(h, i, name, v);
+ }
+
+@@ -457,7 +496,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T set(K name, V... values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, false, name);
+ checkNotNull(values, "values");
+
+ int h = hashingStrategy.hashCode(name);
+@@ -468,6 +507,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ if (v == null) {
+ break;
+ }
++ validateValue(valueValidator, name, v);
+ add0(h, i, name, v);
+ }
+
+@@ -476,14 +516,13 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T setObject(K name, Object value) {
+- checkNotNull(value, "value");
+- V convertedValue = checkNotNull(valueConverter.convertObject(value), "convertedValue");
++ V convertedValue = checkNotNull(fromObject(name, value), "convertedValue");
+ return set(name, convertedValue);
+ }
+
+ @Override
+ public T setObject(K name, Iterable<?> values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, false, name);
+
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+@@ -493,7 +532,9 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ if (v == null) {
+ break;
+ }
+- add0(h, i, name, valueConverter.convertObject(v));
++ V converted = fromObject(name, v);
++ validateValue(valueValidator, name, converted);
++ add0(h, i, name, converted);
+ }
+
+ return thisT();
+@@ -501,7 +542,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T setObject(K name, Object... values) {
+- nameValidator.validateName(name);
++ validateName(nameValidator, false, name);
+
+ int h = hashingStrategy.hashCode(name);
+ int i = index(h);
+@@ -511,7 +552,9 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ if (v == null) {
+ break;
+ }
+- add0(h, i, name, valueConverter.convertObject(v));
++ V converted = fromObject(name, v);
++ validateValue(valueValidator, name, converted);
++ add0(h, i, name, converted);
+ }
+
+ return thisT();
+@@ -519,47 +562,47 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+
+ @Override
+ public T setInt(K name, int value) {
+- return set(name, valueConverter.convertInt(value));
++ return set(name, fromInt(name, value));
+ }
+
+ @Override
+ public T setLong(K name, long value) {
+- return set(name, valueConverter.convertLong(value));
++ return set(name, fromLong(name, value));
+ }
+
+ @Override
+ public T setDouble(K name, double value) {
+- return set(name, valueConverter.convertDouble(value));
++ return set(name, fromDouble(name, value));
+ }
+
+ @Override
+ public T setTimeMillis(K name, long value) {
+- return set(name, valueConverter.convertTimeMillis(value));
++ return set(name, fromTimeMillis(name, value));
+ }
+
+ @Override
+ public T setFloat(K name, float value) {
+- return set(name, valueConverter.convertFloat(value));
++ return set(name, fromFloat(name, value));
+ }
+
+ @Override
+ public T setChar(K name, char value) {
+- return set(name, valueConverter.convertChar(value));
++ return set(name, fromChar(name, value));
+ }
+
+ @Override
+ public T setBoolean(K name, boolean value) {
+- return set(name, valueConverter.convertBoolean(value));
++ return set(name, fromBoolean(name, value));
+ }
+
+ @Override
+ public T setByte(K name, byte value) {
+- return set(name, valueConverter.convertByte(value));
++ return set(name, fromByte(name, value));
+ }
+
+ @Override
+ public T setShort(K name, short value) {
+- return set(name, valueConverter.convertShort(value));
++ return set(name, fromShort(name, value));
+ }
+
+ @Override
+@@ -604,7 +647,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Boolean getBoolean(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToBoolean(v) : null;
++ return v != null ? toBoolean(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -620,7 +663,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Byte getByte(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToByte(v) : null;
++ return v != null ? toByte(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -636,7 +679,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Character getChar(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToChar(v) : null;
++ return v != null ? toChar(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -652,7 +695,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Short getShort(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToShort(v) : null;
++ return v != null ? toShort(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -668,7 +711,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Integer getInt(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToInt(v) : null;
++ return v != null ? toInt(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -684,7 +727,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Long getLong(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToLong(v) : null;
++ return v != null ? toLong(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -700,7 +743,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Float getFloat(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToFloat(v) : null;
++ return v != null ? toFloat(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -716,7 +759,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Double getDouble(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToDouble(v) : null;
++ return v != null ? toDouble(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -732,7 +775,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Long getTimeMillis(K name) {
+ V v = get(name);
+ try {
+- return v != null ? valueConverter.convertToTimeMillis(v) : null;
++ return v != null ? toTimeMillis(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -748,7 +791,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Boolean getBooleanAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToBoolean(v) : null;
++ return v != null ? toBoolean(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -764,7 +807,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Byte getByteAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToByte(v) : null;
++ return v != null ? toByte(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -780,7 +823,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Character getCharAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToChar(v) : null;
++ return v != null ? toChar(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -796,7 +839,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Short getShortAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToShort(v) : null;
++ return v != null ? toShort(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -812,7 +855,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Integer getIntAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToInt(v) : null;
++ return v != null ? toInt(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -828,7 +871,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Long getLongAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToLong(v) : null;
++ return v != null ? toLong(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -844,7 +887,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Float getFloatAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToFloat(v) : null;
++ return v != null ? toFloat(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -860,7 +903,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Double getDoubleAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToDouble(v) : null;
++ return v != null ? toDouble(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -876,7 +919,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ public Long getTimeMillisAndRemove(K name) {
+ V v = getAndRemove(name);
+ try {
+- return v != null ? valueConverter.convertToTimeMillis(v) : null;
++ return v != null ? toTimeMillis(name, v) : null;
+ } catch (RuntimeException ignore) {
+ return null;
+ }
+@@ -957,6 +1000,22 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ return HeadersUtils.toString(getClass(), iterator(), size());
+ }
+
++ /**
++ * Call out to the given {@link NameValidator} to validate the given name.
++ *
++ * @param validator the validator to use
++ * @param forAdd {@code true } if this validation is for adding to the headers, or {@code false} if this is for
++ * setting (overwriting) the given header.
++ * @param name the name to validate.
++ */
++ protected void validateName(NameValidator<K> validator, boolean forAdd, K name) {
++ validator.validateName(name);
++ }
++
++ protected void validateValue(ValueValidator<V> validator, K name, V value) {
++ validator.validate(value);
++ }
++
+ protected HeaderEntry<K, V> newHeaderEntry(int h, K name, V value, HeaderEntry<K, V> next) {
+ return new HeaderEntry<K, V>(h, name, value, next, head);
+ }
+@@ -965,6 +1024,14 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ return valueConverter;
+ }
+
++ protected NameValidator<K> nameValidator() {
++ return nameValidator;
++ }
++
++ protected ValueValidator<V> valueValidator() {
++ return valueValidator;
++ }
++
+ private int index(int hash) {
+ return hash & hashMask;
+ }
+@@ -1012,12 +1079,22 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ return value;
+ }
+
+- private HeaderEntry<K, V> remove0(HeaderEntry<K, V> entry, HeaderEntry<K, V> previous) {
++ HeaderEntry<K, V> remove0(HeaderEntry<K, V> entry, HeaderEntry<K, V> previous) {
+ int i = index(entry.hash);
+- HeaderEntry<K, V> e = entries[i];
+- if (e == entry) {
++ HeaderEntry<K, V> firstEntry = entries[i];
++ if (firstEntry == entry) {
+ entries[i] = entry.next;
+ previous = entries[i];
++ } else if (previous == null) {
++ // If we don't have any existing starting point, then start from the beginning.
++ previous = firstEntry;
++ HeaderEntry<K, V> next = firstEntry.next;
++ while (next != null && next != entry) {
++ previous = next;
++ next = next.next;
++ }
++ assert next != null: "Entry not found in its hash bucket: " + entry;
++ previous.next = entry.next;
+ } else {
+ previous.next = entry.next;
+ }
+@@ -1031,6 +1108,159 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ return (T) this;
+ }
+
++ private V fromObject(K name, Object value) {
++ try {
++ return valueConverter.convertObject(checkNotNull(value, "value"));
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert object value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromBoolean(K name, boolean value) {
++ try {
++ return valueConverter.convertBoolean(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert boolean value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromByte(K name, byte value) {
++ try {
++ return valueConverter.convertByte(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert byte value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromChar(K name, char value) {
++ try {
++ return valueConverter.convertChar(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert char value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromShort(K name, short value) {
++ try {
++ return valueConverter.convertShort(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert short value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromInt(K name, int value) {
++ try {
++ return valueConverter.convertInt(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert int value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromLong(K name, long value) {
++ try {
++ return valueConverter.convertLong(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert long value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromFloat(K name, float value) {
++ try {
++ return valueConverter.convertFloat(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert float value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromDouble(K name, double value) {
++ try {
++ return valueConverter.convertDouble(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert double value for header '" + name + '\'', e);
++ }
++ }
++
++ private V fromTimeMillis(K name, long value) {
++ try {
++ return valueConverter.convertTimeMillis(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert millsecond value for header '" + name + '\'', e);
++ }
++ }
++
++ private boolean toBoolean(K name, V value) {
++ try {
++ return valueConverter.convertToBoolean(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to boolean for header '" + name + '\'');
++ }
++ }
++
++ private byte toByte(K name, V value) {
++ try {
++ return valueConverter.convertToByte(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to byte for header '" + name + '\'');
++ }
++ }
++
++ private char toChar(K name, V value) {
++ try {
++ return valueConverter.convertToChar(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to char for header '" + name + '\'');
++ }
++ }
++
++ private short toShort(K name, V value) {
++ try {
++ return valueConverter.convertToShort(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to short for header '" + name + '\'');
++ }
++ }
++
++ private int toInt(K name, V value) {
++ try {
++ return valueConverter.convertToInt(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to int for header '" + name + '\'');
++ }
++ }
++
++ private long toLong(K name, V value) {
++ try {
++ return valueConverter.convertToLong(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to long for header '" + name + '\'');
++ }
++ }
++
++ private float toFloat(K name, V value) {
++ try {
++ return valueConverter.convertToFloat(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to float for header '" + name + '\'');
++ }
++ }
++
++ private double toDouble(K name, V value) {
++ try {
++ return valueConverter.convertToDouble(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException("Failed to convert header value to double for header '" + name + '\'');
++ }
++ }
++
++ private long toTimeMillis(K name, V value) {
++ try {
++ return valueConverter.convertToTimeMillis(value);
++ } catch (IllegalArgumentException e) {
++ throw new IllegalArgumentException(
++ "Failed to convert header value to millsecond for header '" + name + '\'');
++ }
++ }
++
+ /**
+ * Returns a deep copy of this instance.
+ */
+@@ -1041,7 +1271,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ return copy;
+ }
+
+- private final class HeaderIterator implements Iterator<Map.Entry<K, V>> {
++ private final class HeaderIterator implements Iterator<Entry<K, V>> {
+ private HeaderEntry<K, V> current = head;
+
+ @Override
+@@ -1118,7 +1348,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ }
+ }
+
+- protected static class HeaderEntry<K, V> implements Map.Entry<K, V> {
++ protected static class HeaderEntry<K, V> implements Entry<K, V> {
+ protected final int hash;
+ protected final K key;
+ protected V value;
+@@ -1199,7 +1429,7 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+- Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
++ Entry<?, ?> other = (Entry<?, ?>) o;
+ return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) &&
+ (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));
+ }
=====================================
debian/patches/series
=====================================
@@ -16,3 +16,8 @@ CVE-2021-21290.patch
CVE-2021-21295.patch
CVE-2021-21409.patch
21-java-17.patch
+CVE-2021-37136.patch
+CVE-2021-37137.patch
+CVE-2021-43797.patch
+CVE-2022-41881.patch
+CVE-2022-41915.patch
View it on GitLab: https://salsa.debian.org/java-team/netty/-/commit/5406c2524b94d1268459607b284b2a8c366fae53
--
View it on GitLab: https://salsa.debian.org/java-team/netty/-/commit/5406c2524b94d1268459607b284b2a8c366fae53
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/20230101/98273625/attachment.htm>
More information about the pkg-java-commits
mailing list