From a9ff30b9c07e75024727b9955c43e8d77cc68421 Mon Sep 17 00:00:00 2001 From: Daniel Lohse Date: Sun, 8 May 2016 09:36:14 +0200 Subject: [PATCH] Fix data race around accessing custom validators Because I already introduced a BC break, why not add another? The last build mysteriously failed due to a data race that happens when accessing the custom validators. You need to use the new `Get` and `Set` methods on the `CustomTypeTagMap` struct which protects its private validator type map with a mutex. --- types.go | 22 +++++++++++++++++++++- validator.go | 2 +- validator_test.go | 8 ++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/types.go b/types.go index 0ec33a1..5131beb 100644 --- a/types.go +++ b/types.go @@ -3,6 +3,7 @@ package govalidator import ( "reflect" "regexp" + "sync" ) // Validator is a wrapper for a validator function that returns bool and accepts string. @@ -39,10 +40,29 @@ var ParamTagRegexMap = map[string]*regexp.Regexp{ "matches": regexp.MustCompile(`matches\(([^)]+)\)`), } +type customTypeTagMap struct { + validators map[string]CustomTypeValidator + + sync.RWMutex +} + +func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) { + tm.RLock() + defer tm.RUnlock() + v, ok := tm.validators[name] + return v, ok +} + +func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) { + tm.Lock() + defer tm.Unlock() + tm.validators[name] = ctv +} + // CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function. // Use this to validate compound or custom types that need to be handled as a whole, e.g. // `type UUID [16]byte` (this would be handled as an array of bytes). -var CustomTypeTagMap = map[string]CustomTypeValidator{} +var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)} // TagMap is a map of functions, that can be used as tags for ValidateStruct function. var TagMap = map[string]Validator{ diff --git a/validator.go b/validator.go index 2e63620..674ea24 100644 --- a/validator.go +++ b/validator.go @@ -707,7 +707,7 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e if ok := isValidTag(tagOpts[0]); !ok { continue } - if validatefunc, ok := CustomTypeTagMap[tagOpts[0]]; ok { + if validatefunc, ok := CustomTypeTagMap.Get(tagOpts[0]); ok { customTypeValidatorsExist = true if result := validatefunc(v.Interface(), o.Interface()); !result { if len(tagOpts) == 2 { diff --git a/validator_test.go b/validator_test.go index 7bf1bd5..2d6b771 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1933,7 +1933,7 @@ func TestStructWithCustomByteArray(t *testing.T) { t.Parallel() // add our custom byte array validator that fails when the byte array is pristine (all zeroes) - CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { + CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { switch v := o.(type) { case StructWithCustomByteArray: if len(v.Email) > 0 { @@ -1954,14 +1954,14 @@ func TestStructWithCustomByteArray(t *testing.T) { } } return false - }) - CustomTypeTagMap["customMinLengthValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { + })) + CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { switch v := o.(type) { case StructWithCustomByteArray: return len(v.ID) >= v.CustomMinLength } return false - }) + })) testCustomByteArray := CustomByteArray{'1', '2', '3', '4', '5', '6'} var tests = []struct { param StructWithCustomByteArray