mirror of
https://github.com/golang/go
synced 2024-11-22 21:30:02 -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"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
@ -168,13 +167,19 @@ func (e *InvalidUnmarshalError) Error() 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) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
if je, ok := r.(jsonError); ok {
|
||||
err = je.error
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -295,9 +300,9 @@ func (d *decodeState) init(data []byte) *decodeState {
|
||||
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) {
|
||||
panic(d.addErrorContext(err))
|
||||
panic(jsonError{d.addErrorContext(err)})
|
||||
}
|
||||
|
||||
// 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"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -286,21 +285,20 @@ func newEncodeState() *encodeState {
|
||||
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
if je, ok := r.(jsonError); ok {
|
||||
err = je.error
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
if s, ok := r.(string); ok {
|
||||
panic(s)
|
||||
}
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
e.reflectValue(reflect.ValueOf(v), opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// error aborts the encoding by panicking with err wrapped in jsonError.
|
||||
func (e *encodeState) error(err error) {
|
||||
panic(err)
|
||||
panic(jsonError{err})
|
||||
}
|
||||
|
||||
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