aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2017-12-05 22:53:48 -0800
committerJoe Tsai <thebrokentoaster@gmail.com>2018-02-14 21:34:26 +0000
commit91a6a2a30f95da8ae3fb6329a71c49ed13aa12ad (patch)
tree1924c3c82b14e38b9d9e22e2227d7661b8804a77 /src/encoding/json
parent70a04f6880a2082a76f6282361b607f859db950f (diff)
downloadgo-91a6a2a30f95da8ae3fb6329a71c49ed13aa12ad.tar.xz
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>
Diffstat (limited to 'src/encoding/json')
-rw-r--r--src/encoding/json/decode.go15
-rw-r--r--src/encoding/json/decode_test.go14
-rw-r--r--src/encoding/json/encode.go12
-rw-r--r--src/encoding/json/encode_test.go14
4 files changed, 43 insertions, 12 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 536f25dc7c..f08b0a1c58 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -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,
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 34b7ec6d97..90fdf93dbd 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -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")
+}
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 1e45e445d9..68512d0225 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -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 {
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index 0f194e13d2..b90483cf35 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -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")
+}