mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
encoding/json: fix extra data regression under goexperiment.jsonv2
When operating under v1 semantics in the v2 implementation, a extra data error should take precedence over any semantic error that could theoretically occur within the value itself. This change only affects code compiled under goexperiment.jsonv2. Fixes #74614 Change-Id: I055a606b053fa66b0c766ae205487b8290109285 Reviewed-on: https://go-review.googlesource.com/c/go/+/689919 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
a6eec8bdc7
commit
3636ced112
2 changed files with 24 additions and 19 deletions
|
|
@ -776,7 +776,8 @@ func (d *decoderState) ReadValue(flags *jsonwire.ValueFlags) (Value, error) {
|
||||||
|
|
||||||
// CheckNextValue checks whether the next value is syntactically valid,
|
// CheckNextValue checks whether the next value is syntactically valid,
|
||||||
// but does not advance the read offset.
|
// but does not advance the read offset.
|
||||||
func (d *decoderState) CheckNextValue() error {
|
// If last, it verifies that the stream cleanly terminates with [io.EOF].
|
||||||
|
func (d *decoderState) CheckNextValue(last bool) error {
|
||||||
d.PeekKind() // populates d.peekPos and d.peekErr
|
d.PeekKind() // populates d.peekPos and d.peekErr
|
||||||
pos, err := d.peekPos, d.peekErr
|
pos, err := d.peekPos, d.peekErr
|
||||||
d.peekPos, d.peekErr = 0, nil
|
d.peekPos, d.peekErr = 0, nil
|
||||||
|
|
@ -787,13 +788,18 @@ func (d *decoderState) CheckNextValue() error {
|
||||||
var flags jsonwire.ValueFlags
|
var flags jsonwire.ValueFlags
|
||||||
if pos, err := d.consumeValue(&flags, pos, d.Tokens.Depth()); err != nil {
|
if pos, err := d.consumeValue(&flags, pos, d.Tokens.Depth()); err != nil {
|
||||||
return wrapSyntacticError(d, err, pos, +1)
|
return wrapSyntacticError(d, err, pos, +1)
|
||||||
|
} else if last {
|
||||||
|
return d.checkEOF(pos)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckEOF verifies that the input has no more data.
|
// CheckEOF verifies that the input has no more data.
|
||||||
func (d *decoderState) CheckEOF() error {
|
func (d *decoderState) CheckEOF() error {
|
||||||
switch pos, err := d.consumeWhitespace(d.prevEnd); err {
|
return d.checkEOF(d.prevEnd)
|
||||||
|
}
|
||||||
|
func (d *decoderState) checkEOF(pos int) error {
|
||||||
|
switch pos, err := d.consumeWhitespace(pos); err {
|
||||||
case nil:
|
case nil:
|
||||||
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after top-level value")
|
err := jsonwire.NewInvalidCharacterError(d.buf[pos:], "after top-level value")
|
||||||
return wrapSyntacticError(d, err, pos, 0)
|
return wrapSyntacticError(d, err, pos, 0)
|
||||||
|
|
|
||||||
|
|
@ -409,7 +409,7 @@ func Unmarshal(in []byte, out any, opts ...Options) (err error) {
|
||||||
dec := export.GetBufferedDecoder(in, opts...)
|
dec := export.GetBufferedDecoder(in, opts...)
|
||||||
defer export.PutBufferedDecoder(dec)
|
defer export.PutBufferedDecoder(dec)
|
||||||
xd := export.Decoder(dec)
|
xd := export.Decoder(dec)
|
||||||
err = unmarshalFull(dec, out, &xd.Struct)
|
err = unmarshalDecode(dec, out, &xd.Struct, true)
|
||||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||||
return internal.TransformUnmarshalError(out, err)
|
return internal.TransformUnmarshalError(out, err)
|
||||||
}
|
}
|
||||||
|
|
@ -426,25 +426,13 @@ func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error) {
|
||||||
dec := export.GetStreamingDecoder(in, opts...)
|
dec := export.GetStreamingDecoder(in, opts...)
|
||||||
defer export.PutStreamingDecoder(dec)
|
defer export.PutStreamingDecoder(dec)
|
||||||
xd := export.Decoder(dec)
|
xd := export.Decoder(dec)
|
||||||
err = unmarshalFull(dec, out, &xd.Struct)
|
err = unmarshalDecode(dec, out, &xd.Struct, true)
|
||||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||||
return internal.TransformUnmarshalError(out, err)
|
return internal.TransformUnmarshalError(out, err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalFull(in *jsontext.Decoder, out any, uo *jsonopts.Struct) error {
|
|
||||||
switch err := unmarshalDecode(in, out, uo); err {
|
|
||||||
case nil:
|
|
||||||
return export.Decoder(in).CheckEOF()
|
|
||||||
case io.EOF:
|
|
||||||
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
|
|
||||||
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalDecode deserializes a Go value from a [jsontext.Decoder] according to
|
// UnmarshalDecode deserializes a Go value from a [jsontext.Decoder] according to
|
||||||
// the provided unmarshal options (while ignoring marshal, encode, or decode options).
|
// the provided unmarshal options (while ignoring marshal, encode, or decode options).
|
||||||
// Any unmarshal options already specified on the [jsontext.Decoder]
|
// Any unmarshal options already specified on the [jsontext.Decoder]
|
||||||
|
|
@ -463,14 +451,14 @@ func UnmarshalDecode(in *jsontext.Decoder, out any, opts ...Options) (err error)
|
||||||
defer func() { xd.Struct = optsOriginal }()
|
defer func() { xd.Struct = optsOriginal }()
|
||||||
xd.Struct.JoinWithoutCoderOptions(opts...)
|
xd.Struct.JoinWithoutCoderOptions(opts...)
|
||||||
}
|
}
|
||||||
err = unmarshalDecode(in, out, &xd.Struct)
|
err = unmarshalDecode(in, out, &xd.Struct, false)
|
||||||
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
if err != nil && xd.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||||
return internal.TransformUnmarshalError(out, err)
|
return internal.TransformUnmarshalError(out, err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err error) {
|
func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct, last bool) (err error) {
|
||||||
v := reflect.ValueOf(out)
|
v := reflect.ValueOf(out)
|
||||||
if v.Kind() != reflect.Pointer || v.IsNil() {
|
if v.Kind() != reflect.Pointer || v.IsNil() {
|
||||||
return &SemanticError{action: "unmarshal", GoType: reflect.TypeOf(out), Err: internal.ErrNonNilReference}
|
return &SemanticError{action: "unmarshal", GoType: reflect.TypeOf(out), Err: internal.ErrNonNilReference}
|
||||||
|
|
@ -481,7 +469,11 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
|
||||||
// In legacy semantics, the entirety of the next JSON value
|
// In legacy semantics, the entirety of the next JSON value
|
||||||
// was validated before attempting to unmarshal it.
|
// was validated before attempting to unmarshal it.
|
||||||
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||||
if err := export.Decoder(in).CheckNextValue(); err != nil {
|
if err := export.Decoder(in).CheckNextValue(last); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
|
||||||
|
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -495,8 +487,15 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
|
||||||
if !uo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
if !uo.Flags.Get(jsonflags.AllowDuplicateNames) {
|
||||||
export.Decoder(in).Tokens.InvalidateDisabledNamespaces()
|
export.Decoder(in).Tokens.InvalidateDisabledNamespaces()
|
||||||
}
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
offset := in.InputOffset() + int64(len(in.UnreadBuffer()))
|
||||||
|
return &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if last {
|
||||||
|
return export.Decoder(in).CheckEOF()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue