aboutsummaryrefslogtreecommitdiff
path: root/src/go/parser/parser.go
diff options
context:
space:
mode:
authorRoland Shoemaker <bracewell@google.com>2022-06-15 10:43:05 -0700
committerMichael Knyszek <mknyszek@google.com>2022-07-12 15:20:29 +0000
commitba8788ebcead55e99e631c6a1157ad7b35535d11 (patch)
tree8da196067cd8f4dc227dd636539be87566ad3053 /src/go/parser/parser.go
parent2678d0c957193dceef336c969a9da74dd716a827 (diff)
downloadgo-ba8788ebcead55e99e631c6a1157ad7b35535d11.tar.gz
go-ba8788ebcead55e99e631c6a1157ad7b35535d11.zip
[release-branch.go1.17] go/parser: limit recursion depth
Limit nested parsing to 100,000, which prevents stack exhaustion when parsing deeply nested statements, types, and expressions. Also limit the scope depth to 1,000 during object resolution. Thanks to Juho Nurminen of Mattermost for reporting this issue. Fixes #53707 Updates #53616 Fixes CVE-2022-1962 Change-Id: I4d7b86c1d75d0bf3c7af1fdea91582aa74272c64 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1491025 Reviewed-by: Russ Cox <rsc@google.com> Reviewed-by: Damien Neil <dneil@google.com> (cherry picked from commit 6a856f08d58e4b6705c0c337d461c540c1235c83) Reviewed-on: https://go-review.googlesource.com/c/go/+/417070 Reviewed-by: Heschi Kreinick <heschi@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/go/parser/parser.go')
-rw-r--r--src/go/parser/parser.go54
1 files changed, 50 insertions, 4 deletions
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index f10c8650af..2c42b9f8cc 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -60,6 +60,10 @@ type parser struct {
inRhs bool // if set, the parser is parsing a rhs expression
imports []*ast.ImportSpec // list of imports
+
+ // nestLev is used to track and limit the recursion depth
+ // during parsing.
+ nestLev int
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
@@ -110,6 +114,24 @@ func un(p *parser) {
p.printTrace(")")
}
+// maxNestLev is the deepest we're willing to recurse during parsing
+const maxNestLev int = 1e5
+
+func incNestLev(p *parser) *parser {
+ p.nestLev++
+ if p.nestLev > maxNestLev {
+ p.error(p.pos, "exceeded max nesting depth")
+ panic(bailout{})
+ }
+ return p
+}
+
+// decNestLev is used to track nesting depth during parsing to prevent stack exhaustion.
+// It is used along with incNestLev in a similar fashion to how un and trace are used.
+func decNestLev(p *parser) {
+ p.nestLev--
+}
+
// Advance to the next token.
func (p *parser) next0() {
// Because of one-token look-ahead, print the previous token
@@ -222,8 +244,12 @@ func (p *parser) next() {
}
}
-// A bailout panic is raised to indicate early termination.
-type bailout struct{}
+// A bailout panic is raised to indicate early termination. pos and msg are
+// only populated when bailing out of object resolution.
+type bailout struct {
+ pos token.Pos
+ msg string
+}
func (p *parser) error(pos token.Pos, msg string) {
if p.trace {
@@ -1119,6 +1145,8 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
}
func (p *parser) tryIdentOrType() ast.Expr {
+ defer decNestLev(incNestLev(p))
+
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
@@ -1531,7 +1559,13 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
x = p.parseOperand()
- for {
+ // We track the nesting here rather than at the entry for the function,
+ // since it can iteratively produce a nested output, and we want to
+ // limit how deep a structure we generate.
+ var n int
+ defer func() { p.nestLev -= n }()
+ for n = 1; ; n++ {
+ incNestLev(p)
switch p.tok {
case token.PERIOD:
p.next()
@@ -1591,6 +1625,8 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
func (p *parser) parseUnaryExpr() ast.Expr {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "UnaryExpr"))
}
@@ -1673,7 +1709,13 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
}
x := p.parseUnaryExpr()
- for {
+ // We track the nesting here rather than at the entry for the function,
+ // since it can iteratively produce a nested output, and we want to
+ // limit how deep a structure we generate.
+ var n int
+ defer func() { p.nestLev -= n }()
+ for n = 1; ; n++ {
+ incNestLev(p)
op, oprec := p.tokPrec()
if oprec < prec1 {
return x
@@ -1962,6 +2004,8 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
}
func (p *parser) parseIfStmt() *ast.IfStmt {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "IfStmt"))
}
@@ -2265,6 +2309,8 @@ func (p *parser) parseForStmt() ast.Stmt {
}
func (p *parser) parseStmt() (s ast.Stmt) {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "Statement"))
}