Sep 1, 2026·7 min read·3 visits
An unauthenticated remote attacker can crash gRPC-Go servers (prior to version 1.83.1) by sending millions of fragmented, 1-byte HTTP/2 DATA frames. This bypasses standard flow control limits, forcing excessive metadata allocation on the heap and leading to Out-of-Memory (OOM) crashes.
CVE-2026-84304 is a high-severity uncontrolled resource consumption vulnerability in gRPC-Go, the Go implementation of the gRPC framework. The issue stems from a memory amplification flaw inside the HTTP/2 DATA frame processing subsystem. Remote, unauthenticated attackers can exploit this vulnerability by sending a high volume of heavily fragmented, tiny DATA frames within multiplexed concurrent streams. This causes gRPC-Go servers to allocate excessive internal metadata structures on the Go heap, leading to severe heap memory amplification, intense garbage collection thrashing, and process termination due to Out-of-Memory (OOM) conditions.
The vulnerability resides in the gRPC-Go network transport component, specifically within the internal receive buffer system (recvBuffer) defined in internal/transport/transport.go. This component handles the delivery of incoming HTTP/2 transport frames to active RPC endpoints. The attack surface is exposed to any network interface where a gRPC-Go server is actively listening for incoming client requests, requiring no pre-authentication or specific session state.
Under standard operating conditions, gRPC-Go manages incoming data streams by queueing parsed HTTP/2 frames into memory pools. However, the interface between the HTTP/2 frame parsing loop and the message delivery queue lacked a mechanism to evaluate the ratio of packet metadata size to actual payload data size. Consequently, attackers can manipulate this omission to force the server into a worst-case allocation state.
This security vulnerability belongs to the Uncontrolled Resource Consumption class (CWE-400). The ultimate impact of successful exploitation is a complete denial of service (DoS) for the affected microservice. Because many core Kubernetes components, service meshes, and cloud-native applications rely heavily on gRPC-Go, a failure in this transport component can propagate throughout adjacent service tiers.
To understand the root cause of CVE-2026-84304, it is necessary to examine the interaction between HTTP/2 flow control and the Go runtime heap. HTTP/2 utilizes a window-based flow control mechanism to limit the volume of unacknowledged data bytes sent across a connection or individual stream. This mechanism restricts the size of the raw payloads an endpoint can transmit, preventing memory exhaustion under normal conditions.
However, standard HTTP/2 flow control only tracks raw payload bytes; it does not account for the number of frames used to transmit those bytes. In vulnerable versions of gRPC-Go, every incoming HTTP/2 DATA frame is parsed into an individual tracking structure called recvMsg. On a 64-bit architecture, each recvMsg structure, combined with its underlying slice headers and pointer trackers, incurs approximately 56 bytes of metadata overhead, regardless of payload size.
When an attacker fragments a small payload (for example, 100 KB) into 100,000 separate 1-byte DATA frames, the metadata-to-payload ratio inverts. The raw payload consumes only 100 KB, which comfortably remains within default stream flow-control windows. However, the accompanying tracking structures consume 5.6 MB of heap memory, yielding an amplification ratio of 56:1. By scale, sending 10 MB of data split into 10 million frames forces over 560 MB of heap allocations, triggering garbage collection (GC) thrashing and process termination via the OS Out-of-Memory (OOM) killer.
The vulnerability was mitigated in gRPC-Go version 1.83.1 by implementing an automated, lazy receive buffer compaction mechanism. This mechanism bounds the metadata-to-payload ratio by merging fragmented small frames. The patch defines a utilization threshold to determine when the tracking overhead has become disproportionately high compared to the payload size.
const (
logLevel = 2
// recvMsgSize estimates the memory overhead of a recvMsg in the backlog.
// It accounts for the recvMsg struct itself and the slice header of the
// underlying buffer's data.
recvMsgSize = int(unsafe.Sizeof(recvMsg{}) + unsafe.Sizeof([]byte{}))
// utilizationFactor controls when we consider memory utilization acceptable.
// When backlogHeapSize / payloadSize <= utilizationFactor, compaction is skipped.
utilizationFactor = 2
)
var (
// compactionThreshold is approx 57KB (on 64-bit systems). It allows
// accumulating up to 1024 1-byte payloads before triggering compaction.
compactionThreshold = imem.BufferPoolingThreshold * (recvMsgSize + 1)
)The fix introduces fields in recvBuffer to monitor uncompacted data. When recvBuffer.put() appends an incoming message to the backlog, it evaluates the accumulation metrics using compactBacklogLocked(). If the ratio of total metadata heap size to raw payload bytes exceeds utilizationFactor, and the total allocated size surpasses compactionThreshold, the compaction routine executes.
func (b *recvBuffer) compactBacklogLocked(r recvMsg) {
if !envconfig.EnableReceiveBufferCompaction {
return
}
if r.buffer == nil {
b.uncompactedBytes = 0
b.uncompactedSuffixLen = 0
return
}
b.uncompactedSuffixLen++
b.uncompactedBytes += r.buffer.Len()
backlogHeapSize := b.uncompactedSuffixLen*recvMsgSize + b.uncompactedBytes
// Check if the ratio of allocated heap to actual payload is acceptable
if backlogHeapSize <= utilizationFactor*b.uncompactedBytes {
b.uncompactedBytes = 0
b.uncompactedSuffixLen = 0
return
}
// Skip compaction if total uncompacted footprint is below the threshold
if backlogHeapSize <= compactionThreshold {
return
}
// Perform compaction: Coalesce small, fragmented frames into a contiguous pooled buffer
start := 0
newBuf := b.bufPool.Get(b.uncompactedBytes)
startIdx := len(b.backlog) - b.uncompactedSuffixLen
for i := startIdx; i < len(b.backlog); i++ {
m := b.backlog[i]
b.backlog[i] = recvMsg{} // Prevent memory leaks by clearing reference
start += copy((*newBuf)[start:], m.buffer.ReadOnlyData())
m.buffer.Free() // Free individual small buffers back to pool
}
b.backlog[startIdx] = recvMsg{
buffer: mem.NewBuffer(newBuf, b.bufPool),
}
b.backlog = b.backlog[:startIdx+1] // Truncate the slice length
b.uncompactedBytes = 0
b.uncompactedSuffixLen = 0
}An attack utilizing CVE-2026-84304 requires direct TCP network access to the gRPC-Go server port. The attacker begins by initiating a standard HTTP/2 connection handshake. Once the connection is established, the attacker opens multiple concurrent streams using multiplexing, which increases the server's processing concurrency and distributes the load across several handler routines.
Next, the attacker initiates a standard RPC request call. Instead of transmitting the request payload in a small number of standard-sized HTTP/2 DATA frames, the attacker's client custom-codes the frame layer to split the payload into single-byte blocks. This produces an extremely high volume of DATA frames sent in immediate succession.
Throughout this process, the attacker ensures that the total volume of raw payload bytes remains below the active stream and connection-level WINDOW_UPDATE thresholds. Consequently, standard HTTP/2 flow control limits are never triggered. The target server continues to accept the incoming stream of tiny frames, accumulating hundreds of thousands of recvMsg structures in the stream's backlog queue until system memory is exhausted.
Although the receive buffer compaction patch effectively mitigates the primary memory exhaustion vector, secondary security implications remain. Researchers must consider potential CPU exhaustion vectors arising from the compaction process itself. An attacker could craft a specific sequence of frame sizes designed to hover precisely around the compactionThreshold, causing the server to execute repeated memory allocations and copy() calls.
Additionally, the patch relies on Go's slice slice-expression (b.backlog[:startIdx+1]) to shrink the active backlog. While this operation decreases the length of the slice, it does not reduce the capacity of the underlying array. If the queue grows substantially prior to a compaction cycle, a portion of the memory allocated for the slice's backing array remains referenced on the heap until the slice is fully garbage collected.
Finally, the mitigation can be completely bypassed if the environment variable GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION is explicitly set to false. Administrators of cloud-native and Kubernetes deployments must audit container manifests to ensure this variable has not been disabled by legacy configuration scripts.
To resolve the vulnerability, development teams must upgrade their gRPC-Go dependencies to version 1.83.1 or later. This can be accomplished by updating the Go module file and recompiling the application binary:
go get google.golang.org/grpc@v1.83.1
go mod tidyIf immediate patching is not possible, security teams can employ temporary workarounds. Deploying a reverse proxy or Web Application Firewall (WAF) such as Envoy or NGINX in front of the gRPC service can provide protection. These proxies can be configured to enforce limits on the minimum size of incoming HTTP/2 DATA frames and restrict maximum stream concurrency.
Additionally, administrators can limit exposure by configuring tight read deadlines and minimizing the MaxConcurrentStreams parameter on gRPC servers. This limits the number of parallel channels an attacker can exploit on a single TCP connection, reducing the rate of heap memory accumulation.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
gRPC-Go gRPC | < 1.83.1 | 1.83.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 8.7 |
| Vulnerability Type | Uncontrolled Resource Consumption |
| Exploit Status | none |
| CISA KEV Status | Not Listed |
The software does not properly control the allocation or maintenance of critical resources, enabling attackers to exhaust system memory and trigger a denial of service.
An authentication oracle vulnerability exists in Filament before 4.12.5 and 5.7.5. The application initiates MFA challenge workflows prior to verifying user authorization policies, allowing unauthenticated attackers to validate guessed credentials.
A multi-factor authentication bypass vulnerability exists in Filament (Laravel full-stack framework panels) due to improper time-step tracking of Time-Based One-Time Password (TOTP) codes. By submitting valid TOTP codes from an older time window within the drift allowance, an attacker with a user's password can bypass the single-use MFA guarantee and obtain unauthorized account access.
CVE-2026-19418 is a high-severity origin validation vulnerability in TYPO3 CMS that enables Cross-Site Request Forgery (CSRF) and access control bypasses. Due to architectural consolidation of entry points in version 13.0, the core ReferrerEnforcer fails to isolate backend endpoints from the frontend, allowing an attacker with frontend script execution capabilities to perform unauthorized administrative actions.
CVE-2026-79675 is a critical command injection vulnerability in NLTK versions prior to 3.10.3 that permits remote attackers to execute arbitrary code on the hosting system. This vulnerability stems from an incomplete mitigation of a previous vulnerability, CVE-2026-12841. While NLTK verified global JVM options configured through the library's setup routines, it failed to perform equivalent safety checks on options provided during per-call invocations of Stanford NLP Java wrappers. Attackers controlling these parameters can pass dangerous Java configuration options to the system shell, bypassing security boundaries to spawn interactive processes or load untrusted Java archives.
A vulnerability in Django REST Framework (DRF) before version 3.17.2 allows remote attackers to bypass the native Django DATA_UPLOAD_MAX_MEMORY_SIZE limits. When parsing JSON or URL-encoded request bodies, DRF's JSONParser and FormParser read directly from the low-level HTTP network stream, bypassing Django's high-level request size checks and causing Denial of Service (DoS) via resource exhaustion.
A directory traversal vulnerability exists in pacquet, the Rust port of pnpm. When executing an install with the --trust-lockfile flag enabled, a crafted pnpm-lock.yaml file bypasses resolution-policy verification. This allows an attacker to inject path traversal sequences into package names or versions, leading to symbolic links being written outside the workspace directory.