mirror of
https://github.com/golang/go
synced 2024-11-23 04:00:03 -07:00
encoding/json: make error capture logic in recover more type safe
Rather than only ignoring runtime.Error panics, which are a very narrow set of possible panic values, switch it such that the json package only captures panic values that have been properly wrapped in a jsonError struct. This ensures that only intentional panics originating from the json package are captured. Fixes #23012 Change-Id: I5e85200259edd2abb1b0512ce6cc288849151a6d Reviewed-on: https://go-review.googlesource.com/94019 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
70a04f6880
commit
91a6a2a30f
@ -14,7 +14,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
@ -168,13 +167,19 @@ func (e *InvalidUnmarshalError) Error() string {
|
|||||||
return "json: Unmarshal(nil " + e.Type.String() + ")"
|
return "json: Unmarshal(nil " + e.Type.String() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jsonError is an error wrapper type for internal use only.
|
||||||
|
// Panics with errors are wrapped in jsonError so that the top-level recover
|
||||||
|
// can distinguish intentional panics from this package.
|
||||||
|
type jsonError struct{ error }
|
||||||
|
|
||||||
func (d *decodeState) unmarshal(v interface{}) (err error) {
|
func (d *decodeState) unmarshal(v interface{}) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if _, ok := r.(runtime.Error); ok {
|
if je, ok := r.(jsonError); ok {
|
||||||
|
err = je.error
|
||||||
|
} else {
|
||||||
panic(r)
|
panic(r)
|
||||||
}
|
}
|
||||||
err = r.(error)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -295,9 +300,9 @@ func (d *decodeState) init(data []byte) *decodeState {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// error aborts the decoding by panicking with err.
|
// error aborts the decoding by panicking with err wrapped in jsonError.
|
||||||
func (d *decodeState) error(err error) {
|
func (d *decodeState) error(err error) {
|
||||||
panic(d.addErrorContext(err))
|
panic(jsonError{d.addErrorContext(err)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveError saves the first err it is called with,
|
// saveError saves the first err it is called with,
|
||||||
|
@ -2166,3 +2166,17 @@ func TestUnmarshalEmbeddedPointerUnexported(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unmarshalPanic struct{}
|
||||||
|
|
||||||
|
func (unmarshalPanic) UnmarshalJSON([]byte) error { panic(0xdead) }
|
||||||
|
|
||||||
|
func TestUnmarshalPanic(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if got := recover(); !reflect.DeepEqual(got, 0xdead) {
|
||||||
|
t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
Unmarshal([]byte("{}"), &unmarshalPanic{})
|
||||||
|
t.Fatalf("Unmarshal should have panicked")
|
||||||
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -286,21 +285,20 @@ func newEncodeState() *encodeState {
|
|||||||
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
|
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if _, ok := r.(runtime.Error); ok {
|
if je, ok := r.(jsonError); ok {
|
||||||
|
err = je.error
|
||||||
|
} else {
|
||||||
panic(r)
|
panic(r)
|
||||||
}
|
}
|
||||||
if s, ok := r.(string); ok {
|
|
||||||
panic(s)
|
|
||||||
}
|
|
||||||
err = r.(error)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
e.reflectValue(reflect.ValueOf(v), opts)
|
e.reflectValue(reflect.ValueOf(v), opts)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// error aborts the encoding by panicking with err wrapped in jsonError.
|
||||||
func (e *encodeState) error(err error) {
|
func (e *encodeState) error(err error) {
|
||||||
panic(err)
|
panic(jsonError{err})
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmptyValue(v reflect.Value) bool {
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
@ -981,3 +981,17 @@ func TestMarshalRawMessageValue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type marshalPanic struct{}
|
||||||
|
|
||||||
|
func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) }
|
||||||
|
|
||||||
|
func TestMarshalPanic(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if got := recover(); !reflect.DeepEqual(got, 0xdead) {
|
||||||
|
t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
Marshal(&marshalPanic{})
|
||||||
|
t.Error("Marshal should have panicked")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user