1
0
mirror of https://github.com/golang/go synced 2024-09-25 01:20:13 -06:00

text/template: detect pathologically recursive template invocations

Return an error message instead of eating memory and eventually
triggering a stack overflow.

Fixes #15618

Change-Id: I3dcf1d669104690a17847a20fbfeb6d7e39e8751
Reviewed-on: https://go-review.googlesource.com/23091
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Andrew Gerrand 2016-05-12 13:55:46 -07:00
parent 8f48efb31c
commit eb69476c66
2 changed files with 28 additions and 4 deletions

View File

@ -15,14 +15,21 @@ import (
"text/template/parse"
)
// maxExecDepth specifies the maximum stack depth of templates within
// templates. This limit is only practically reached by accidentally
// recursive template invocations. This limit allows us to return
// an error instead of triggering a stack overflow.
const maxExecDepth = 100000
// state represents the state of an execution. It's not part of the
// template so that multiple executions of the same template
// can execute in parallel.
type state struct {
tmpl *Template
wr io.Writer
node parse.Node // current node, for errors
vars []variable // push-down stack of variable values.
tmpl *Template
wr io.Writer
node parse.Node // current node, for errors
vars []variable // push-down stack of variable values.
depth int // the height of the stack of executing templates.
}
// variable holds the dynamic value of a variable such as $, $x etc.
@ -363,9 +370,13 @@ func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
if tmpl == nil {
s.errorf("template %q not defined", t.Name)
}
if s.depth == maxExecDepth {
s.errorf("exceeded maximum template depth (%v)", maxExecDepth)
}
// Variables declared by the pipeline persist.
dot = s.evalPipeline(dot, t.Pipe)
newState := *s
newState.depth++
newState.tmpl = tmpl
// No dynamic scoping: template invocations inherit no variables.
newState.vars = []variable{{"$", dot}}

View File

@ -1297,3 +1297,16 @@ func TestMissingFieldOnNil(t *testing.T) {
t.Errorf("got error %q, want %q", got, want)
}
}
func TestMaxExecDepth(t *testing.T) {
tmpl := Must(New("tmpl").Parse(`{{template "tmpl" .}}`))
err := tmpl.Execute(ioutil.Discard, nil)
got := "<nil>"
if err != nil {
got = err.Error()
}
const want = "exceeded maximum template depth"
if !strings.Contains(got, want) {
t.Errorf("got error %q; want %q", got, want)
}
}