CVE-2023-34462 (Java, published 2023-06-22T23:15Z)
Description: value: Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. The `SniHandler` can allocate up to 16MB of heap for each channel during the TLS handshake. When the handler or the channel does not have an idle timeout, it can be used to make a TCP server using the `SniHandler` to allocate 16MB of heap. The `SniHandler` class is a handler that waits for the TLS handshake to configure a `SslHandler` according to the indicated server name by the `ClientHello` record. For this matter it allocates a `ByteBuf` using the value defined in the `ClientHello` record. Normally the value of the packet should be smaller than the handshake packet but there are not checks done here and the way the code is written, it is possible to craft a packet that makes the `SslClientHelloHandler`. This vulnerability has been fixed in version 4.1.94.Final.
Severity: nan (CVSS v3 base score: 6.5)
Weakness: CWE-400 \u2014 Uncontrolled Resource Consumption
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
Repository: https://github.com/netty/netty
Files changed:
handler/src/main/java/io/netty/handler/ssl/AbstractSniHandler.java
handler/src/main/java/io/netty/handler/ssl/SniHandler.java
handler/src/main/java/io/netty/handler/ssl/SslClientHelloHandler.java
handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java
Commit message: Merge pull request from GHSA-6mjq-h674-j845

Motivation:

In theory the ClientHello can span multiple records and so reach the length of 16MB. This can result in high memory usage, we should allow the user to define a maximum length.

Modifications:

Add new constructor which allows to limit the maximum length of a ClientHello message.

Result:

Be able to guard against high memory usage when parsing ClientHello messages

Vulnerable code:
     * @param handshakeTimeoutMillis the handshake timeout in milliseconds
        this(0L);
    public SniHandler(Mapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        this(new AsyncMappingAdapter(mapping), handshakeTimeoutMillis);
        this(mapping, 0L);
    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        super(handshakeTimeoutMillis);
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.SSLContext;
        }, 10);
        }, 100);

Fixed code:
     * @param handshakeTimeoutMillis    the handshake timeout in milliseconds
        this(0, handshakeTimeoutMillis);
    }

    /**
     * @paramm maxClientHelloLength     the maximum length of the client hello message.
     * @param handshakeTimeoutMillis    the handshake timeout in milliseconds
     */
    protected AbstractSniHandler(int maxClientHelloLength, long handshakeTimeoutMillis) {
        super(maxClientHelloLength);
        this(0, 0L);
     * @param maxClientHelloLength the maximum length of the client hello message
    public SniHandler(Mapping<? super String, ? extends SslContext> mapping,
                      int maxClientHelloLength, long handshakeTimeoutMillis) {
        this(new AsyncMappingAdapter(mapping), maxClientHelloLength, handshakeTimeoutMillis);
        this(mapping, 0, 0L);
     * @param maxClientHelloLength the maximum length of the client hello message
    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping,
                      int maxClientHelloLength, long handshakeTimeoutMillis) {
        super(maxClientHelloLength, handshakeTimeoutMillis);
    /**
     * Creates a SNI detection handler with configured {@link SslContext}
     * maintained by {@link Mapping}
     *
     * @param mapping the mapping of domain name to {@link SslContext}
     * @param handshakeTimeoutMillis the handshake timeout in milliseconds
     */
    public SniHandler(Mapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        this(new AsyncMappingAdapter(mapping), handshakeTimeoutMillis);
    }

    /**
     * Creates a SNI detection handler with configured {@link SslContext}
     * maintained by {@link AsyncMapping}
     *
     * @param mapping the mapping of domain name to {@link SslContext}
     * @param handshakeTimeoutMillis the handshake timeout in milliseconds
     */
    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        this(mapping, 0, handshakeTimeoutMillis);
    }

import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.internal.ObjectUtil;
    /**
     * The maximum length of client hello message as defined by
     * <a href="https://www.rfc-editor.org/rfc/rfc5246#section-6.2.1">RFC5246</a>.
     */
    public static final int MAX_CLIENT_HELLO_LENGTH = 0xFFFFFF;

    private final int maxClientHelloLength;
    public SslClientHelloHandler() {
        this(MAX_CLIENT_HELLO_LENGTH);
    }

    protected SslClientHelloHandler(int maxClientHelloLength) {
        // 16MB is the maximum as per RFC:
        // See https://www.rfc-editor.org/rfc/rfc5246#section-6.2.1
        this.maxClientHelloLength =
                ObjectUtil.checkInRange(maxClientHelloLength, 0, MAX_CLIENT_HELLO_LENGTH, "maxClientHelloLength");
    }

                                    if (handshakeLength > maxClientHelloLength && maxClientHelloLength != 0) {
                                        TooLongFrameException e = new TooLongFrameException(
                                                "ClientHello length exceeds " + maxClientHelloLength +
                                                        ": " + handshakeLength);
                                        in.skipBytes(in.readableBytes());
                                        ctx.fireUserEventTriggered(new SniCompletionEvent(e));
                                        SslUtils.handleHandshakeFailure(ctx, e, true);
                                        throw e;
                                    }
            } catch (TooLongFrameException e) {
                // Just rethrow as in this case we also closed the channel
                throw e;
import io.netty.handler.codec.TooLongFrameException;
    @Test
    public void testSniHandlerFailsOnTooBigClientHello() throws Exception {
        SniHandler handler = new SniHandler(new Mapping<String, SslContext>() {
            @Override
            public SslContext map(String input) {
                throw new UnsupportedOperationException("Should not be called");
            }
        }, 10, 0);

        final AtomicReference<SniCompletionEvent> completionEventRef =
                new AtomicReference<SniCompletionEvent>();
        final EmbeddedChannel ch = new EmbeddedChannel(handler, new ChannelInboundHandlerAdapter() {
            @Override
            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
                if (evt instanceof SniCompletionEvent) {
                    completionEventRef.set((SniCompletionEvent) evt);
                }
            }
        });
        final ByteBuf buffer = ch.alloc().buffer();
        buffer.writeByte(0x16);      // Content Type: Handshake
        buffer.writeShort((short) 0x0303); // TLS 1.2
        buffer.writeShort((short) 0x0006); // Packet length

        // 16_777_215
        buffer.writeByte((byte) 0x01); // Client Hello
        buffer.writeMedium(0xFFFFFF); // Length
        buffer.writeShort((short) 0x0303); // TLS 1.2

        assertThrows(TooLongFrameException.class, new Executable() {
            @Override
            public void execute() throws Throwable {
                ch.writeInbound(buffer);
            }
        });
        try {
            while (completionEventRef.get() == null) {
                Thread.sleep(100);
                // We need to run all pending tasks as the handshake timeout is scheduled on the EventLoop.
                ch.runPendingTasks();
            }
            SniCompletionEvent completionEvent = completionEventRef.get();
            assertNotNull(completionEvent);
            assertNotNull(completionEvent.cause());
            assertEquals(TooLongFrameException.class, completionEvent.cause().getClass());
        } finally {
            ch.finishAndReleaseAll();
        }
    }

        }, 0, 10);
        }, 0, 100);