Attacker-controlled parameter can cause denial of service in hamba avro
Description
Hamba avro is a go lang encoder/decoder implementation of the avro codec specification. In affected versions a well-crafted string passed to avro's github.com/hamba/avro/v2.Unmarshal() can throw a fatal error: runtime: out of memory which is unrecoverable and can cause denial of service of the consumer of avro. The root cause of the issue is that avro uses part of the input to Unmarshal() to determine the size when creating a new slice and hence an attacker may consume arbitrary amounts of memory which in turn may cause the application to crash. This issue has been addressed in commit b4a402f4 which has been included in release version 2.13.0. Users are advised to upgrade. There are no known workarounds for this vulnerability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Crafted input to Hamba avro's Unmarshal causes out-of-memory crash via attacker-controlled slice size, leading to DoS.
The vulnerability resides in the github.com/hamba/avro/v2.Unmarshal() function, which uses part of the input data to determine the size when creating a new slice. An attacker can craft a string that specifies an extremely large size, causing the library to allocate an arbitrary amount of memory, resulting in a fatal out-of-memory error and a denial of service [1][3].
To exploit this, an attacker only needs to supply a malicious input to any application that calls Unmarshal() on attacker-controlled data. No authentication or special privileges are required, making the attack surface broad. The issue was discovered during a security audit of Dapr, which uses the avro library [1].
The impact is a complete denial of service: the application crashes with an unrecoverable runtime error, affecting availability. There is no risk of data confidentiality or integrity loss, but the crash can disrupt services relying on the avro library [3].
The issue has been fixed in commit b4a402f4 included in release version 2.13.0 [1][3]. Users are advised to upgrade. However, note that the repository has been archived and is no longer maintained [2]. The fix introduced a configurable maximum byte slice size via a pull request [4]. No known workarounds exist for unpatched versions [1].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/hamba/avroGo | < 2.13.0 | 2.13.0 |
github.com/hamba/avro/v2Go | < 2.13.0 | 2.13.0 |
Affected products
9- osv-coords8 versionspkg:apk/chainguard/argo-eventspkg:apk/chainguard/argo-events-compatpkg:apk/chainguard/argo-events-fipspkg:apk/chainguard/argo-events-fips-compatpkg:apk/wolfi/argo-eventspkg:apk/wolfi/argo-events-compatpkg:golang/github.com/hamba/avropkg:golang/github.com/hamba/avro/v2
< 1.9.2-r1+ 7 more
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 1.9.2-r1
- (no CPE)range: < 2.13.0
- (no CPE)range: < 2.13.0
- hamba/avrov5Range: < 2.13.0
Patches
1b4a402f41cf4feat: add max byte slice size config (#273)
4 files changed · +55 −1
config.go+14 −0 modified@@ -8,6 +8,8 @@ import ( "github.com/modern-go/reflect2" ) +const maxByteSliceSize = 1024 * 1024 + // DefaultConfig is the default API. var DefaultConfig = Config{}.Freeze() @@ -43,6 +45,10 @@ type Config struct { // Disable caching layer for encoders and decoders, forcing them to get rebuilt on every // call to Marshal() and Unmarshal() DisableCaching bool + + // MaxByteSliceSize is the maximum size of `bytes` or `string` types the Reader will create, defaulting to 1MiB. + // If this size is exceeded, the Reader returns an error. This can be disabled by setting a negative number. + MaxByteSliceSize int } // Freeze makes the configuration immutable. @@ -252,3 +258,11 @@ func (c *frozenConfig) getBlockLength() int { } return blockSize } + +func (c *frozenConfig) getMaxByteSliceSize() int { + size := c.config.MaxByteSliceSize + if size == 0 { + return maxByteSliceSize + } + return size +}
reader.go+8 −1 modified@@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "strings" "unsafe" ) @@ -216,12 +217,18 @@ func (r *Reader) ReadString() string { func (r *Reader) readBytes(op string) []byte { size := int(r.ReadLong()) if size < 0 { - r.ReportError("ReadString", "invalid "+op+" length") + fnName := "Read" + strings.ToTitle(op) + r.ReportError(fnName, "invalid "+op+" length") return nil } if size == 0 { return []byte{} } + if max := r.cfg.getMaxByteSliceSize(); max > 0 && size > max { + fnName := "Read" + strings.ToTitle(op) + r.ReportError(fnName, "size is greater than `Config.MaxByteSliceSize`") + return nil + } // The bytes are entirely in the buffer and of a reasonable size. // Use the byte slab.
reader_test.go+26 −0 modified@@ -507,6 +507,19 @@ func TestReader_ReadBytes(t *testing.T) { } } +func TestReader_ReadBytesLargerThanMaxByteSliceSize(t *testing.T) { + data := []byte{ + 246, 255, 255, 255, 255, 10, 255, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, + } + r := avro.NewReader(bytes.NewReader(data), 4) + + _ = r.ReadBytes() + + assert.Error(t, r.Error) +} + func TestReader_ReadString(t *testing.T) { tests := []struct { data []byte @@ -583,6 +596,19 @@ func TestReader_ReadString(t *testing.T) { } } +func TestReader_ReadStringLargerThanMaxByteSliceSize(t *testing.T) { + data := []byte{ + 246, 255, 255, 255, 255, 10, 255, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, + } + r := avro.NewReader(bytes.NewReader(data), 4) + + _ = r.ReadString() + + assert.Error(t, r.Error) +} + func TestReader_ReadStringFastPathIsntBoundToBuffer(t *testing.T) { data := []byte{0x06, 0x66, 0x6F, 0x6F, 0x08, 0x61, 0x76, 0x72, 0x6F} r := avro.NewReader(bytes.NewReader(data), 4)
README.md+7 −0 modified@@ -125,11 +125,18 @@ encoding and decoding. Enums may also implement `TextMarshaler` and `TextUnmarshaler`, and must resolve to valid symbols in the given enum schema. ##### Identical Underlying Types + One type can be [ConvertibleTo](https://go.dev/ref/spec#Conversions) another type if they have identical underlying types. A non-native type is allowed be used if it can be convertible to *time.Time*, *big.Rat* or *avro.LogicalDuration* for the particular of *LogicalTypes*. Ex.: `type Timestamp time.Time` +##### Untrusted Input With Bytes and Strings + +For security reasons, the configuration `Config.MaxByteSliceSize` restricts the maximum size of `bytes` and `string` types created +by the `Reader`. The default maximum size is `1MiB` and is configurable. This is required to stop untrusted input from consuming all memory and +crashing the application. Should this not be need, setting a negative number will disable the behaviour. + ### Recursive Structs At this moment recursive structs are not supported. It is planned for the future.
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-9x44-9pgq-cf45ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-37475ghsaADVISORY
- github.com/hamba/avro/blob/3abfe1e6382c5dccf2e1a00260c51a64bc1f1ca1/reader.goghsaWEB
- github.com/hamba/avro/commit/b4a402f41cf44b6094b5131286830ba9bb1eb290ghsax_refsource_MISCWEB
- github.com/hamba/avro/pull/273ghsaWEB
- github.com/hamba/avro/releases/tag/v2.13.0ghsaWEB
- github.com/hamba/avro/security/advisories/GHSA-9x44-9pgq-cf45ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.