Skip to content

Commit 9c1530d

Browse files
committed
text/template: special depth limit for WASM
Deeply nested parenthesized expressions in templates can cause stack overflows in WebAssembly environments, which typically have more restricted stack space than native platforms. This change introduces a separate, lower maximum depth constant for WASM (1000 levels vs 10000 for other platforms), and modifies the tests to use the appropriate limit based on the current architecture. Similar to the approach used in encoding/xml package.
1 parent f067d5a commit 9c1530d

File tree

2 files changed

+17
-5
lines changed

2 files changed

+17
-5
lines changed

src/text/template/parse/parse.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const (
4646
// maxExpressionParenDepth is the maximum depth of nested parenthesized expressions.
4747
// It is used to prevent stack overflows from deep finite recursion in the parser.
4848
const maxExpressionParenDepth = 10000
49+
const maxExpressionParenDepthWasm = 1000 // Lower limit for WASM environments
4950

5051
// Copy returns a copy of the [Tree]. Any parsing state is discarded.
5152
func (t *Tree) Copy() *Tree {
@@ -793,7 +794,8 @@ func (t *Tree) term() Node {
793794
}
794795
return number
795796
case itemLeftParen:
796-
if t.parenDepth >= maxExpressionParenDepth {
797+
if t.parenDepth >= maxExpressionParenDepth ||
798+
runtime.GOARCH == "wasm" && t.parenDepth >= maxExpressionParenDepthWasm {
797799
t.errorf("max expression depth exceeded")
798800
}
799801
t.parenDepth++

src/text/template/parse/parse_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package parse
77
import (
88
"flag"
99
"fmt"
10+
"runtime"
1011
"strings"
1112
"testing"
1213
)
@@ -330,12 +331,12 @@ var parseTests = []parseTest{
330331

331332
// Parenthesis nesting depth tests
332333
{"paren nesting normal", "{{( ( ( ( (1) ) ) ) )}}", noError, "{{(((((1)))))}}"},
333-
{"paren nesting at limit", "{{" + buildNestedParenExpression(10000, "1") + "}}", noError, "{{" + buildNestedParenExpression(10000, "1") + "}}"},
334-
{"paren nesting exceeds limit", "{{" + buildNestedParenExpression(10001, "1") + "}}", hasError, "template: test:1: max expression depth exceeded"},
334+
{"paren nesting at limit", "{{" + buildNestedParenExpression(getMaxParenDepth(), "1") + "}}", noError, "{{" + buildNestedParenExpression(getMaxParenDepth(), "1") + "}}"},
335+
{"paren nesting exceeds limit", "{{" + buildNestedParenExpression(getMaxParenDepth()+1, "1") + "}}", hasError, "template: test:1: max expression depth exceeded"},
335336
{"paren nesting in pipeline", "{{ ( ( ( ( (1) ) ) ) ) | printf }}", noError, "{{(((((1))))) | printf}}"},
336-
{"paren nesting in pipeline exceeds limit", "{{ " + buildNestedParenExpression(10001, "1") + " | printf }}", hasError, "template: test:1: max expression depth exceeded"},
337+
{"paren nesting in pipeline exceeds limit", "{{ " + buildNestedParenExpression(getMaxParenDepth()+1, "1") + " | printf }}", hasError, "template: test:1: max expression depth exceeded"},
337338
{"paren nesting with other constructs", "{{if " + buildNestedParenExpression(5, "true") + "}}YES{{end}}", noError, "{{if " + buildNestedParenExpression(5, "true") + "}}\"YES\"{{end}}"},
338-
{"paren nesting with other constructs exceeds limit", "{{if " + buildNestedParenExpression(10001, "true") + "}}YES{{end}}", hasError, "template: test:1: max expression depth exceeded"},
339+
{"paren nesting with other constructs exceeds limit", "{{if " + buildNestedParenExpression(getMaxParenDepth()+1, "true") + "}}YES{{end}}", hasError, "template: test:1: max expression depth exceeded"},
339340
}
340341

341342
var builtins = map[string]any{
@@ -730,3 +731,12 @@ func BenchmarkListString(b *testing.B) {
730731
func buildNestedParenExpression(depth int, content string) string {
731732
return strings.Repeat("(", depth) + content + strings.Repeat(")", depth)
732733
}
734+
735+
// getMaxParenDepth returns the appropriate parenthesis nesting depth limit
736+
// based on the current architecture.
737+
func getMaxParenDepth() int {
738+
if runtime.GOARCH == "wasm" {
739+
return maxExpressionParenDepthWasm
740+
}
741+
return maxExpressionParenDepth
742+
}

0 commit comments

Comments
 (0)