fix: clamp DecodeUntypedMap allocation at maxMapSize#76
Conversation
DecodeUntypedMap allocated map[interface{}]interface{} with the
attacker-declared capacity, unlike every sibling map decode path
(decodeMapValue, decodeMapStringInterfaceN, decodeTypedMapN), which
clamp the size hint at maxMapSize unless DisableAllocLimit is set. A
map32 header declaring ~4G entries forced a multi-GB upfront map
allocation before any payload was read.
Found by internal security review of #63.
There was a problem hiding this comment.
Code Review
This pull request limits the initial allocation size of untyped maps during decoding to prevent excessive memory usage when a large map size is declared. Feedback focuses on 32-bit architecture compatibility: first, a signed integer overflow can bypass the nil map check and cause a panic in make, which requires adding a check for n < -1; second, the test case using 0xffffffff will overflow to -1 on 32-bit systems and should be updated to 0x7fffffff to avoid false positives.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| ln := n | ||
| if d.flags&disableAllocLimitFlag == 0 { | ||
| ln = min(ln, maxMapSize) | ||
| } | ||
|
|
||
| m := make(map[interface{}]interface{}, ln) |
There was a problem hiding this comment.
On 32-bit architectures, an extremely large declared map size (e.g., 0x80000000 / 2147483648) can overflow the signed 32-bit int and result in a negative value other than -1 (e.g., -2147483648). This bypasses the n == -1 check, leading to a negative ln value and causing make(map[interface{}]interface{}, ln) to panic with panic: size out of range.
To prevent this panic, we should check if n < -1 and return an error.
Note: The other map decoding paths (decodeMapValue, decodeMapStringStringPtr, and decodeTypedMapN) also suffer from this same overflow panic on 32-bit systems and should be updated similarly.
if n < -1 {
return nil, fmt.Errorf("msgpack: invalid map length: %d", n)
}
ln := n
if d.flags&disableAllocLimitFlag == 0 {
ln = min(ln, maxMapSize)
}
m := make(map[interface{}]interface{}, ln)| // hint must be clamped at maxMapSize before allocation (with the old | ||
| // code this allocated a multi-GB map upfront), then fail decoding the | ||
| // first key. | ||
| data := []byte{0xdf, 0xff, 0xff, 0xff, 0xff} |
There was a problem hiding this comment.
On 32-bit architectures, 0xffffffff (4294967295) overflows the signed 32-bit int and becomes -1. This causes DecodeMapLen() to return -1, which is interpreted as a nil map, making DecodeUntypedMap() return nil, nil without an error. As a result, this test will fail on 32-bit platforms because err is nil.
Using 0x7fffffff (2147483647) instead avoids the overflow on 32-bit systems while still being large enough to trigger the allocation clamp and fail decoding due to the missing payload.
| data := []byte{0xdf, 0xff, 0xff, 0xff, 0xff} | |
| data := []byte{0xdf, 0x7f, 0xff, 0xff, 0xff} |
Summary
DecodeUntypedMapallocatedmap[interface{}]interface{}with the attacker-declared capacity — the only map decode path missing themaxMapSizeclamp (decodeMapValue,decodeMapStringInterfaceN, anddecodeTypedMapNall have it). Amap32header declaring ~4G entries forced a multi-GB upfront map allocation before any payload was read. Found by the internal security review pass on #75.The fix mirrors the sibling paths exactly: clamp the size hint at
maxMapSize(1M) unlessDisableAllocLimit(true)is set. The map still grows to the real size if the payload genuinely contains more entries.Testing
TestDecodeUntypedMapHugeDeclaredLen: map32 declaring ~4G entries fails fast (previously attempted a multi-GB allocation).TestDecodeUntypedMap: round-trip coverage for the path (it had none).go test ./...andgo test -race ./...pass.No benchmark changes: the clamp is one branch on a cold path (map header decode).
🤖 Generated with Claude Code