mirror of
https://github.com/golang/go.git
synced 2025-11-10 05:31:03 +00:00
encoding/json: detect cyclic maps and slices
Now reports an error if cyclic maps and slices are to be encoded
instead of an infinite recursion. This case wasn't handled in CL 187920.
Fixes #40745.
Change-Id: Ia34b014ecbb71fd2663bb065ba5355a307dbcc15
GitHub-Last-Rev: 6f874944f4
GitHub-Pull-Request: golang/go#40756
Reviewed-on: https://go-review.googlesource.com/c/go/+/248358
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Trust: Daniel Martí <mvdan@mvdan.cc>
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
parent
25a33daa2b
commit
428509402b
2 changed files with 53 additions and 1 deletions
|
|
@ -779,6 +779,16 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|||
e.WriteString("null")
|
||||
return
|
||||
}
|
||||
if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {
|
||||
// We're a large number of nested ptrEncoder.encode calls deep;
|
||||
// start checking if we've run into a pointer cycle.
|
||||
ptr := v.Pointer()
|
||||
if _, ok := e.ptrSeen[ptr]; ok {
|
||||
e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})
|
||||
}
|
||||
e.ptrSeen[ptr] = struct{}{}
|
||||
defer delete(e.ptrSeen, ptr)
|
||||
}
|
||||
e.WriteByte('{')
|
||||
|
||||
// Extract and sort the keys.
|
||||
|
|
@ -801,6 +811,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|||
me.elemEnc(e, v.MapIndex(kv.v), opts)
|
||||
}
|
||||
e.WriteByte('}')
|
||||
e.ptrLevel--
|
||||
}
|
||||
|
||||
func newMapEncoder(t reflect.Type) encoderFunc {
|
||||
|
|
@ -857,7 +868,23 @@ func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|||
e.WriteString("null")
|
||||
return
|
||||
}
|
||||
if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {
|
||||
// We're a large number of nested ptrEncoder.encode calls deep;
|
||||
// start checking if we've run into a pointer cycle.
|
||||
// Here we use a struct to memorize the pointer to the first element of the slice
|
||||
// and its length.
|
||||
ptr := struct {
|
||||
ptr uintptr
|
||||
len int
|
||||
}{v.Pointer(), v.Len()}
|
||||
if _, ok := e.ptrSeen[ptr]; ok {
|
||||
e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})
|
||||
}
|
||||
e.ptrSeen[ptr] = struct{}{}
|
||||
defer delete(e.ptrSeen, ptr)
|
||||
}
|
||||
se.arrayEnc(e, v, opts)
|
||||
e.ptrLevel--
|
||||
}
|
||||
|
||||
func newSliceEncoder(t reflect.Type) encoderFunc {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue