Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Panic when marshalling a struct with a map and a custom MarshalJSON() #12

Open
ziemekobel-ef opened this issue Oct 4, 2023 · 4 comments

Comments

@ziemekobel-ef
Copy link

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/jettison@v0.7.4/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/jettison@v0.7.4/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/jettison@v0.7.4/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/jettison@v0.7.4/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/jettison@v0.7.4/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/jettison@v0.7.4/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

@wI2L
Copy link
Owner

wI2L commented Oct 4, 2023

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 ?

@ziemekobel-ef
Copy link
Author

ziemekobel-ef commented Oct 4, 2023

@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)
	}
}

@wI2L
Copy link
Owner

wI2L commented Oct 6, 2023

@ziemekobel-ef Found the issue, I'll send a fix for it.

@morya
Copy link

morya commented Feb 20, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants