Version in base suite: 4.1.48-4 Base version: netty_4.1.48-4 Target version: netty_4.1.48-4+deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/netty/netty_4.1.48-4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/netty/netty_4.1.48-4+deb11u1.dsc changelog | 11 copyright | 4 patches/CVE-2021-37136.patch | 80 +++ patches/CVE-2021-37137.patch | 183 +++++++++ patches/CVE-2021-43797.patch | 298 ++++++++++++++ patches/CVE-2022-41881.patch | 150 +++++++ patches/CVE-2022-41915.patch | 866 +++++++++++++++++++++++++++++++++++++++++++ patches/series | 5 8 files changed, 1595 insertions(+), 2 deletions(-) diff -Nru netty-4.1.48/debian/changelog netty-4.1.48/debian/changelog --- netty-4.1.48/debian/changelog 2021-04-01 05:01:52.000000000 +0000 +++ netty-4.1.48/debian/changelog 2023-01-01 18:17:21.000000000 +0000 @@ -1,3 +1,14 @@ +netty (1:4.1.48-4+deb11u1) bullseye-security; 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 Sun, 01 Jan 2023 19:17:21 +0100 + netty (1:4.1.48-4) unstable; urgency=high * Team upload. diff -Nru netty-4.1.48/debian/copyright netty-4.1.48/debian/copyright --- netty-4.1.48/debian/copyright 2021-04-01 05:01:52.000000000 +0000 +++ netty-4.1.48/debian/copyright 2023-01-01 18:17:21.000000000 +0000 @@ -127,8 +127,8 @@ Files: debian/* Copyright: 2009, Thierry Carrez 2011, Damien Raude-Morvan - 2015-2018, Emmanuel Bourg - 2016-2017, tony mancill + 2015-2022, Emmanuel Bourg + 2016-2022, tony mancill License: Apache-2.0 License: Apache-2.0 diff -Nru netty-4.1.48/debian/patches/CVE-2021-37136.patch netty-4.1.48/debian/patches/CVE-2021-37136.patch --- netty-4.1.48/debian/patches/CVE-2021-37136.patch 1970-01-01 00:00:00.000000000 +0000 +++ netty-4.1.48/debian/patches/CVE-2021-37136.patch 2023-01-01 18:17:21.000000000 +0000 @@ -0,0 +1,80 @@ +From: Markus Koschany +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; diff -Nru netty-4.1.48/debian/patches/CVE-2021-37137.patch netty-4.1.48/debian/patches/CVE-2021-37137.patch --- netty-4.1.48/debian/patches/CVE-2021-37137.patch 1970-01-01 00:00:00.000000000 +0000 +++ netty-4.1.48/debian/patches/CVE-2021-37137.patch 2023-01-01 18:17:21.000000000 +0000 @@ -0,0 +1,183 @@ +From: Markus Koschany +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(); diff -Nru netty-4.1.48/debian/patches/CVE-2021-43797.patch netty-4.1.48/debian/patches/CVE-2021-43797.patch --- netty-4.1.48/debian/patches/CVE-2021-43797.patch 1970-01-01 00:00:00.000000000 +0000 +++ netty-4.1.48/debian/patches/CVE-2021-43797.patch 2023-01-01 18:17:21.000000000 +0000 @@ -0,0 +1,298 @@ +From: Markus Koschany +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()); ++ } + } diff -Nru netty-4.1.48/debian/patches/CVE-2022-41881.patch netty-4.1.48/debian/patches/CVE-2022-41881.patch --- netty-4.1.48/debian/patches/CVE-2022-41881.patch 1970-01-01 00:00:00.000000000 +0000 +++ netty-4.1.48/debian/patches/CVE-2022-41881.patch 2023-01-01 18:17:21.000000000 +0000 @@ -0,0 +1,150 @@ +From: Markus Koschany +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 leakDetector = + ResourceLeakDetectorFactory.instance().newResourceLeakDetector(HAProxyMessage.class); + +@@ -223,7 +226,7 @@ public final class HAProxyMessage extends AbstractReferenceCounted { + } + + private static List 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 encapsulatedTlvs = new ArrayList(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); ++ } ++ }); ++ } + } diff -Nru netty-4.1.48/debian/patches/CVE-2022-41915.patch netty-4.1.48/debian/patches/CVE-2022-41915.patch --- netty-4.1.48/debian/patches/CVE-2022-41915.patch 1970-01-01 00:00:00.000000000 +0000 +++ netty-4.1.48/debian/patches/CVE-2022-41915.patch 2023-01-01 18:17:21.000000000 +0000 @@ -0,0 +1,866 @@ +From: Markus Koschany +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> implements Headers + private final byte hashMask; + private final ValueConverter valueConverter; + private final NameValidator nameValidator; ++ private final ValueValidator valueValidator; + private final HashingStrategy hashingStrategy; + int size; + +@@ -72,6 +73,22 @@ public class DefaultHeaders> implements Headers + }; + } + ++ public interface ValueValidator { ++ /** ++ * 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() { ++ @Override ++ public void validate(Object value) { ++ } ++ }; ++ } ++ + @SuppressWarnings("unchecked") + public DefaultHeaders(ValueConverter valueConverter) { + this(JAVA_HASHER, valueConverter); +@@ -102,13 +119,30 @@ public class DefaultHeaders> implements Headers + */ + @SuppressWarnings("unchecked") + public DefaultHeaders(HashingStrategy nameHashingStrategy, +- ValueConverter valueConverter, NameValidator nameValidator, int arraySizeHint) { ++ ValueConverter valueConverter, NameValidator nameValidator, int arraySizeHint) { ++ this(nameHashingStrategy, valueConverter, nameValidator, arraySizeHint, ++ (ValueValidator) 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 nameHashingStrategy, ValueConverter valueConverter, ++ NameValidator nameValidator, int arraySizeHint, ValueValidator 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(); + } +@@ -197,52 +231,52 @@ public class DefaultHeaders> 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> 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> implements Headers + + @Override + public T add(K name, Iterable 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> 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> 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> 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> 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> implements Headers + + @Override + public T set(K name, Iterable values) { +- nameValidator.validateName(name); ++ validateName(nameValidator, false, name); + checkNotNull(values, "values"); + + int h = hashingStrategy.hashCode(name); +@@ -449,6 +487,7 @@ public class DefaultHeaders> implements Headers + if (v == null) { + break; + } ++ validateValue(valueValidator, name, v); + add0(h, i, name, v); + } + +@@ -457,7 +496,7 @@ public class DefaultHeaders> 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> implements Headers + if (v == null) { + break; + } ++ validateValue(valueValidator, name, v); + add0(h, i, name, v); + } + +@@ -476,14 +516,13 @@ public class DefaultHeaders> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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 validator, boolean forAdd, K name) { ++ validator.validateName(name); ++ } ++ ++ protected void validateValue(ValueValidator validator, K name, V value) { ++ validator.validate(value); ++ } ++ + protected HeaderEntry newHeaderEntry(int h, K name, V value, HeaderEntry next) { + return new HeaderEntry(h, name, value, next, head); + } +@@ -965,6 +1024,14 @@ public class DefaultHeaders> implements Headers + return valueConverter; + } + ++ protected NameValidator nameValidator() { ++ return nameValidator; ++ } ++ ++ protected ValueValidator valueValidator() { ++ return valueValidator; ++ } ++ + private int index(int hash) { + return hash & hashMask; + } +@@ -1012,12 +1079,22 @@ public class DefaultHeaders> implements Headers + return value; + } + +- private HeaderEntry remove0(HeaderEntry entry, HeaderEntry previous) { ++ HeaderEntry remove0(HeaderEntry entry, HeaderEntry previous) { + int i = index(entry.hash); +- HeaderEntry e = entries[i]; +- if (e == entry) { ++ HeaderEntry 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 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> 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> implements Headers + return copy; + } + +- private final class HeaderIterator implements Iterator> { ++ private final class HeaderIterator implements Iterator> { + private HeaderEntry current = head; + + @Override +@@ -1118,7 +1348,7 @@ public class DefaultHeaders> implements Headers + } + } + +- protected static class HeaderEntry implements Map.Entry { ++ protected static class HeaderEntry implements Entry { + protected final int hash; + protected final K key; + protected V value; +@@ -1199,7 +1429,7 @@ public class DefaultHeaders> 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())); + } diff -Nru netty-4.1.48/debian/patches/series netty-4.1.48/debian/patches/series --- netty-4.1.48/debian/patches/series 2021-04-01 05:01:52.000000000 +0000 +++ netty-4.1.48/debian/patches/series 2023-01-01 18:17:21.000000000 +0000 @@ -15,3 +15,8 @@ CVE-2021-21290.patch CVE-2021-21295.patch CVE-2021-21409.patch +CVE-2021-37136.patch +CVE-2021-37137.patch +CVE-2021-43797.patch +CVE-2022-41881.patch +CVE-2022-41915.patch