Skip to main content
Vulnerability Database/CVE-2026-84304

CVE-2026-84304: gRPC-Go HTTP/2 DoS Vulnerability

CVE-2026-84304 is a denial of service flaw in gRPC-Go that allows attackers to exhaust memory via fragmented HTTP/2 frames. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-84304 Overview

CVE-2026-84304 is a resource exhaustion vulnerability [CWE-400] affecting gRPC-Go, the Go language implementation of gRPC. The flaw exists in internal/transport/transport.go, which stores each fragmented HTTP/2 DATA frame as a separate recvMsg entry in the recvBuffer. An unauthenticated remote attacker can transmit millions of one-byte DATA frames across concurrent multiplexed streams to consume disproportionate heap memory. The attack succeeds even when payload bytes stay within HTTP/2 connection and stream flow-control windows. The result is a runtime panic or out-of-memory (OOM) termination of the gRPC server process. The issue is fixed in gRPC-Go version 1.83.1.

Critical Impact

Unauthenticated remote attackers can force gRPC-Go servers into OOM termination or runtime panic, breaking service availability for all connected clients.

Affected Products

  • gRPC-Go versions prior to 1.83.1
  • Applications and services embedding the google.golang.org/grpc module below 1.83.1
  • Go-based microservices exposing gRPC endpoints to untrusted networks

Discovery Timeline

  • 2026-09-01 - CVE-2026-84304 published to NVD
  • 2026-09-01 - Last updated in NVD database

Technical Details for CVE-2026-84304

Vulnerability Analysis

The vulnerability originates in how gRPC-Go's HTTP/2 transport layer buffers inbound DATA frames. Each fragmented DATA frame is queued as its own recvMsg structure inside recvBuffer, regardless of payload size. HTTP/2 flow control governs the sum of payload bytes in flight, not the number of frames. This design gap allows an attacker to send an enormous number of one-byte DATA frames while remaining within permitted window sizes.

Each recvMsg carries object overhead, pool metadata, and pointer references. Millions of tiny frames multiply this per-object overhead into gigabytes of heap pressure. Combined with HTTP/2 stream multiplexing, a single TCP connection can issue thousands of concurrent streams, each contributing to the buffer growth. The Go runtime eventually panics or the process is terminated by the OS OOM killer.

Root Cause

The root cause is a mismatch between HTTP/2 flow-control accounting (byte-based) and gRPC-Go's internal buffer accounting (frame-count-based). Small DATA frames pass flow-control checks trivially but each allocates a full recvMsg with associated overhead. No frame-count throttle or receive-buffer compaction existed prior to the fix.

Attack Vector

An unauthenticated network attacker opens a TLS or plaintext gRPC connection and negotiates HTTP/2. The attacker opens many concurrent streams and, for each, sends a stream of one-byte DATA frames without ending the stream. Server-side buffers grow without bound until memory is exhausted.

go
// Patch excerpt: introduces a receive-buffer compaction toggle
// Source: https://github.com/grpc/grpc-go/commit/8cfeca0e1ee5ea0980dcc320e20240fa1079ec77

// EnableReceiveBufferCompaction enables the compaction of data buffers
// to reduce the number of buffers in the receive buffer.
//
// This environment variable serves as an escape hatch to disable the
// feature if unforeseen issues arise, and it will be removed in a future
// release.
//
// TODO: Remove this env var once v1.85.0 is released.
EnableReceiveBufferCompaction = boolFromEnv("GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION", true)
go
// Patch excerpt: introduces BufferPoolingThreshold to avoid pooling tiny buffers
// Source: https://github.com/grpc/grpc-go/commit/7354d9c8debb4bcf2225bf429857078de310c176

var (
    // BufferPoolingThreshold is the minimum size of a buffer that can be pooled.
    // This is used to determine whether to pool buffers or allocate them directly.
    BufferPoolingThreshold = 1 << 10
)

func init() {
    internal.SetBufferPoolingThresholdForTesting = func(threshold int) {
        BufferPoolingThreshold = threshold
    }
}

Detection Methods for CVE-2026-84304

Indicators of Compromise

  • Sustained heap growth on gRPC-Go server processes without corresponding increase in application throughput or request rate
  • HTTP/2 connections issuing an abnormally high ratio of DATA frames to bytes transferred (tiny average frame size, often 1 byte)
  • Server logs showing runtime: out of memory, Go runtime panics, or SIGKILL termination from the OS OOM killer
  • Elevated count of concurrent open HTTP/2 streams per single client connection

Detection Strategies

  • Instrument gRPC servers with Prometheus or OpenTelemetry metrics tracking DATA-frame-per-second and average frame-payload size per connection
  • Alert on go_memstats_heap_inuse_bytes growth that outpaces grpc_server_handled_total request counts
  • Deploy network flow analysis to flag HTTP/2 sessions with high frame counts and low goodput

Monitoring Recommendations

  • Monitor grpc.server.streams_open and Go runtime memory statistics on every gRPC-Go workload
  • Track sudden increases in RSS (resident set size) for Go binaries exposing gRPC listeners
  • Correlate server OOM kill events with source IPs of active HTTP/2 peers using flow telemetry

How to Mitigate CVE-2026-84304

Immediate Actions Required

  • Upgrade all gRPC-Go dependencies to version 1.83.1 or later by updating go.mod and rebuilding affected binaries
  • Confirm receive-buffer compaction is enabled (default in 1.83.1) by leaving GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION unset or set to true
  • Inventory all Go services that import google.golang.org/grpc and validate module versions in build pipelines

Patch Information

The fix is available in gRPC Go Release v1.83.1. Technical details are in gRPC Go Security Advisory GHSA-vp52-pcj8-j9qc and pull requests #9331 and #9333. The patch introduces receive-buffer compaction and a BufferPoolingThreshold that avoids pooling sub-kilobyte buffers.

Workarounds

  • If immediate upgrade is not feasible, place gRPC-Go servers behind an HTTP/2-aware reverse proxy that enforces per-connection frame-rate and stream-count limits
  • Reduce MaxConcurrentStreams server option to constrain the number of simultaneous streams per client connection
  • Set process-level memory limits via GOMEMLIMIT or container memory.max to trigger controlled restarts instead of host-wide impact
bash
# Update gRPC-Go to the patched release
go get google.golang.org/grpc@v1.83.1
go mod tidy

# Verify receive-buffer compaction is enabled (default)
export GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION=true

# Constrain runtime memory as a defense-in-depth measure
export GOMEMLIMIT=2GiB

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.