From 6eb23deb6f2da36c949088e12839fae70d5b0e0c Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 18 Feb 2021 17:46:28 +0900 Subject: [PATCH] Fix decoding of embedded unexported pointer field --- decode_compile.go | 20 ++++++++++++++++---- decode_struct.go | 7 +++++++ decode_test.go | 6 ++---- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/decode_compile.go b/decode_compile.go index b563d50..da9f1c0 100644 --- a/decode_compile.go +++ b/decode_compile.go @@ -1,8 +1,10 @@ package json import ( + "fmt" "reflect" "strings" + "unicode" "unsafe" ) @@ -247,7 +249,7 @@ func decodeCompileInterface(typ *rtype, structName, fieldName string) (decoder, return newInterfaceDecoder(typ, structName, fieldName), nil } -func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, baseOffset uintptr) { +func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) { for k, v := range dec.fieldMap { if _, exists := conflictedMap[k]; exists { // already conflicted key @@ -257,7 +259,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM if !exists { fieldSet := &structFieldSet{ dec: v.dec, - offset: baseOffset + v.offset, + offset: field.Offset + v.offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), @@ -281,7 +283,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM if v.isTaggedKey { fieldSet := &structFieldSet{ dec: v.dec, - offset: baseOffset + v.offset, + offset: field.Offset + v.offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), @@ -318,6 +320,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD if isIgnoredStructField(field) { continue } + isUnexportedField := unicode.IsLower([]rune(field.Name)[0]) tag := structTagFromField(field) dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder) if err != nil { @@ -329,13 +332,20 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD // recursive definition continue } - decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field.Offset) + decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field) } else if pdec, ok := dec.(*ptrDecoder); ok { contentDec := pdec.contentDecoder() if pdec.typ == typ { // recursive definition continue } + var fieldSetErr error + if isUnexportedField { + fieldSetErr = fmt.Errorf( + "json: cannot set embedded pointer to unexported struct: %v", + field.Type.Elem(), + ) + } if dec, ok := contentDec.(*structDecoder); ok { for k, v := range dec.fieldMap { if _, exists := conflictedMap[k]; exists { @@ -350,6 +360,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), + err: fieldSetErr, } fieldMap[k] = fieldSet lower := strings.ToLower(k) @@ -374,6 +385,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), + err: fieldSetErr, } fieldMap[k] = fieldSet lower := strings.ToLower(k) diff --git a/decode_struct.go b/decode_struct.go index f1e347a..4264620 100644 --- a/decode_struct.go +++ b/decode_struct.go @@ -15,6 +15,7 @@ type structFieldSet struct { isTaggedKey bool key string keyLen int64 + err error } type structDecoder struct { @@ -524,6 +525,9 @@ func (d *structDecoder) decodeStream(s *stream, p unsafe.Pointer) error { } } if field != nil { + if field.err != nil { + return field.err + } if err := field.dec.decodeStream(s, unsafe.Pointer(uintptr(p)+field.offset)); err != nil { return err } @@ -591,6 +595,9 @@ func (d *structDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int6 return 0, errExpected("object value after colon", cursor) } if field != nil { + if field.err != nil { + return 0, field.err + } c, err := field.dec.decode(buf, cursor, unsafe.Pointer(uintptr(p)+field.offset)) if err != nil { return 0, err diff --git a/decode_test.go b/decode_test.go index e5e7da9..26c91fe 100644 --- a/decode_test.go +++ b/decode_test.go @@ -2539,7 +2539,6 @@ func TestInvalidStringOption(t *testing.T) { } */ -/* // Test unmarshal behavior with regards to embedded unexported structs. // // (Issue 21357) If the embedded struct is a pointer and is unallocated, @@ -2604,7 +2603,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) { in: `{"R":2,"Q":1}`, ptr: new(S1), out: &S1{R: 2}, - err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"), + err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json_test.embed1"), }, { // The top level Q field takes precedence. in: `{"Q":1}`, @@ -2626,7 +2625,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) { in: `{"R":2,"Q":1}`, ptr: new(S5), out: &S5{R: 2}, - err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"), + err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json_test.embed3"), }, { // Issue 24152, ensure decodeState.indirect does not panic. in: `{"embed1": {"Q": 1}}`, @@ -2670,7 +2669,6 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) { } } } -*/ /* func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {