Skip to content

iskorotkov/avro: CPU Exhaustion in Decoder

High severity GitHub Reviewed Published May 11, 2026 in iskorotkov/avro • Updated Jun 9, 2026

Package

gomod github.com/iskorotkov/avro/v2 (Go)

Affected versions

< 2.33.0

Patched versions

2.33.0

Description

CPU Exhaustion in Avro Decoder via Unbounded Block-Count Iteration

Summary

The Avro array and map decoders looped over an attacker-controlled block-count value without checking the underlying reader's error state inside the loop body. Reader.ReadBlockHeader returns the count as a Go int, which is 64-bit on amd64 / arm64 targets — so a producer can declare a block of up to math.MaxInt64 (~9.2 × 10¹⁸) elements followed by EOF (or any truncated payload), and the decoder will attempt that many no-op iterations before propagating the error. The realistic ceiling is "indefinite until the worker is killed externally" — a single hostile payload pins a CPU core until the process is OOM-killed, deadline-cancelled, or terminated. Remote, unauthenticated denial-of-service.

The fix exits the loop on the first inner-decode error. It does not bound the loop length itself; for full coverage on untrusted inputs, also configure Config.MaxSliceAllocSize and Config.MaxMapAllocSize (the latter introduced in v2.33.0).

Description

Avro arrays and maps are encoded as one or more blocks; each block declares an element count followed by that many encoded elements. The decoder reads the block count as a zigzag-encoded long, then iterates that many times calling an inner decoder.

Three iteration sites trusted the block count without checking the reader's accumulated error state between iterations:

  • codec_skip.go sliceSkipDecoder.Decode — skip helper for arrays.
  • codec_skip.go mapSkipDecoder.Decode — skip helper for maps.
  • reader_generic.go Reader.ReadArrayCB and Reader.ReadMapCB — callback-based decoders used by generic and unmarshaling code paths.

Because the inner Decode(nil, r) call is a no-op when r has already errored (it returns immediately without consuming bytes), the loop would run to completion even after the first iteration's EOF. On amd64 / arm64, Reader.ReadBlockHeader returns the count as int (= int64), so the loop bound is whatever the wire payload specified, up to math.MaxInt64. A modest 200-million-count payload (well under 2³¹) already burns several seconds; a math.MaxInt − 2 payload (the value used in the regression test TestDecoder_ArrayMultiBlockExceedsMaxInt from PR #9) effectively pins the goroutine until external kill.

This overlaps with GHSA-mc57-h6j3-3hmv: the same large-block-count payload that drives the unbounded loop here also drives the cumulative-arithmetic overflow there (cross-platform), and on a 32-bit target additionally triggers the union-index / byte-slice narrowing.

Affected components

File Function PR Fix commit
codec_skip.go sliceSkipDecoder.Decode b124caa
codec_skip.go mapSkipDecoder.Decode b124caa
reader_generic.go Reader.ReadArrayCB #4 2ce4242
reader_generic.go Reader.ReadMapCB #4 2ce4242

These are the audited and patched sites. Any other code path that iterates over an attacker-controlled count while calling a Reader-style decoder is structurally susceptible to the same pattern; reviewers of consumer code should grep for for range l / for i := 0; i < int(l); i++ near Reader method calls and confirm an in-loop error check.

Technical details

Vulnerable pattern:

for range l {
    d.decoder.Decode(nil, r)
    // r.Error may have been set by Decode; loop continues regardless.
}

After r.Error != nil, subsequent Decode calls short-circuit and return without consuming bytes or doing useful work, but the loop control variable still runs to l. With l = math.MaxInt64, the loop body executes ~9.2 × 10¹⁸ times — effectively infinite for any realistic timeout.

Fixed pattern (b124caa, 2ce4242):

for range l {
    d.decoder.Decode(nil, r)
    if r.Error != nil {
        break
    }
}

The fix terminates the loop on the first inner error. It does not bound l itself — a well-formed payload that actually contains N encoded null elements still iterates N times. The MaxSliceAllocSize / MaxMapAllocSize caps are the policy-level bound on that case (see Mitigation).

Fixed behavior

The reader's accumulated error is checked after every inner Decode in the four affected loops. Decoder errors now surface in O(1) iterations instead of O(blockCount) when the underlying read fails mid-stream.

Affected versions

  • github.com/hamba/avro/v2 — all versions up to and including v2.31.0 (repository is read-only upstream).
  • github.com/iskorotkov/avro/v2 — all versions prior to v2.33.0.

Fixed versions

github.com/iskorotkov/avro/v2 v2.33.0 and later. There is no upstream fix for github.com/hamba/avro/v2 — module path is archived. Migrate to the fork as described under Mitigation.

Mitigation

Migrate from github.com/hamba/avro/v2 to github.com/iskorotkov/avro/v2 >= v2.33.0. Replace the import path and run go mod tidy:

go get github.com/iskorotkov/avro/v2@latest

Or, for consumers that prefer the original import path, a replace directive in go.mod:

replace github.com/hamba/avro/v2 => github.com/iskorotkov/avro/v2 v2.33.0

replace is honoured only for the main module of a build — transitive consumers must add their own replace, or migrate the import path directly.

The error-propagation fix runs on the existing decode path and requires no configuration.

For defense-in-depth against well-formed but oversized payloads (where the fix above does not help, because no error fires), set explicit allocation caps:

cfg := avro.Config{
    MaxByteSliceSize:  102_400,
    MaxSliceAllocSize: 10_000,
    MaxMapAllocSize:   10_000,
}.Freeze()

decoder := cfg.NewDecoder(schema, reader)

MaxMapAllocSize is new in v2.33.0 and opt-in (default zero, which leaves the previous unbounded behavior). Without setting it, a producer that ships a math.MaxInt64-count block still consumes the corresponding memory and CPU; see GHSA-mx64-mj3q-7prj for the cumulative-allocation enforcement details.

If you cannot upgrade immediately, the structural workarounds are application-level: per-request decode timeouts, isolated decoder workers under CPU quotas, and rejection of payloads whose advertised block count exceeds a known sane bound for your schema.

Proof-of-concept input

A minimal payload that triggers the bug for an array of int:

zigzag-encoded long: math.MaxInt64   (block element count)
EOF                                  (no further bytes)

The decoder reads the block-count header, enters the loop, fails to read the first element (EOF), records the error, and then iterates math.MaxInt64 − 1 further times calling the inner decoder as a no-op. Wall-clock cost on commodity hardware: indefinite — the goroutine pins one CPU core until the process is OOM-killed, deadline-cancelled, or terminated externally. The classic "a few seconds per request" characterisation applies only to small-but-still-pathological block counts in the 10⁸–10⁹ range (e.g. 200_999_000 in TestDecoder_SkipArrayEOF); the architectural ceiling is math.MaxInt64.

A negative block count (-N) is also legal in Avro (signals an N-element block with an explicit byte length); the same iteration pattern applies once the count is negated.

References

Credits

  • Discovery and fixes (commits b124caa skip helpers and 2ce4242 callback path, PR #4): Daniel Błażewicz (@klajok)
  • Release authorship: Ivan Korotkov (@iskorotkov)

Timeline

  • 2026-04-28 — Skip-decoder fix (b124caa) merged.
  • 2026-04-30 — Callback-decoder fix (PR #4, 2ce4242) merged.
  • 2026-05-06v2.33.0 tagged and released.
  • 2026-05-11 — Advisory published.
  • 2026-05-15 — Advisory revised.

References

@iskorotkov iskorotkov published to iskorotkov/avro May 11, 2026
Published to the GitHub Advisory Database May 18, 2026
Reviewed May 18, 2026
Published by the National Vulnerability Database May 29, 2026
Last updated Jun 9, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v4 base metrics

Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements None
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity None
Availability High
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None

CVSS v4 base metrics

Exploitability Metrics
Attack Vector: This metric reflects the context by which vulnerability exploitation is possible. This metric value (and consequently the resulting severity) will be larger the more remote (logically, and physically) an attacker can be in order to exploit the vulnerable system. The assumption is that the number of potential attackers for a vulnerability that could be exploited from across a network is larger than the number of potential attackers that could exploit a vulnerability requiring physical access to a device, and therefore warrants a greater severity.
Attack Complexity: This metric captures measurable actions that must be taken by the attacker to actively evade or circumvent existing built-in security-enhancing conditions in order to obtain a working exploit. These are conditions whose primary purpose is to increase security and/or increase exploit engineering complexity. A vulnerability exploitable without a target-specific variable has a lower complexity than a vulnerability that would require non-trivial customization. This metric is meant to capture security mechanisms utilized by the vulnerable system.
Attack Requirements: This metric captures the prerequisite deployment and execution conditions or variables of the vulnerable system that enable the attack. These differ from security-enhancing techniques/technologies (ref Attack Complexity) as the primary purpose of these conditions is not to explicitly mitigate attacks, but rather, emerge naturally as a consequence of the deployment and execution of the vulnerable system.
Privileges Required: This metric describes the level of privileges an attacker must possess prior to successfully exploiting the vulnerability. The method by which the attacker obtains privileged credentials prior to the attack (e.g., free trial accounts), is outside the scope of this metric. Generally, self-service provisioned accounts do not constitute a privilege requirement if the attacker can grant themselves privileges as part of the attack.
User interaction: This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable system. This metric determines whether the vulnerability can be exploited solely at the will of the attacker, or whether a separate user (or user-initiated process) must participate in some manner.
Vulnerable System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the VULNERABLE SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the VULNERABLE SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the VULNERABLE SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
Subsequent System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the SUBSEQUENT SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the SUBSEQUENT SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the SUBSEQUENT SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
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

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(30th percentile)

Weaknesses

Uncontrolled Resource Consumption

The product does not properly control the allocation and maintenance of a limited resource. Learn more on MITRE.

Loop with Unreachable Exit Condition ('Infinite Loop')

The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop. Learn more on MITRE.

Improper Validation of Specified Quantity in Input

The product receives input that is expected to specify a quantity (such as size or length), but it does not validate or incorrectly validates that the quantity has the required properties. Learn more on MITRE.

CVE ID

CVE-2026-46385

GHSA ID

GHSA-w8j3-pq8g-8m7w

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.