From dea64f1a5c07747ff7f617d4531351e7d99d477a Mon Sep 17 00:00:00 2001 From: Ignacio Van Droogenbroeck Date: Fri, 12 Jun 2026 15:25:22 -0600 Subject: [PATCH] fix: clamp DecodeUntypedMap allocation at maxMapSize 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. --- decode_map.go | 7 ++++++- msgpack_test.go | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/decode_map.go b/decode_map.go index 7ba8e10..fc3c3e7 100644 --- a/decode_map.go +++ b/decode_map.go @@ -221,7 +221,12 @@ func (d *Decoder) DecodeUntypedMap() (map[interface{}]interface{}, error) { return nil, nil } - m := make(map[interface{}]interface{}, n) + ln := n + if d.flags&disableAllocLimitFlag == 0 { + ln = min(ln, maxMapSize) + } + + m := make(map[interface{}]interface{}, ln) for i := 0; i < n; i++ { mk, err := d.decodeInterfaceCond() diff --git a/msgpack_test.go b/msgpack_test.go index 6de7319..d18a207 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -75,6 +75,26 @@ func (t *MsgpackTest) TestLargeString() { t.Equal(dst, src) } +func (t *MsgpackTest) TestDecodeUntypedMapHugeDeclaredLen() { + // A map32 header declaring ~4G entries with no payload: the map size + // 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} + dec := msgpack.NewDecoder(bytes.NewReader(data)) + _, err := dec.DecodeUntypedMap() + t.NotNil(err) +} + +func (t *MsgpackTest) TestDecodeUntypedMap() { + in := map[interface{}]interface{}{int8(1): "one", "two": int8(2)} + t.Nil(t.enc.Encode(in)) + + out, err := t.dec.DecodeUntypedMap() + t.Nil(err) + t.Equal(in, out) +} + func (t *MsgpackTest) TestSliceOfStructs() { in := []*nameStruct{{"hello"}} var out []*nameStruct