Apr 7, 2026·6 min read·4 visits
A flaw in the go-ipld-prime DAG-CBOR decoder allows attackers to trigger Out-of-Memory (OOM) crashes via small payloads with large declared map or list lengths. The vulnerability is fixed in version 0.22.0.
The go-ipld-prime library prior to version 0.22.0 suffers from a resource exhaustion vulnerability in its DAG-CBOR decoding implementation. Maliciously crafted CBOR payloads containing artificially large declared collection sizes bypass early budget checks, triggering massive upfront memory allocations that result in immediate application crashes.
The go-ipld-prime library provides an implementation of the InterPlanetary Linked Data (IPLD) specification, primarily utilized in decentralized and distributed systems for data model representation. A critical component of this library is the DAG-CBOR decoder, which translates concise binary object representation payloads into Go data structures. The decoding process must handle untrusted, externally supplied input streams while managing system resources.
Prior to version 0.22.0, the DAG-CBOR decoder failed to properly constrain memory allocation operations derived from user-controlled input lengths. This behavior corresponds to CWE-770 (Allocation of Resources Without Limits or Throttling). The vulnerability manifests when the decoder processes Major Type 4 (Arrays) or Major Type 5 (Maps) CBOR headers, which explicitly declare the number of upcoming elements in the collection.
Applications utilizing go-ipld-prime to parse untrusted DAG-CBOR payloads are susceptible to resource exhaustion attacks. An adversary can submit a minimal payload that claims a massive element count, forcing the Go runtime to attempt an allocation exceeding available system memory. The resulting Out-of-Memory (OOM) panic causes immediate application termination, resulting in a high-impact Denial of Service (DoS) condition.
The vulnerability originates from a fundamental disconnect between the declared length in a CBOR header and the actual bytes required to represent that length in the payload stream. In the CBOR specification, a 64-bit integer specifying billions of elements requires a maximum of 9 bytes on the wire. The decoder read this length integer and directly used it as a preallocation hint for Go's internal slice and map creation functions.
When creating Go slices or maps, the runtime allocates a contiguous block of backing memory based on the requested capacity. The vulnerable implementation executed make([]datamodel.Node, length) or equivalent map instantiations immediately after parsing the header. This eager allocation strategy intended to optimize performance by minimizing future reallocation overhead as elements were decoded.
While go-ipld-prime implemented an allocation budget system to prevent resource exhaustion, the validation logic contained a critical sequencing flaw. The decoder only decremented the budget counter as individual elements were successfully parsed and validated. Because the massive preallocation occurred before any child elements were processed, the budget system was completely bypassed during the initial memory request.
An examination of the decoding sequence highlights the structural flaw in early versions. When the decoder identified a map or list, it extracted the length field and passed it directly to the node builder. The runtime then attempted to fulfill the request, ignoring the established resource limits.
The remediation introduced in commit e43bf4a27055fe8d895671a731ee5041e2d983a9 implements a dual-layer defense mechanism within codec/dagcbor/unmarshal.go. The first layer integrates the existing budget system directly into the preallocation phase. The decoder now subtracts the declared length from the remaining allocation budget upfront, returning an ErrAllocationBudgetExceeded error if the request exceeds the limits.
// Early budget validation
if *budget-allocLen < 0 {
return ErrAllocationBudgetExceeded
}
// Preallocation capping layer
if allocLen > options.maxPrealloc() {
allocLen = options.maxPrealloc()
}
// Safe initialization
ma, err := na.BeginMap(allocLen)The second defensive layer introduces MaxCollectionPrealloc, which defaults to 1024 entries. Regardless of the declared payload length, the initial memory allocation hint passed to the Go runtime is capped at this maximum value. Valid structures containing more than 1024 elements continue to decode correctly, as Go maps and slices will dynamically grow, but the memory consumption is strictly tied to validated, physical bytes received over the wire.
Exploitation requires no authentication and relies entirely on delivering a syntactically valid but maliciously structured CBOR sequence to a target endpoint. The attacker constructs a payload consisting of a single Major Type 4 or 5 header, explicitly declaring a 32-bit or 64-bit length. The remainder of the payload can be omitted or truncated.
The attack efficiency scales dramatically through structural nesting. An attacker can construct a list containing a list, repeating this pattern multiple times. Each nested level triggers its own unconstrained preallocation while consuming only a single unit of budget from the parent collection. This structural amplification allows a payload of less than 100 bytes to demand gigabytes of memory allocation.
The following logical proof-of-concept demonstrates the payload generation mechanism required to trigger the vulnerability. The code generates a single map header with a declared length of twenty million elements.
// Conceptual payload generator for CVE-2026-35480
func generateMaliciousCbor() []byte {
var buf bytes.Buffer
// 0xBA indicates a Map (Major type 5) with a 32-bit length to follow
buf.WriteByte(0xBA)
// Requesting 20,000,000 preallocated elements
binary.Write(&buf, binary.BigEndian, uint32(20_000_000))
return buf.Bytes()
}The direct consequence of this vulnerability is an unrecoverable application crash. Go runtimes handle out-of-memory states by terminating the process with a fatal panic that cannot be intercepted by standard recover() routines. An attacker can repeatedly send the malicious payload, maintaining a sustained Denial of Service condition against the target infrastructure.
The severity of the impact varies based on the decoding methodology employed by the application. Schema-free decoding utilizing basicnode.Prototype.Any represents the highest risk profile, as it permits arbitrary structural nesting without constraint. Applications utilizing schema-bound decoding mitigate nested amplification attacks by restricting the depth and structure to a predefined layout, but remain vulnerable to single-level preallocation exhaustion if large collections are permitted.
The CVSS v3.1 score for this vulnerability is 6.2 (Medium). The official vector assigns an Attack Vector of Local (AV:L), which typically implies local access to the execution environment. However, since DAG-CBOR is predominantly used as a serialization format for network communications, network-based exploitation is highly probable in standard deployment architectures.
The primary and most effective remediation is updating the go-ipld-prime dependency to version 0.22.0 or later. This release fundamentally alters the memory allocation behavior, ensuring that memory consumption scales linearly with actual parsed data rather than attacker-controlled metadata flags.
In environments where immediate dependency updating is unfeasible, mitigation options are severely limited. Network layer filtering is generally ineffective, as analyzing serialized CBOR structures requires deep packet inspection capabilities that understand the DAG-CBOR format. Web Application Firewalls (WAFs) cannot easily differentiate between a legitimate large map header and a malicious one without maintaining stateful payload analysis.
Developers should verify their software supply chain and execute dependency scanning tools to identify transitive dependencies that may rely on vulnerable versions of go-ipld-prime. Following the update, no configuration changes are required to activate the protection, as the 1024-element preallocation cap is enabled by default in the new unmarshal options.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
go-ipld-prime IPLD | < 0.22.0 | 0.22.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-770 |
| Attack Vector | Local / Network |
| CVSS Score | 6.2 |
| Exploit Status | Proof of Concept (Unit Tests) |
| CISA KEV | Not Listed |
| Impact | High Availability Impact (DoS) |
Allocation of Resources Without Limits or Throttling