diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Hpack.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Hpack.kt index 7c269d9c8da8..d759d44059a1 100644 --- a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Hpack.kt +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Hpack.kt @@ -47,6 +47,11 @@ object Hpack { private const val PREFIX_6_BITS = 0x3f private const val PREFIX_7_BITS = 0x7f + /** + * Per-entry overhead of a header list entry, as defined by RFC 9113 section 6.5.2. + */ + private const val HEADER_ENTRY_OVERHEAD = 32 + private const val SETTINGS_HEADER_TABLE_SIZE = 4_096 /** @@ -411,7 +416,9 @@ object Hpack { private fun addHeader(header: Header) { headerList.add(header) - val headerSize = header.name.size + header.value.size + // Include the per-entry overhead of RFC 9113 section 6.5.2, so the accounting + // also grows for headers whose name and value are both empty. + val headerSize = header.name.size + header.value.size + HEADER_ENTRY_OVERHEAD val newHeaderListSize = headerListByteCount + headerSize headerListByteCount = newHeaderListSize if (newHeaderListSize > HEADER_LIMIT) { diff --git a/okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HpackTest.kt b/okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HpackTest.kt index cf6fc58a1ea5..a6d1bd619810 100644 --- a/okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HpackTest.kt +++ b/okhttp/src/jvmTest/kotlin/okhttp3/internal/http2/HpackTest.kt @@ -36,6 +36,24 @@ class HpackTest { private val bytesOut = Buffer() private var hpackWriter: Hpack.Writer? = null + /** + * Each header list entry must account for the RFC 9113 section 6.5.2 per-entry overhead, + * including headers whose name and value are both empty. Otherwise a peer could flood + * zero-length literal header fields and grow the header list without ever exceeding + * [HEADER_LIMIT]. + */ + @Test + fun manyEmptyHeadersExceedByteLimit() { + repeat(8193) { + bytesIn.writeByte(0x00) // Literal Header Field without Indexing, new name. + bytesIn.writeByte(0x00) // Name length 0. + bytesIn.writeByte(0x00) // Value length 0. + } + assertFailsWith { + hpackReader!!.readHeaders() + } + } + @BeforeEach fun reset() { hpackReader = newReader(bytesIn)