Sep 1, 2026·7 min read·229 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.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.