diff --git a/binding/binding_test.go b/binding/binding_test.go index caabaace..855592e9 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -1206,6 +1206,20 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad assert.Error(t, err) } +type Identifier struct { + Assigner *Reference `json:"assigner,omitempty"` +} + +type Reference struct { + Identifier *Identifier `json:"identifier,omitempty"` +} + +func TestLoopReference(t *testing.T) { + r := Reference{} + err := mapForm(&r, map[string][]string{}) + assert.Error(t, err) +} + func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { assert.Equal(t, name, b.Name()) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 80b1d15a..48db75c2 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -15,7 +15,10 @@ import ( "github.com/gin-gonic/gin/internal/json" ) +var LimitMappingCallNumber = 10000 + var errUnknownType = errors.New("Unknown type") +var ErrMaybeCircularReference = errors.New("Maybe entering a circular reference") func mapUri(ptr interface{}, m map[string][]string) error { return mapFormByTag(ptr, m, "uri") @@ -46,11 +49,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 +63,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 +73,10 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag return isSetted, nil } + if callNumber > LimitMappingCallNumber { + return false, ErrMaybeCircularReference + } + if vKind != reflect.Struct || !field.Anonymous { ok, err := tryToSetValue(value, field, setter, tag) if err != nil { @@ -89,7 +96,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 } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index c9d6111b..04145c21 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -52,7 +52,7 @@ func TestMappingBaseTypes(t *testing.T) { field := val.Elem().Type().Field(0) - _, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form") + _, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form", 0) assert.NoError(t, err, testName) actual := val.Elem().Field(0).Interface()