diff --git a/binding/form_mapping.go b/binding/form_mapping.go index 33389b28..0ecdea6b 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -18,7 +18,8 @@ import ( ) var ( - errUnknownType = errors.New("unknown type") + errUnknownType = errors.New("unknown type") + errRecursionTooDeep = errors.New("recursion too deep") // ErrConvertMapStringSlice can not convert to map[string][]string ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings") @@ -75,14 +76,18 @@ func (form formSource) TrySet(value reflect.Value, field reflect.StructField, ta } func mappingByPtr(ptr any, 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, deepth int) (bool, error) { if field.Tag.Get(tag) == "-" { // just ignoring this field return false, nil } + if deepth >= 1000 { + // avoid causing stackoverflow. + return false, errRecursionTooDeep + } vKind := value.Kind() @@ -93,7 +98,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag isNew = true vPtr = reflect.New(value.Type().Elem()) } - isSet, err := mapping(vPtr.Elem(), field, setter, tag) + isSet, err := mapping(vPtr.Elem(), field, setter, tag, deepth+1) if err != nil { return false, err } @@ -122,7 +127,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), sf, setter, tag) + ok, err := mapping(value.Field(i), sf, setter, tag, deepth+1) if err != nil { return false, err } diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go index afd51f9d..b39f30eb 100644 --- a/binding/form_mapping_test.go +++ b/binding/form_mapping_test.go @@ -58,7 +58,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() @@ -318,6 +318,16 @@ func TestMappingMapField(t *testing.T) { assert.Equal(t, map[string]int{"one": 1}, s.M) } +func TestMappingCircularRef(t *testing.T) { + type S struct { + S *S `form:"s"` + } + var s S + + err := mappingByPtr(&s, formSource{}, "form") + assert.ErrorIs(t, err, errRecursionTooDeep) +} + func TestMappingIgnoredCircularRef(t *testing.T) { type S struct { S *S `form:"-"`