[Git][java-team/netty][master] 3 commits: Fix CVE-2021-21295

Markus Koschany gitlab at salsa.debian.org
Fri Mar 26 22:31:45 GMT 2021



Markus Koschany pushed to branch master at Debian Java Maintainers / netty


Commits:
224b8b5c by Markus Koschany at 2021-03-26T13:36:08+01:00
Fix CVE-2021-21295

- - - - -
2110fec9 by Markus Koschany at 2021-03-26T13:38:29+01:00
Updae changelog

- - - - -
200e9921 by Markus Koschany at 2021-03-26T23:25:32+01:00
Update CVE-2021-21295.patch

- - - - -


3 changed files:

- debian/changelog
- + debian/patches/CVE-2021-21295.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,19 @@
+netty (1:4.1.48-3) unstable; urgency=high
+
+  * Team upload.
+  * Fix CVE-2021-21295:
+    There is a vulnerability that enables request smuggling. If a
+    Content-Length header is present in the original HTTP/2 request, the field
+    is not validated by `Http2MultiplexHandler` as it is propagated up. This is
+    fine as long as the request is not proxied through as HTTP/1.1. If the
+    request comes in as an HTTP/2 stream, gets converted into the HTTP/1.1
+    domain objects (`HttpRequest`, `HttpContent`, etc.) via
+    `Http2StreamFrameToHttpObjectCodec `and then sent up to the child channel's
+    pipeline and proxied through a remote peer as HTTP/1.1 this may result in
+    request smuggling. (Closes: #984948)
+
+ -- Markus Koschany <apo at debian.org>  Fri, 26 Mar 2021 13:37:15 +0100
+
 netty (1:4.1.48-2) unstable; urgency=high
 
   * Team upload.


=====================================
debian/patches/CVE-2021-21295.patch
=====================================
@@ -0,0 +1,553 @@
+From: Markus Koschany <apo at debian.org>
+Date: Fri, 26 Mar 2021 13:30:44 +0100
+Subject: CVE-2021-21295
+
+Origin: https://github.com/netty/netty/commit/89c241e3b1795ff257af4ad6eadc616cb2fb3dc4
+---
+ .../handler/codec/http/HttpObjectDecoder.java      |  44 +++----
+ .../java/io/netty/handler/codec/http/HttpUtil.java |  86 ++++++++++++++
+ .../codec/http2/DefaultHttp2ConnectionDecoder.java | 100 ++++++++++++++--
+ .../http2/DefaultHttp2ConnectionDecoderTest.java   | 128 +++++++++++++++++++++
+ 4 files changed, 329 insertions(+), 29 deletions(-)
+
+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 4134735..1384df7 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
+@@ -102,11 +102,13 @@ import java.util.List;
+  * implement all abstract methods properly.
+  */
+ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
++    public static final boolean DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS = false;
+     private static final String EMPTY_VALUE = "";
+ 
+     private final int maxChunkSize;
+     private final boolean chunkedSupported;
+     protected final boolean validateHeaders;
++    private final boolean allowDuplicateContentLengths;
+     private final HeaderParser headerParser;
+     private final LineParser lineParser;
+ 
+@@ -170,6 +172,14 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+     protected HttpObjectDecoder(
+             int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
+             boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) {
++        this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders, initialBufferSize,
++             DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS);
++    }
++
++    protected HttpObjectDecoder(
++            int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
++            boolean chunkedSupported, boolean validateHeaders, int initialBufferSize,
++            boolean allowDuplicateContentLengths) {
+         checkPositive(maxInitialLineLength, "maxInitialLineLength");
+         checkPositive(maxHeaderSize, "maxHeaderSize");
+         checkPositive(maxChunkSize, "maxChunkSize");
+@@ -180,6 +190,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+         this.maxChunkSize = maxChunkSize;
+         this.chunkedSupported = chunkedSupported;
+         this.validateHeaders = validateHeaders;
++        this.allowDuplicateContentLengths = allowDuplicateContentLengths;
+     }
+ 
+     @Override
+@@ -585,34 +596,27 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
+         name = null;
+         value = null;
+ 
+-        List<String> values = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
+-        int contentLengthValuesCount = values.size();
++        List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
+ 
+-        if (contentLengthValuesCount > 0) {
++        if (!contentLengthFields.isEmpty()) {
++            HttpVersion version = message.protocolVersion();
++            boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1
++                    && version.minorVersion() == 0);
+             // Guard against multiple Content-Length headers as stated in
+             // https://tools.ietf.org/html/rfc7230#section-3.3.2:
+-            //
+-            // If a message is received that has multiple Content-Length header
+-            //   fields with field-values consisting of the same decimal value, or a
+-            //   single Content-Length header field with a field value containing a
+-            //   list of identical decimal values (e.g., "Content-Length: 42, 42"),
+-            //   indicating that duplicate Content-Length header fields have been
+-            //   generated or combined by an upstream message processor, then the
+-            //   recipient MUST either reject the message as invalid or replace the
+-            //   duplicated field-values with a single valid Content-Length field
+-            //   containing that decimal value prior to determining the message body
+-            //   length or forwarding the message.
+-            if (contentLengthValuesCount > 1 && message.protocolVersion() == HttpVersion.HTTP_1_1) {
+-                throw new IllegalArgumentException("Multiple Content-Length headers found");
+-            }
+-            contentLength = Long.parseLong(values.get(0));
+-        }
++
++            contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields,
++                    isHttp10OrEarlier, allowDuplicateContentLengths);
++            if (contentLength != -1) {
++                headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength);
++             }
++         }
+ 
+         if (isContentAlwaysEmpty(message)) {
+             HttpUtil.setTransferEncodingChunked(message, false);
+             return State.SKIP_CONTROL_CHARS;
+         } else if (HttpUtil.isTransferEncodingChunked(message)) {
+-            if (contentLengthValuesCount > 0 && message.protocolVersion() == HttpVersion.HTTP_1_1) {
++            if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
+                 handleTransferEncodingChunkedWithContentLength(message);
+             }
+             return State.READ_CHUNK_SIZE;
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
+index 31fd14d..afa3ec4 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
+@@ -24,10 +24,14 @@ import java.util.ArrayList;
+ import java.util.Iterator;
+ import java.util.List;
+ 
++import io.netty.handler.codec.Headers;
+ import io.netty.util.AsciiString;
+ import io.netty.util.CharsetUtil;
+ import io.netty.util.NetUtil;
+ import io.netty.util.internal.ObjectUtil;
++import io.netty.util.internal.UnstableApi;
++
++import static io.netty.util.internal.StringUtil.COMMA;
+ 
+ /**
+  * Utility methods useful in the HTTP context.
+@@ -36,6 +40,7 @@ public final class HttpUtil {
+ 
+     private static final AsciiString CHARSET_EQUALS = AsciiString.of(HttpHeaderValues.CHARSET + "=");
+     private static final AsciiString SEMICOLON = AsciiString.cached(";");
++    private static final String COMMA_STRING = String.valueOf(COMMA);
+ 
+     private HttpUtil() { }
+ 
+@@ -530,4 +535,85 @@ public final class HttpUtil {
+         }
+         return hostString;
+     }
++
++    /**
++     * Validates, and optionally extracts the content length from headers. This method is not intended for
++     * general use, but is here to be shared between HTTP/1 and HTTP/2 parsing.
++     *
++     * @param contentLengthFields the content-length header fields.
++     * @param isHttp10OrEarlier {@code true} if we are handling HTTP/1.0 or earlier
++     * @param allowDuplicateContentLengths {@code true}  if multiple, identical-value content lengths should be allowed.
++     * @return the normalized content length from the headers or {@code -1} if the fields were empty.
++     * @throws IllegalArgumentException if the content-length fields are not valid
++     */
++    @UnstableApi
++    public static long normalizeAndGetContentLength(
++            List<? extends CharSequence> contentLengthFields, boolean isHttp10OrEarlier,
++            boolean allowDuplicateContentLengths) {
++        if (contentLengthFields.isEmpty()) {
++            return -1;
++        }
++
++        // Guard against multiple Content-Length headers as stated in
++        // https://tools.ietf.org/html/rfc7230#section-3.3.2:
++        //
++        // If a message is received that has multiple Content-Length header
++        //   fields with field-values consisting of the same decimal value, or a
++        //   single Content-Length header field with a field value containing a
++        //   list of identical decimal values (e.g., "Content-Length: 42, 42"),
++        //   indicating that duplicate Content-Length header fields have been
++        //   generated or combined by an upstream message processor, then the
++        //   recipient MUST either reject the message as invalid or replace the
++        //   duplicated field-values with a single valid Content-Length field
++        //   containing that decimal value prior to determining the message body
++        //   length or forwarding the message.
++        String firstField = contentLengthFields.get(0).toString();
++        boolean multipleContentLengths =
++                contentLengthFields.size() > 1 || firstField.indexOf(COMMA) >= 0;
++
++        if (multipleContentLengths && !isHttp10OrEarlier) {
++            if (allowDuplicateContentLengths) {
++                // Find and enforce that all Content-Length values are the same
++                String firstValue = null;
++                for (CharSequence field : contentLengthFields) {
++                    String[] tokens = field.toString().split(COMMA_STRING, -1);
++                    for (String token : tokens) {
++                        String trimmed = token.trim();
++                        if (firstValue == null) {
++                            firstValue = trimmed;
++                        } else if (!trimmed.equals(firstValue)) {
++                            throw new IllegalArgumentException(
++                                    "Multiple Content-Length values found: " + contentLengthFields);
++                        }
++                    }
++                }
++                // Replace the duplicated field-values with a single valid Content-Length field
++                firstField = firstValue;
++            } else {
++                // Reject the message as invalid
++                throw new IllegalArgumentException(
++                        "Multiple Content-Length values found: " + contentLengthFields);
++            }
++        }
++        // Ensure we not allow sign as part of the content-length:
++        // See https://github.com/squid-cache/squid/security/advisories/GHSA-qf3v-rc95-96j5
++        if (!Character.isDigit(firstField.charAt(0))) {
++            // Reject the message as invalid
++            throw new IllegalArgumentException(
++                    "Content-Length value is not a number: " + firstField);
++        }
++        try {
++            final long value = Long.parseLong(firstField);
++            if (value < 0) {
++                // Reject the message as invalid
++                throw new IllegalArgumentException(
++                        "Content-Length value must be >=0: " + value);
++            }
++            return value;
++        } catch (NumberFormatException e) {
++            // Reject the message as invalid
++            throw new IllegalArgumentException(
++                    "Content-Length value is not a number: " + firstField, e);
++        }
++    }
+ }
+diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
+index 10da347..5b6e3f7 100644
+--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
++++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
+@@ -16,8 +16,11 @@ package io.netty.handler.codec.http2;
+ 
+ import io.netty.buffer.ByteBuf;
+ import io.netty.channel.ChannelHandlerContext;
++import io.netty.handler.codec.http.HttpHeaderNames;
+ import io.netty.handler.codec.http.HttpStatusClass;
++import io.netty.handler.codec.http.HttpUtil;
+ import io.netty.handler.codec.http2.Http2Connection.Endpoint;
++import io.netty.util.internal.SystemPropertyUtil;
+ import io.netty.util.internal.UnstableApi;
+ import io.netty.util.internal.logging.InternalLogger;
+ import io.netty.util.internal.logging.InternalLoggerFactory;
+@@ -49,6 +52,8 @@ import static java.lang.Math.min;
+  */
+ @UnstableApi
+ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
++    private static final boolean VALIDATE_CONTENT_LENGTH =
++            SystemPropertyUtil.getBoolean("io.netty.http2.validateContentLength", true);
+     private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2ConnectionDecoder.class);
+     private Http2FrameListener internalFrameListener = new PrefaceFrameListener();
+     private final Http2Connection connection;
+@@ -59,6 +64,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+     private final Http2PromisedRequestVerifier requestVerifier;
+     private final Http2SettingsReceivedConsumer settingsReceivedConsumer;
+     private final boolean autoAckPing;
++    private final Http2Connection.PropertyKey contentLengthKey;
+ 
+     public DefaultHttp2ConnectionDecoder(Http2Connection connection,
+                                          Http2ConnectionEncoder encoder,
+@@ -125,6 +131,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+             settingsReceivedConsumer = (Http2SettingsReceivedConsumer) encoder;
+         }
+         this.connection = checkNotNull(connection, "connection");
++        contentLengthKey = this.connection.newKey();
+         this.frameReader = checkNotNull(frameReader, "frameReader");
+         this.encoder = checkNotNull(encoder, "encoder");
+         this.requestVerifier = checkNotNull(requestVerifier, "requestVerifier");
+@@ -223,6 +230,23 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+         listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
+     }
+ 
++    // See https://tools.ietf.org/html/rfc7540#section-8.1.2.6
++    private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception {
++        if (!VALIDATE_CONTENT_LENGTH) {
++            return;
++        }
++        ContentLength contentLength = stream.getProperty(contentLengthKey);
++        if (contentLength != null) {
++            try {
++                contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd);
++            } finally {
++                if (isEnd) {
++                    stream.removeProperty(contentLengthKey);
++                }
++            }
++        }
++    }
++
+     /**
+      * Handles all inbound frames from the network.
+      */
+@@ -232,7 +256,8 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+                               boolean endOfStream) throws Http2Exception {
+             Http2Stream stream = connection.stream(streamId);
+             Http2LocalFlowController flowController = flowController();
+-            int bytesToReturn = data.readableBytes() + padding;
++            int readable = data.readableBytes();
++            int bytesToReturn = readable + padding;
+ 
+             final boolean shouldIgnore;
+             try {
+@@ -259,7 +284,6 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+                 // All bytes have been consumed.
+                 return bytesToReturn;
+             }
+-
+             Http2Exception error = null;
+             switch (stream.state()) {
+                 case OPEN:
+@@ -287,6 +311,8 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+                     throw error;
+                 }
+ 
++                verifyContentLength(stream, readable, endOfStream);
++
+                 // Call back the application and retrieve the number of bytes that have been
+                 // immediately processed.
+                 bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream);
+@@ -367,14 +393,34 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+                             stream.state());
+             }
+ 
+-            stream.headersReceived(isInformational);
+-            encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
+-
+-            listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream);
++            if (!stream.isHeadersReceived()) {
++                // extract the content-length header
++                List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
++                if (contentLength != null && !contentLength.isEmpty()) {
++                    try {
++                        long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true);
++                        if (cLength != -1) {
++                            headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength);
++                            stream.setProperty(contentLengthKey, new ContentLength(cLength));
++                        }
++                    } catch (IllegalArgumentException e) {
++                        throw streamError(stream.id(), PROTOCOL_ERROR,
++                                "Multiple content-length headers received", e);
++                    }
++                }
++            }
+ 
+-            // If the headers completes this stream, close it.
+-            if (endOfStream) {
+-                lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
++            stream.headersReceived(isInformational);
++            try {
++                verifyContentLength(stream, 0, endOfStream);
++                encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive);
++                listener.onHeadersRead(ctx, streamId, headers, streamDependency,
++                        weight, exclusive, padding, endOfStream);
++            } finally {
++                // If the headers completes this stream, close it.
++                if (endOfStream) {
++                    lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture());
++                }
+             }
+         }
+ 
+@@ -740,4 +786,40 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
+             onUnknownFrame0(ctx, frameType, streamId, flags, payload);
+         }
+     }
++
++    private static final class ContentLength {
++        private final long expected;
++        private long seen;
++
++        ContentLength(long expected) {
++            this.expected = expected;
++        }
++
++        void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception {
++            seen += bytes;
++            // Check for overflow
++            if (seen < 0) {
++                throw streamError(streamId, PROTOCOL_ERROR,
++                        "Received amount of data did overflow and so not match content-length header %d", expected);
++            }
++            // Check if we received more data then what was advertised via the content-length header.
++            if (seen > expected) {
++                throw streamError(streamId, PROTOCOL_ERROR,
++                        "Received amount of data %d does not match content-length header %d", seen, expected);
++            }
++
++            if (isEnd) {
++                if (seen == 0 && !server) {
++                    // This may be a response to a HEAD request, let's just allow it.
++                    return;
++                }
++
++                // Check that we really saw what was told via the content-length header.
++                if (expected > seen) {
++                    throw streamError(streamId, PROTOCOL_ERROR,
++                            "Received amount of data %d does not match content-length header %d", seen, expected);
++                }
++            }
++        }
++    }
+ }
+diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
+index 9eb66bc..c1bcc33 100644
+--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
++++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
+@@ -21,17 +21,21 @@ import io.netty.channel.ChannelFuture;
+ import io.netty.channel.ChannelHandlerContext;
+ import io.netty.channel.ChannelPromise;
+ import io.netty.channel.DefaultChannelPromise;
++import io.netty.handler.codec.http.HttpHeaderNames;
+ import io.netty.handler.codec.http.HttpResponseStatus;
+ import junit.framework.AssertionFailedError;
+ import org.junit.Before;
+ import org.junit.Test;
+ import org.mockito.ArgumentCaptor;
++import org.mockito.ArgumentMatchers;
+ import org.mockito.Mock;
+ import org.mockito.MockitoAnnotations;
+ import org.mockito.invocation.InvocationOnMock;
+ import org.mockito.stubbing.Answer;
+ 
+ import java.util.Collections;
++import java.util.IdentityHashMap;
++import java.util.Map;
+ import java.util.concurrent.atomic.AtomicInteger;
+ 
+ import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
+@@ -133,6 +137,21 @@ public class DefaultHttp2ConnectionDecoderTest {
+         when(stream.id()).thenReturn(STREAM_ID);
+         when(stream.state()).thenReturn(OPEN);
+         when(stream.open(anyBoolean())).thenReturn(stream);
++
++        final Map<Object, Object> properties = new IdentityHashMap<Object, Object>();
++        when(stream.getProperty(ArgumentMatchers.<Http2Connection.PropertyKey>any())).thenAnswer(new Answer<Object>() {
++            @Override
++            public Object answer(InvocationOnMock invocationOnMock) {
++                return properties.get(invocationOnMock.getArgument(0));
++            }
++        });
++        when(stream.setProperty(ArgumentMatchers.<Http2Connection.PropertyKey>any(), any())).then(new Answer<Object>() {
++            @Override
++            public Object answer(InvocationOnMock invocationOnMock) {
++                return properties.put(invocationOnMock.getArgument(0), invocationOnMock.getArgument(1));
++            }
++        });
++
+         when(pushStream.id()).thenReturn(PUSH_STREAM_ID);
+         doAnswer(new Answer<Boolean>() {
+             @Override
+@@ -773,6 +792,115 @@ public class DefaultHttp2ConnectionDecoderTest {
+         verify(listener).onGoAwayRead(eq(ctx), eq(1), eq(2L), eq(EMPTY_BUFFER));
+     }
+ 
++    @Test(expected = Http2Exception.StreamException.class)
++    public void dataContentLengthMissmatch() throws Exception {
++        dataContentLengthInvalid(false);
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void dataContentLengthInvalid() throws Exception {
++        dataContentLengthInvalid(true);
++    }
++
++    private void dataContentLengthInvalid(boolean negative) throws Exception {
++        final ByteBuf data = dummyData();
++        int padding = 10;
++        int processedBytes = data.readableBytes() + padding;
++        mockFlowControl(processedBytes);
++        try {
++            decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers()
++                    .setLong(HttpHeaderNames.CONTENT_LENGTH, negative ? -1L : 1L), padding, false);
++            decode().onDataRead(ctx, STREAM_ID, data, padding, true);
++            verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true));
++            verify(localFlow).consumeBytes(eq(stream), eq(processedBytes));
++
++            verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(),
++                    any(Http2Headers.class), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false),
++                    eq(padding), eq(false));
++            // Verify that the event was absorbed and not propagated to the observer.
++            verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean());
++        } finally {
++            data.release();
++        }
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void headersContentLengthPositiveSign() throws Exception {
++        headersContentLengthSign("+1");
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void headersContentLengthNegativeSign() throws Exception {
++        headersContentLengthSign("-1");
++    }
++
++    private void headersContentLengthSign(String length) throws Exception {
++        int padding = 10;
++        when(connection.isServer()).thenReturn(true);
++        decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers()
++                .set(HttpHeaderNames.CONTENT_LENGTH, length), padding, false);
++
++        // Verify that the event was absorbed and not propagated to the observer.
++        verify(listener, never()).onHeadersRead(eq(ctx), anyInt(),
++                any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean());
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void headersContentLengthMissmatch() throws Exception {
++        headersContentLength(false);
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void headersContentLengthInvalid() throws Exception {
++        headersContentLength(true);
++    }
++
++    private void headersContentLength(boolean negative) throws Exception {
++        int padding = 10;
++        when(connection.isServer()).thenReturn(true);
++        decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers()
++                .setLong(HttpHeaderNames.CONTENT_LENGTH, negative ? -1L : 1L), padding, true);
++
++        // Verify that the event was absorbed and not propagated to the observer.
++        verify(listener, never()).onHeadersRead(eq(ctx), anyInt(),
++                any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean());
++    }
++
++    @Test
++    public void multipleHeadersContentLengthSame() throws Exception {
++        multipleHeadersContentLength(true);
++    }
++
++    @Test(expected = Http2Exception.StreamException.class)
++    public void multipleHeadersContentLengthDifferent() throws Exception {
++        multipleHeadersContentLength(false);
++    }
++
++    private void multipleHeadersContentLength(boolean same) throws Exception {
++        int padding = 10;
++        when(connection.isServer()).thenReturn(true);
++        Http2Headers headers = new DefaultHttp2Headers();
++        if (same) {
++            headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0);
++            headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0);
++        } else {
++            headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0);
++            headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 1);
++        }
++
++        decode().onHeadersRead(ctx, STREAM_ID, headers, padding, true);
++
++        if (same) {
++            verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(),
++                    any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean());
++            assertEquals(1, headers.getAll(HttpHeaderNames.CONTENT_LENGTH).size());
++        } else {
++            // Verify that the event was absorbed and not propagated to the observer.
++            verify(listener, never()).onHeadersRead(eq(ctx), anyInt(),
++                    any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean());
++        }
++    }
++
+     private static ByteBuf dummyData() {
+         // The buffer is purposely 8 bytes so it will even work for a ping frame.
+         return wrappedBuffer("abcdefgh".getBytes(UTF_8));


=====================================
debian/patches/series
=====================================
@@ -13,3 +13,4 @@
 16-disable-substratevm-support.patch
 17-disable-blockhound-integration.patch
 CVE-2021-21290.patch
+CVE-2021-21295.patch



View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/06a357352ad77107bc679e971dfd715abc3d95cc...200e9921ff56d59d1871b02a04b7099c60d0de6c

-- 
View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/06a357352ad77107bc679e971dfd715abc3d95cc...200e9921ff56d59d1871b02a04b7099c60d0de6c
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/20210326/41da6d1f/attachment.htm>


More information about the pkg-java-commits mailing list