From 15e9387ca00c99ba2d69ffacaba548e0e1be0b76 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Sat, 13 Jul 2019 21:05:23 +0800 Subject: [PATCH] Improve the problem described in #1978 In the go, the stack burst will occupy 1G memory, the error message is as follows ```runtime: goroutine stack exceeds 1000000000-byte limit``` If it is a user of gin, the structure of the circular reference is defined on the server side, and the server memory may be used up as long as 100 go processes, and even the server may be down. So I suggest that the mapping function should add a limit on the number of calls, and the number of times can be configured. Of course, I tried several other modifications when I solved the stackoverflow problem described in #1978, but it was not compatible with the existing API, so I did not adopt the solution I tried. --- binding/form_mapping.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 80b1d15a..c4543f9a 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -15,6 +15,8 @@ import ( "github.com/gin-gonic/gin/internal/json" ) +var LimitMappingCallNumber = 10000 + var errUnknownType = errors.New("Unknown type") func mapUri(ptr interface{}, m map[string][]string) error { @@ -46,11 +48,11 @@ func (form formSource) TrySet(value reflect.Value, field reflect.StructField, ta } func mappingByPtr(ptr interface{}, setter setter, tag string) error { - _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag) + _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag, 0) return err } -func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { +func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string, callNumber int) (bool, error) { var vKind = value.Kind() if vKind == reflect.Ptr { @@ -60,7 +62,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag isNew = true vPtr = reflect.New(value.Type().Elem()) } - isSetted, err := mapping(vPtr.Elem(), field, setter, tag) + isSetted, err := mapping(vPtr.Elem(), field, setter, tag, callNumber+1) if err != nil { return false, err } @@ -70,6 +72,10 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag return isSetted, nil } + if callNumber > LimitMappingCallNumber { + return false, errors.New("Maybe entering a circular reference") + } + if vKind != reflect.Struct || !field.Anonymous { ok, err := tryToSetValue(value, field, setter, tag) if err != nil { @@ -89,7 +95,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag if sf.PkgPath != "" && !sf.Anonymous { // unexported continue } - ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag) + ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag, callNumber+1) if err != nil { return false, err }