ugorji / go

idiomatic codec and rpc lib for msgpack, cbor, json, etc. msgpack.org[Go]
MIT License
1.83k stars 294 forks source link

Duplicate key inside of a map triggers a "cannot decode unsigned integer" msgpack decode error #392

Closed giuliano-sider closed 1 year ago

giuliano-sider commented 1 year ago

When running the following program with github.com/ugorji/go/codec v1.2.10 ,

package main

import (
    "fmt"

    "github.com/ugorji/go/codec"
)

func main() {
    // the msgpack encodes `{"a": 1, "a": "b"}`
    msgpack := []byte{0x82, 0xa1, 0x61, 0xcc, 0x01, 0xa1, 0x61, 0xa1, 0x62}

    var (
        v  interface{} // value to decode/encode into
        mh codec.MsgpackHandle
    )

    dec := codec.NewDecoderBytes(msgpack, &mh)
    err := dec.Decode(&v)
    if err != nil {
        fmt.Printf("Unable to decode msgpack: %v", err)
    }
}

I get a

msgpack decode error [pos 8]: cannot decode unsigned integer: unrecognized descriptor byte: a1/string|bytes

error. It seems that if a map has duplicate keys and their type doesn't match up, the decoder throws a mysterious looking parse error. Is this somehow expected? I don't think duplicate keys in msgpack maps are explicitly banned from the spec (https://github.com/msgpack/msgpack/blob/master/spec.md). This is the stack trace at the line where the error is generated:

 0  0x00000000006a1ff8 in github.com/ugorji/go/codec.(*msgpackDecDriver).DecodeUint64
    at github.com/ugorji/go/codec@v1.2.10/msgpack.go:787
 1  0x000000000063c4a3 in github.com/ugorji/go/codec.(*Decoder).kUint64
    at github.com/ugorji/go/codec@v1.2.10/decode.go:471
 2  0x00000000006447a6 in github.com/ugorji/go/codec.(*Decoder).decodeValueNoCheckNil
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1827
 3  0x00000000006443cd in github.com/ugorji/go/codec.(*Decoder).decodeValue
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1796
 4  0x000000000063d951 in github.com/ugorji/go/codec.(*Decoder).kInterface
    at github.com/ugorji/go/codec@v1.2.10/decode.go:652
 5  0x00000000006447a6 in github.com/ugorji/go/codec.(*Decoder).decodeValueNoCheckNil
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1827
 6  0x0000000000642138 in github.com/ugorji/go/codec.(*Decoder).kMap
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1296
 7  0x00000000006447a6 in github.com/ugorji/go/codec.(*Decoder).decodeValueNoCheckNil
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1827
 8  0x00000000006443cd in github.com/ugorji/go/codec.(*Decoder).decodeValue
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1796
 9  0x00000000006442d8 in github.com/ugorji/go/codec.(*Decoder).decode
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1778
10  0x000000000063c8b2 in github.com/ugorji/go/codec.(*Decoder).kInterfaceNaked
    at github.com/ugorji/go/codec@v1.2.10/decode.go:507
11  0x000000000063d7a5 in github.com/ugorji/go/codec.(*Decoder).kInterface
    at github.com/ugorji/go/codec@v1.2.10/decode.go:627
12  0x00000000006447a6 in github.com/ugorji/go/codec.(*Decoder).decodeValueNoCheckNil
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1827
13  0x00000000006443cd in github.com/ugorji/go/codec.(*Decoder).decodeValue
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1796
14  0x0000000000643d0d in github.com/ugorji/go/codec.(*Decoder).decode
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1769
15  0x0000000000642e25 in github.com/ugorji/go/codec.(*Decoder).MustDecode
15  0x0000000000642e25 in github.com/ugorji/go/codec.(*Decoder).MustDecode
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1579
16  0x0000000000642c25 in github.com/ugorji/go/codec.(*Decoder).Decode
    at github.com/ugorji/go/codec@v1.2.10/decode.go:1564
17  0x00000000006b2e75 in main.main
    at ./msgpack.go:35

It looks like the msgpack decoder wants to get an integer (Uint64?) for the value of the second "a" element in the map, but when it sees that the value is a string, it panics.

ugorji commented 1 year ago

The problem here, is that an interface{} value contained a number originally. You then tried to decode a string into that value. That's why it failed - it basically saw a number first, and tried to decode a number into it, and failed.

To resolve, set InterfaceReset = true in the Handle.

var mh codec.MsgpackHandle
mh.InterfaceReset = true

See https://pkg.go.dev/github.com/ugorji/go/codec#DecodeOptions ... and I copy docs for InterfaceReset below

    // InterfaceReset controls how we decode into an interface.
    //
    // By default, when we see a field that is an interface{...},
    // or a map with interface{...} value, we will attempt decoding into the
    // "contained" value.
    //
    // However, this prevents us from reading a string into an interface{}
    // that formerly contained a number.
    //
    // If true, we will decode into a new "blank" value, and set that in the interface.
    // If false, we will decode into whatever is contained in the interface.
    InterfaceReset bool