jettison
jettison copied to clipboard
Panic when marshalling a struct with a map and a custom MarshalJSON()
Marshalling panics when a struct containing a map field and a custom MarshalJSON() is provided. Tested on Apple M1 Pro, running macOS 14.0.
The below test code:
type Fields struct {
AdditionalProperties map[string]string `json:"-"`
}
func (a Fields) MarshalJSON() ([]byte, error) {
var err error
object := make(map[string]json.RawMessage)
for fieldName, field := range a.AdditionalProperties {
object[fieldName], err = json.Marshal(field)
if err != nil {
return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
}
}
return json.Marshal(object)
}
func TestJettison(t *testing.T) {
v := Fields{
AdditionalProperties: map[string]string{
"foo": "bar",
},
}
_, err := jettison.Marshal(v)
if err != nil {
t.Fatal(err)
}
}
results in:
unexpected fault address 0x6bff37c970
fatal error: fault
[signal SIGBUS: bus error code=0x1 addr=0x6bff37c970 pc=0x100fa32d8]
goroutine 6 [running]:
runtime.throw({0x101105eaf?, 0x100fa07b4?})
/usr/local/go/src/runtime/panic.go:1077 +0x40 fp=0x1400005d910 sp=0x1400005d8e0 pc=0x100fcabd0
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:858 +0x178 fp=0x1400005d970 sp=0x1400005d910 pc=0x100fe26c8
runtime.evacuated(...)
/usr/local/go/src/runtime/map.go:205
runtime.mapiternext(0x1400005dab8)
/usr/local/go/src/runtime/map.go:897 +0xe8 fp=0x1400005d9f0 sp=0x1400005d980 pc=0x100fa32d8
runtime.mapiterinit(0xb?, 0x0?, 0x10117aa40?)
/usr/local/go/src/runtime/map.go:864 +0x2a0 fp=0x1400005da20 sp=0x1400005d9f0 pc=0x100fa31b0
ef-studio/catalyst/tests/lib.Fields.MarshalJSON({0x1400005db40?})
/Users/ziemekobel/ws/ef-studio/backend/go/catalyst/tests/lib/jettison_test.go:21 +0x58 fp=0x1400005db20 sp=0x1400005da20 pc=0x101105078
github.com/wI2L/jettison.encodeJSONMarshaler({0x10118e100?, 0x1400005dd98}, {0x1400016a000, 0x0, 0x1000}, {{0x1011b98c0, 0x10131db00}, {0x10110eada, 0x23}, 0x5, ...}, ...)
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/encode.go:692 +0x7c fp=0x1400005dbc0 sp=0x1400005db20 pc=0x1010fb9ec
github.com/wI2L/jettison.encodeMarshaler(0x1400005dd98, {0x1400016a000, 0x0, 0x1000}, {{0x1011b98c0, 0x10131db00}, {0x10110eada, 0x23}, 0x5, 0x0, ...}, ...)
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/encode.go:668 +0x2b4 fp=0x1400005dc80 sp=0x1400005dbc0 pc=0x1010fb524
github.com/wI2L/jettison.newMarshalerTypeInstr.newJSONMarshalerInstr.func5(0x1400000e1f8?, {0x1400016a000?, 0x1400005dd58?, 0x1010f7134?}, {{0x1011b98c0, 0x10131db00}, {0x10110eada, 0x23}, 0x5, 0x0, ...})
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/instruction.go:241 +0x68 fp=0x1400005dd10 sp=0x1400005dc80 pc=0x1010fdec8
github.com/wI2L/jettison.cachedInstr.wrapInlineInstr.func1(0x14000108ea0, {0x1400016a003, 0x0, 0x1000}, {{0x1011b98c0, 0x10131db00}, {0x10110eada, 0x23}, 0x5, 0x0, ...})
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/instruction.go:406 +0x94 fp=0x1400005dd90 sp=0x1400005dd10 pc=0x1010fd094
github.com/wI2L/jettison.marshalJSON({0x10118e100, 0x14000108ea0?}, {{0x1011b98c0, 0x10131db00}, {0x10110eada, 0x23}, 0x5, 0x0, 0x0, 0x0})
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/json.go:167 +0xe0 fp=0x1400005de70 sp=0x1400005dd90 pc=0x1011007b0
github.com/wI2L/jettison.Marshal({0x10118e100?, 0x14000108ea0?})
/Users/ziemekobel/go/pkg/mod/github.com/w!i2!l/[email protected]/json.go:115 +0x84 fp=0x1400005df10 sp=0x1400005de70 pc=0x101100674
ef-studio/catalyst/tests/lib.TestJettison(0x140001524e0)
/Users/ziemekobel/ws/ef-studio/backend/go/catalyst/tests/lib/jettison_test.go:36 +0x7c fp=0x1400005df60 sp=0x1400005df10 pc=0x10110525c
It's probably related to the map hiter header copied from the runtime, used to reduce allocations for map ranges. I'll have a look.
Can you tell me which version of Go you are using please, and share the unit test you seems to be using ?
@wI2L It's 1.21
And here's the entire test code:
package lib_test
import (
"encoding/json"
"fmt"
"testing"
"github.com/wI2L/jettison"
)
type Fields struct {
AdditionalProperties map[string]string `json:"-"`
}
func (a Fields) MarshalJSON() ([]byte, error) {
var err error
object := make(map[string]json.RawMessage)
for fieldName, field := range a.AdditionalProperties {
object[fieldName], err = json.Marshal(field)
if err != nil {
return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
}
}
return json.Marshal(object)
}
func TestJettison(t *testing.T) {
v := Fields{
AdditionalProperties: map[string]string{
"foo": "bar",
},
}
_, err := jettison.Marshal(v)
if err != nil {
t.Fatal(err)
}
}
@ziemekobel-ef Found the issue, I'll send a fix for it.
yes, same error here.
import "gorm.io/datatypes"
struct A {
B datatypes.JSONMap // type JSONMap map[string]interface{}
}
datatypes.JSONMap is a map[string]
type, which cause out of memory error.