1
0
mirror of https://github.com/json-iterator/go.git synced 2024-11-27 08:30:57 +02:00

Adds MaxDepth config option

Defaults to 10,000 to match the existing maxDepth constant everywhetre,
except when using `ConfigCompatibleWithStandardLibrary` - which retains
the limitless depth (and causes a stack overflow).

Added tests for the new config, and also up to jsoniter's stack overflow limit.
This commit is contained in:
Ben Brooks 2019-11-11 16:13:57 +00:00
parent dc11f49689
commit d296277d5c
3 changed files with 59 additions and 5 deletions

View File

@ -2,9 +2,11 @@ package test
import (
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/json-iterator/go"
jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
@ -24,6 +26,51 @@ func Test_customize_float_marshal(t *testing.T) {
should.Equal("1.234568", str)
}
func Test_max_depth(t *testing.T) {
deepJSON := func(depth int) []byte {
return []byte(strings.Repeat(`[`, depth) + strings.Repeat(`]`, depth))
}
tests := []struct {
jsonDepth int
cfgMaxDepth int
expectedErr string
}{
// Test the default depth
{jsonDepth: 10000, cfgMaxDepth: 0},
{jsonDepth: 10001, cfgMaxDepth: 0, expectedErr: "max depth"},
// Test max depth logic
{jsonDepth: 5, cfgMaxDepth: 6},
{jsonDepth: 5, cfgMaxDepth: 5},
{jsonDepth: 5, cfgMaxDepth: 4, expectedErr: "max depth"},
// Now try some larger values to figure out the limit
{jsonDepth: 128000, cfgMaxDepth: -1},
{jsonDepth: 512000, cfgMaxDepth: -1},
{jsonDepth: 768000, cfgMaxDepth: -1},
{jsonDepth: 860367, cfgMaxDepth: -1}, // largest value for jsoniter without stack overflow
}
for _, test := range tests {
t.Run(fmt.Sprintf("jsonDepth:%v_cfgMaxDepth:%v", test.jsonDepth, test.cfgMaxDepth), func(t *testing.T) {
if testing.Short() && test.jsonDepth >= 512000 {
t.Skip("skipping in -short due to large input data")
}
should := require.New(t)
cfg := jsoniter.Config{MaxDepth: test.cfgMaxDepth}.Froze()
var val interface{}
err := cfg.Unmarshal(deepJSON(test.jsonDepth), &val)
if test.expectedErr != "" {
should.Error(err)
should.Contains(err.Error(), test.expectedErr)
} else {
should.NoError(err)
}
})
}
}
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {

View File

@ -11,6 +11,9 @@ import (
"github.com/modern-go/reflect2"
)
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
const defaultMaxDepth = 10000
// Config customize how the API should behave.
// The API is created from Config by Froze.
type Config struct {
@ -25,6 +28,7 @@ type Config struct {
ValidateJsonRawMessage bool
ObjectFieldMustBeSimpleString bool
CaseSensitive bool
MaxDepth int
}
// API the public interface of this package.
@ -56,6 +60,7 @@ var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
MaxDepth: -1, // encoding/json has no max depth (stack overflow at 2581101)
}.Froze()
// ConfigFastest marshals float with only 6 digits precision
@ -80,6 +85,7 @@ type frozenConfig struct {
streamPool *sync.Pool
iteratorPool *sync.Pool
caseSensitive bool
maxDepth int
}
func (cfg *frozenConfig) initCache() {
@ -127,6 +133,9 @@ func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
// Froze forge API from config
func (cfg Config) Froze() API {
if cfg.MaxDepth == 0 {
cfg.MaxDepth = defaultMaxDepth
}
api := &frozenConfig{
sortMapKeys: cfg.SortMapKeys,
indentionStep: cfg.IndentionStep,
@ -134,6 +143,7 @@ func (cfg Config) Froze() API {
onlyTaggedField: cfg.OnlyTaggedField,
disallowUnknownFields: cfg.DisallowUnknownFields,
caseSensitive: cfg.CaseSensitive,
maxDepth: cfg.MaxDepth,
}
api.streamPool = &sync.Pool{
New: func() interface{} {

View File

@ -327,12 +327,9 @@ func (iter *Iterator) Read() interface{} {
}
}
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
const maxDepth = 10000
func (iter *Iterator) incrementDepth() (success bool) {
iter.depth++
if iter.depth <= maxDepth {
if iter.depth <= iter.cfg.maxDepth || iter.cfg.maxDepth < 0 {
return true
}
iter.ReportError("incrementDepth", "exceeded max depth")