Fix race conditions marshal.typeDecoders (#3072)

When constructing a recursive decoder (e.g, mapDecoder, arrayDecoder, setDecoder),
the new decoder is placed in the cache before its element decoders are created to
avoid a cycle.

If another go routine finds the new decoder in the cache, it can use it before it's
fully initialized.

Use a RWMutex to guard against this. Take the write lock out before adding the decoder
to the cache and release after the element decoders have been initialized.

Use a read lock in the decoder to function go ensure that it blocks until initialization
is complete.

toward: #3071
This commit is contained in:
Eric Halpern
2017-01-13 13:11:27 -08:00
committed by GitHub
parent 760e7f9945
commit 930e54cb6d
+20 -4
View File
@@ -370,7 +370,9 @@ func sliceDecoder(t reflect.Type) decoderFunc {
}
var decoder decoderFunc
var init sync.RWMutex
init.Lock()
defer init.Unlock()
d = func(v types.Value, rv reflect.Value) {
var slice reflect.Value
if rv.IsNil() {
@@ -378,6 +380,8 @@ func sliceDecoder(t reflect.Type) decoderFunc {
} else {
slice = rv.Slice(0, 0)
}
init.RLock()
defer init.RUnlock()
iterListOrSlice(v, t, func(v types.Value, _ uint64) {
elemRv := reflect.New(t.Elem()).Elem()
decoder(v, elemRv)
@@ -398,7 +402,9 @@ func arrayDecoder(t reflect.Type) decoderFunc {
}
var decoder decoderFunc
var init sync.RWMutex
init.Lock()
defer init.Unlock()
d = func(v types.Value, rv reflect.Value) {
size := t.Len()
list, ok := v.(types.Collection)
@@ -410,6 +416,8 @@ func arrayDecoder(t reflect.Type) decoderFunc {
if l != size {
panic(&UnmarshalTypeMismatchError{v, t, ", length does not match"})
}
init.RLock()
defer init.RUnlock()
iterListOrSlice(list, t, func(v types.Value, i uint64) {
decoder(v, rv.Index(int(i)))
})
@@ -427,7 +435,9 @@ func mapFromSetDecoder(t reflect.Type) decoderFunc {
}
var decoder decoderFunc
var init sync.RWMutex
init.Lock()
defer init.Unlock()
d = func(v types.Value, rv reflect.Value) {
m := rv
@@ -436,6 +446,8 @@ func mapFromSetDecoder(t reflect.Type) decoderFunc {
panic(&UnmarshalTypeMismatchError{v, t, `, field has "set" tag`})
}
init.RLock()
defer init.RUnlock()
nomsSet.IterAll(func(v types.Value) {
keyRv := reflect.New(t.Key()).Elem()
decoder(v, keyRv)
@@ -460,7 +472,9 @@ func mapDecoder(t reflect.Type, tags nomsTags) decoderFunc {
var keyDecoder decoderFunc
var valueDecoder decoderFunc
var init sync.RWMutex
init.Lock()
defer init.Unlock()
d = func(v types.Value, rv reflect.Value) {
m := rv
@@ -475,6 +489,8 @@ func mapDecoder(t reflect.Type, tags nomsTags) decoderFunc {
panic(&UnmarshalTypeMismatchError{v, t, ""})
}
init.RLock()
defer init.RUnlock()
nomsMap.IterAll(func(k, v types.Value) {
keyRv := reflect.New(t.Key()).Elem()
keyDecoder(k, keyRv)