Skip to content

Commit

Permalink
encoding/json: always ignore embedded pointers to unexported struct t…
Browse files Browse the repository at this point in the history
…ypes

CL 60410 fixes a bug in reflect that allows assignments to an embedded
field of a pointer to an unexported struct type.
This breaks the json package because unmarshal is now unable to assign
a newly allocated struct to such fields.

In order to be consistent in the behavior for marshal and unmarshal,
this CL changes both marshal and unmarshal to always ignore
embedded pointers to unexported structs.

Fixes golang#21357

Change-Id: If62ea11155555e61115ebb9cfa5305caf101bde5
Reviewed-on: https://go-review.googlesource.com/76851
Run-TryBot: Joe Tsai <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
dsnet committed Nov 13, 2017
1 parent 5103270 commit 0cee4b7
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 6 deletions.
19 changes: 19 additions & 0 deletions src/encoding/json/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ type embed struct {
Q int
}

type Issue21357 struct {
*embed
R int
}

type Loop struct {
Loop1 int `json:",omitempty"`
Loop2 int `json:",omitempty"`
Expand Down Expand Up @@ -866,6 +871,20 @@ var unmarshalTests = []unmarshalTest{
err: fmt.Errorf("json: unknown field \"extra\""),
disallowUnknownFields: true,
},

// Issue 21357.
// Ignore any embedded fields that are pointers to unexported structs.
{
in: `{"Q":1,"R":2}`,
ptr: new(Issue21357),
out: Issue21357{R: 2},
},
{
in: `{"Q":1,"R":2}`,
ptr: new(Issue21357),
err: fmt.Errorf("json: unknown field \"Q\""),
disallowUnknownFields: true,
},
}

func TestMarshal(t *testing.T) {
Expand Down
13 changes: 10 additions & 3 deletions src/encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1094,11 +1094,18 @@ func typeFields(t reflect.Type) []field {
isUnexported := sf.PkgPath != ""
if sf.Anonymous {
t := sf.Type
if t.Kind() == reflect.Ptr {
isPointer := t.Kind() == reflect.Ptr
if isPointer {
t = t.Elem()
}
if isUnexported && t.Kind() != reflect.Struct {
// Ignore embedded fields of unexported non-struct types.
isStruct := t.Kind() == reflect.Struct
if isUnexported && (!isStruct || isPointer) {
// Ignore embedded fields of unexported non-struct types
// or pointers to unexported struct types.
//
// The latter is forbidden because unmarshal is unable
// to assign a new struct to the unexported field.
// See https://golang.org/issue/21357
continue
}
// Do not ignore embedded fields of unexported struct types
Expand Down
7 changes: 4 additions & 3 deletions src/encoding/json/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,9 @@ func TestAnonymousFields(t *testing.T) {
want: `{"X":2,"Y":4}`,
}, {
// Exported fields of pointers to embedded structs should have their
// exported fields be serialized regardless of whether the struct types
// themselves are exported.
// exported fields be serialized only for exported struct types.
// Pointers to unexported structs are not allowed since the decoder
// is unable to allocate a struct for that field
label: "EmbeddedStructPointer",
makeInput: func() interface{} {
type (
Expand All @@ -378,7 +379,7 @@ func TestAnonymousFields(t *testing.T) {
)
return S{&s1{1, 2}, &S2{3, 4}}
},
want: `{"X":2,"Y":4}`,
want: `{"Y":4}`,
}, {
// Exported fields on embedded unexported structs at multiple levels
// of nesting should still be serialized.
Expand Down

0 comments on commit 0cee4b7

Please sign in to comment.