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.
This commit is contained in:
guonaihong 2019-07-13 21:05:23 +08:00
parent 502c898d75
commit 15e9387ca0
1 changed files with 10 additions and 4 deletions

View File

@ -15,6 +15,8 @@ import (
"github.com/gin-gonic/gin/internal/json" "github.com/gin-gonic/gin/internal/json"
) )
var LimitMappingCallNumber = 10000
var errUnknownType = errors.New("Unknown type") var errUnknownType = errors.New("Unknown type")
func mapUri(ptr interface{}, m map[string][]string) error { 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 { 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 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() var vKind = value.Kind()
if vKind == reflect.Ptr { if vKind == reflect.Ptr {
@ -60,7 +62,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
isNew = true isNew = true
vPtr = reflect.New(value.Type().Elem()) 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 { if err != nil {
return false, err return false, err
} }
@ -70,6 +72,10 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
return isSetted, nil return isSetted, nil
} }
if callNumber > LimitMappingCallNumber {
return false, errors.New("Maybe entering a circular reference")
}
if vKind != reflect.Struct || !field.Anonymous { if vKind != reflect.Struct || !field.Anonymous {
ok, err := tryToSetValue(value, field, setter, tag) ok, err := tryToSetValue(value, field, setter, tag)
if err != nil { if err != nil {
@ -89,7 +95,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
if sf.PkgPath != "" && !sf.Anonymous { // unexported if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue 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 { if err != nil {
return false, err return false, err
} }