aboutsummaryrefslogtreecommitdiff
path: root/src/text
diff options
context:
space:
mode:
authorDidier Spezia <didier.06@gmail.com>2015-05-14 16:44:58 +0000
committerRob Pike <r@golang.org>2015-05-16 00:32:21 +0000
commitebe733cb40c49148b2fe53d27ce9b1f76993591e (patch)
tree76300bfae23839514fb821b18326fa7e0ffbd599 /src/text
parentd820d5f3ab49bec0fb5f8a177ed48b99502a0be1 (diff)
downloadgo-ebe733cb40c49148b2fe53d27ce9b1f76993591e.tar.xz
text/template: fix race condition on function maps
The Template objects are supposed to be goroutine-safe once they have been parsed. This includes the text and html ones. For html/template, the escape mechanism is triggered at execution time. It may alter the internal structures of the template, so a mutex protects them against concurrent accesses. The text/template package is free of any synchronization primitive. A race condition may occur when nested templates are escaped: the escape algorithm alters the function maps of the associated text templates, while a concurrent template execution may access the function maps in read mode. The less invasive fix I have found is to introduce a RWMutex in text/template to protect the function maps. This is unfortunate but it should be effective. Fixes #9945 Change-Id: I1edb73c0ed0f1fcddd2f1516230b548b92ab1269 Reviewed-on: https://go-review.googlesource.com/10101 Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/text')
-rw-r--r--src/text/template/funcs.go2
-rw-r--r--src/text/template/template.go12
2 files changed, 12 insertions, 2 deletions
diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
index cdd187bda2..ccd0dfc80d 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -92,6 +92,8 @@ func goodFunc(typ reflect.Type) bool {
// findFunction looks for a function in the template, and global map.
func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
if tmpl != nil && tmpl.common != nil {
+ tmpl.muFuncs.RLock()
+ defer tmpl.muFuncs.RUnlock()
if fn := tmpl.execFuncs[name]; fn.IsValid() {
return fn, true
}
diff --git a/src/text/template/template.go b/src/text/template/template.go
index 8611faad9f..a7c5c8cd2c 100644
--- a/src/text/template/template.go
+++ b/src/text/template/template.go
@@ -7,18 +7,20 @@ package template
import (
"fmt"
"reflect"
+ "sync"
"text/template/parse"
)
// common holds the information shared by related templates.
type common struct {
- tmpl map[string]*Template
+ tmpl map[string]*Template
+ option option
// We use two maps, one for parsing and one for execution.
// This separation makes the API cleaner since it doesn't
// expose reflection to the client.
+ muFuncs sync.RWMutex // protects parseFuncs and execFuncs
parseFuncs FuncMap
execFuncs map[string]reflect.Value
- option option
}
// Template is the representation of a parsed template. The *parse.Tree
@@ -84,6 +86,8 @@ func (t *Template) Clone() (*Template, error) {
tmpl := v.copy(nt.common)
nt.tmpl[k] = tmpl
}
+ t.muFuncs.RLock()
+ defer t.muFuncs.RUnlock()
for k, v := range t.parseFuncs {
nt.parseFuncs[k] = v
}
@@ -146,6 +150,8 @@ func (t *Template) Delims(left, right string) *Template {
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template {
t.init()
+ t.muFuncs.Lock()
+ defer t.muFuncs.Unlock()
addValueFuncs(t.execFuncs, funcMap)
addFuncs(t.parseFuncs, funcMap)
return t
@@ -169,7 +175,9 @@ func (t *Template) Lookup(name string) *Template {
// can contain text other than space, comments, and template definitions.)
func (t *Template) Parse(text string) (*Template, error) {
t.init()
+ t.muFuncs.RLock()
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+ t.muFuncs.RUnlock()
if err != nil {
return nil, err
}