Commit eb69476c authored by Andrew Gerrand's avatar Andrew Gerrand

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/23091Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 8f48efb3
......@@ -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}}
......
......@@ -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)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment