GHSA-mc57-h6j3-3hmv
HIGHGHSA-mc57-h6j3-3hmv is a high-severity (CVSS 7.5) CWE-190 vulnerability in github.com/iskorotkov/avro/v2. O3 Security confirms whether GHSA-mc57-h6j3-3hmv is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
iskorotkov/avro: Integer Overflow in Decoder
Exploitation Status
No confirmed exploitation observed yet
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-mc57-h6j3-3hmv.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-mc57-h6j3-3hmv plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 360,142 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
github.com/iskorotkov/avro/v2Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Integer Overflow in Avro Decoder
Summary
Several Avro decoder paths read attacker-controlled 64-bit values from the wire format and either narrowed them to platform-sized int before bounds-checking, or summed them with overflow-prone signed-int arithmetic. On 32-bit targets (GOARCH=386, arm, mips, wasm, etc.), the truncation paths can silently bypass byte-slice limits, select the wrong union branch, or hit the OCF negative-make panic via wrap. Three sub-issues are not 32-bit-specific: cumulative-size arithmetic overflow in arrayDecoder.Decode / mapDecoder.Decode / mapDecoderUnmarshaler.Decode (wraps at math.MaxInt64 on amd64 / arm64 and bypasses MaxSliceAllocSize / MaxMapAllocSize), math.MinInt negation in block-header handling, and make([]byte, size) with a negative size in OCF block reads — all three panic or bypass caps on any platform, giving an attacker a denial-of-service primitive there.
Exploitation requires only an untrusted Avro stream. No primitives reach beyond denial-of-service on current code paths; see the union-index discussion below for a caveat.
Description
Six call sites in the decoder accepted int64 values from the Avro wire format and converted to int before validation. On a 32-bit build any wire value with magnitude ≥ 2³¹ truncates and the post-conversion value bears no useful relationship to the original. A value of (1<<32) + 5 narrows to 5; 1<<32 narrows to 0; values just past MaxInt32 narrow to large negatives.
This is distinct from the existing Config.MaxSliceAllocSize, Config.MaxByteSliceSize, and the new Config.MaxMapAllocSize limits, because narrowing happens before the limit comparison — the limit sees the truncated value, not the original wire value, so the cap is bypassed.
Three further sub-issues are not 32-bit-specific:
arrayDecoder.Decode,mapDecoder.Decode, andmapDecoderUnmarshaler.Decodesummed attacker-controlled block lengths viasize += int(l)and then checkedsize > limit. On amd64 / arm64 the running total wraps atmath.MaxInt64; the post-wrap negative value passes the> limitcheck, and the decoder proceeds. Regression test:TestDecoder_ArrayMultiBlockExceedsMaxIntusesmath.MaxInt − 2for the second block's count and aMaxSliceAllocSizeof 13 to demonstrate this on amd64. The Avro block-count field is a signedlongon the wire, so block counts up tomath.MaxInt64are admissible — there is no implicit 2³¹ ceiling.ReadBlockHeader()returns the absolute value of negative block lengths; the negation is unsafe formath.MinInt, which on every platform panics on overflow.ocf/ocf.go readBlock()passes the decoded block size directly tomake([]byte, size). A negative wire value panics on every platform; on 32-bit, values> MaxInt32additionally panic via the narrowing path.
Affected components
| File | Function(s) | Bug class | Platforms |
|---|---|---|---|
reader.go | ReadBlockHeader — narrowing | Narrowing | 32-bit |
reader.go | ReadBlockHeader — -math.MinInt | Signed overflow (CWE-191) | all |
reader.go | readBytes (via Reader.ReadBytes, Reader.ReadString) | Narrowing | 32-bit |
reader_skip.go | SkipString, SkipBytes (and OCF skip path) | Narrowing | 32-bit |
codec_array.go | arrayDecoder.Decode | Cumulative-size arithmetic overflow (CWE-190) | all |
codec_map.go | mapDecoder.Decode, mapDecoderUnmarshaler.Decode | Cumulative-size arithmetic overflow (CWE-190) | all |
ocf/ocf.go | skipToEnd, readBlock — narrowing | Narrowing | 32-bit |
ocf/ocf.go | readBlock — negative make([]byte, …) | Unchecked-negative (CWE-1284) | all |
reader_generic.go | union-type index decoding in Reader.ReadNext | Narrowing, possible wrong-branch selection | 32-bit |
PR #9 (commit bed99b3) covered ReadBlockHeader, the cumulative checks in array/map codecs, and the skip helpers. The completeness pass (commit e1a570f) covered the union index, readBytes, and OCF readBlock, and added a 32-bit CI job.
Note: the typed-codec union decoder in codec_union.go (getUnionSchema → Reader.ReadInt) is not affected by the union-index narrowing — ReadInt returns int32, no narrowing occurs. The narrowing is specific to Reader.ReadNext in the generic decode path (reached via Unmarshal into any / map[string]any).
Technical details
-
Block-header narrowing and
MinIntnegation.ReadBlockHeader()returned wire-formatint64values through narrower operations; on 32-bit, large positives truncated. Negatingmath.MinIntto convert a negative block-count signal into a positive size is undefined-on-overflow, and on every platform-MinIntpanics on overflow when used in subsequent arithmetic. The fix reads into a*64-suffixed local, range-checks againstMinInt32/MaxInt32(orMinInt/MaxIntas appropriate), and narrows after validation. -
Cumulative array and map size overflow (all platforms).
arrayDecoder.Decode,mapDecoder.Decode, andmapDecoderUnmarshaler.Decodesummed attacker-controlled block lengths using overflow-prone addition; cumulative size could wrap before reaching the configured limit. On amd64 withMaxSliceAllocSize = 13, block 1 of 3 elements, block 2 ofmath.MaxInt − 2elements: the pre-fixsize += int(l)wraps tomath.MinInt, thenMinInt > 13is false, so the check passes and the decoder proceeds. The fix uses subtraction-safe comparisons (l > limit - sizerather thansize + l > limit), which is overflow-immune. -
Skip-length truncation.
SkipString,SkipBytes, and the OCF skip helper now route throughSkipNBytesInt64(), which keeps the length asint64and range-checks before any narrowing. -
Byte-slice length truncation. A wire-format length such as
(1<<32) + 5truncated to5inreadBytes(), slipping pastConfig.MaxByteSliceSizeon 32-bit. The fix reads the length asint64, compares againstMaxByteSliceSizebefore narrowing, and returns "value is too big" if exceeded. -
Union index narrowing (generic decode path only).
Reader.ReadNextdecoded the union index asint64and immediately cast toint. On 32-bit,1<<32narrowed to0and silently selectedtypes[0]despite the explicit upper-bound check immediately above. Iftypes[0]is the null branch (idiomatic for["null", T]nullable unions), the practical result is a null value where the producer encoded a non-null payload — a DoS-grade logic error. Iftypes[0]is a non-trivial schema, downstream bytes are parsed against the wrong schema and produce well-typed but semantically wrong values; treat this as the worst-case interpretation when assessing impact on your own deployment. The typed-codec union decoder (codec_union.gogetUnionSchema→Reader.ReadInt) is not affected. -
OCF block-size narrowing and negative
make.readBlock()passes the decodedint64size directly tomake([]byte, size). A negative wire value panics on every platform; a value> MaxInt32additionally panics via the 32-bit narrowing path. The fix validates the size is in[0, MaxByteSliceSize]before narrowing.
Fixed behavior
Both commits apply the same pattern across every site:
- Read the wire value into an
int64-typed local. - Range-check upper and lower bounds before narrowing.
- Compare cumulative limits using subtraction-safe arithmetic.
- Route skip operations through
SkipNBytesInt64(). - Return descriptive errors using the consistent
"value is too big"/"value is too small"wording. - Cast to
intonly after validation succeeds.
CI: a test-386 job runs the suite under GOARCH=386 with CGO_ENABLED=0 (-race is amd64/arm64-only). Three tests with untyped 2147483648 constants whose t.Skipf gates fire too late (the file fails to compile before any test runs) were split into sibling *_64bit_test.go files gated by //go:build amd64 || arm64 || ....
Affected versions
github.com/hamba/avro/v2— all versions up to and includingv2.31.0(repository is read-only upstream).github.com/iskorotkov/avro/v2— all versions prior tov2.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. The packages share the same API surface; replace the import path and run go mod tidy:
- import "github.com/hamba/avro/v2"
+ import "github.com/iskorotkov/avro/v2"
For consumers that prefer the original import path, a replace directive in go.mod is supported:
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.
No further configuration is required to benefit from the integer-narrowing fixes — the validation runs on the existing decode path.
If you cannot upgrade immediately:
- Do not decode untrusted Avro data on any platform — the cumulative-arithmetic overflow paths (
arrayDecoder.Decode,mapDecoder.Decode,mapDecoderUnmarshaler.Decode) are reachable on amd64 / arm64. The truncation paths on 32-bit cannot be mitigated by settingConfig.MaxByteSliceSizelower, because the truncated post-narrowing value is what the limit sees, not the original wire value. - For the cross-platform
math.MinIntand OCF negative-size panic paths, wrappingDecode/ OCF read calls in a goroutine withdefer recover()contains the crash, but is not a substitute for upgrading. The other narrowing paths return errors rather than panicking, sorecover()does nothing for them. - Isolate decoding workers so a crash is bounded.
Proof-of-concept inputs
- A
bytesorstringlength of(1<<32) + Nfor smallN, which narrows toNon 32-bit and bypassesConfig.MaxByteSliceSize. - A union index of
1<<32, which narrows to0on 32-bit and selectstypes[0]despite the upper-bound check. - An array or map encoded across multiple blocks whose cumulative element count wraps the signed
intrunning total before the limit check fires. Demonstrated on amd64 byTestDecoder_ArrayMultiBlockExceedsMaxInt:MaxSliceAllocSize = 13, block 1 of3, block 2 ofmath.MaxInt − 2. Wraps tomath.MinInt, check passes, decoder proceeds. - A block header whose absolute value is
math.MinInt, triggering the unsafe negation (cross-platform). - An OCF block size that is negative on the wire, causing
make([]byte, size)to panic (cross-platform); or a positive value> MaxInt32on 32-bit, same outcome via narrowing.
References
- Initial hardening PR: iskorotkov/avro#9
- Completeness pass PR: iskorotkov/avro#10
- Fix commits:
bed99b3,e1a570f - Release:
v2.33.0 - Security policy:
SECURITY.md - Related advisories on this fork:
GHSA-w8j3-pq8g-8m7w(CPU exhaustion — overlaps via the same large-block-count payload shape),GHSA-mx64-mj3q-7prj(unbounded map allocation) - Cross-module precedent on
hamba/avro:GO-2023-1930/CVE-2023-37475/GHSA-9x44-9pgq-cf45 - Upstream (read-only):
hamba/avro
Credits
- Discovery and initial fixes (PR #9, commit
bed99b3—ReadBlockHeader, cumulative array/map checks, skip helpers): Daniel Błażewicz (@klajok) - Completeness fixes (commit
e1a570f— union index,readBytes, OCFreadBlock, 32-bit CI coverage): Ivan Korotkov (@iskorotkov)
Timeline
- 2026-05-04 — Initial integer-overflow hardening (PR #9,
bed99b3) merged. - 2026-05-04 — Completeness pass (
e1a570f) merged; 32-bit CI job added. - 2026-05-06 —
v2.33.0tagged and released. - 2026-05-11 — Advisory published.
- 2026-05-15 — Advisory revised.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/iskorotkov/avro/v2 | all versions | 2.33.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/iskorotkov/avro/v2. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update github.com/iskorotkov/avro/v2 to 2.33.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-mc57-h6j3-3hmv is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-mc57-h6j3-3hmv is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-mc57-h6j3-3hmv. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
This vulnerability affects multiple Avro decoder code paths in affected Go Avro libraries. Red Hat Product Security has assessed this issue as an Important severity vulnerability. A remote unauthenticated attacker may supply specially crafted Avro payloads containing malformed or excessively large integer values that…
Frequently Asked Questions
Is GHSA-mc57-h6j3-3hmv in your dependencies?
O3 detects GHSA-mc57-h6j3-3hmv across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.