Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion decode_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +224 to +229

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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)


for i := 0; i < n; i++ {
mk, err := d.decodeInterfaceCond()
Expand Down
20 changes: 20 additions & 0 deletions msgpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
data := []byte{0xdf, 0xff, 0xff, 0xff, 0xff}
data := []byte{0xdf, 0x7f, 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
Expand Down
Loading