diff options
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch')
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch new file mode 100644 index 0000000000..b2ab5d0669 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch | |||
@@ -0,0 +1,357 @@ | |||
1 | From ba8788ebcead55e99e631c6a1157ad7b35535d11 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Wed, 15 Jun 2022 10:43:05 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.17] go/parser: limit recursion depth | ||
5 | |||
6 | Limit nested parsing to 100,000, which prevents stack exhaustion when | ||
7 | parsing deeply nested statements, types, and expressions. Also limit | ||
8 | the scope depth to 1,000 during object resolution. | ||
9 | |||
10 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
11 | |||
12 | Fixes #53707 | ||
13 | Updates #53616 | ||
14 | Fixes CVE-2022-1962 | ||
15 | |||
16 | Change-Id: I4d7b86c1d75d0bf3c7af1fdea91582aa74272c64 | ||
17 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1491025 | ||
18 | Reviewed-by: Russ Cox <rsc@google.com> | ||
19 | Reviewed-by: Damien Neil <dneil@google.com> | ||
20 | (cherry picked from commit 6a856f08d58e4b6705c0c337d461c540c1235c83) | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/417070 | ||
22 | Reviewed-by: Heschi Kreinick <heschi@google.com> | ||
23 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
24 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
25 | |||
26 | Upstream-Status: Backport [https://github.com/golang/go/commit/ba8788ebcead55e99e631c6a1157ad7b35535d11] | ||
27 | CVE: CVE-2022-1962 | ||
28 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
29 | --- | ||
30 | src/go/parser/interface.go | 10 ++- | ||
31 | src/go/parser/parser.go | 48 ++++++++-- | ||
32 | src/go/parser/parser_test.go | 169 +++++++++++++++++++++++++++++++++++ | ||
33 | 3 files changed, 220 insertions(+), 7 deletions(-) | ||
34 | |||
35 | diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go | ||
36 | index 54f9d7b..537b327 100644 | ||
37 | --- a/src/go/parser/interface.go | ||
38 | +++ b/src/go/parser/interface.go | ||
39 | @@ -92,8 +92,11 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) | ||
40 | defer func() { | ||
41 | if e := recover(); e != nil { | ||
42 | // resume same panic if it's not a bailout | ||
43 | - if _, ok := e.(bailout); !ok { | ||
44 | + bail, ok := e.(bailout) | ||
45 | + if !ok { | ||
46 | panic(e) | ||
47 | + } else if bail.msg != "" { | ||
48 | + p.errors.Add(p.file.Position(bail.pos), bail.msg) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | @@ -188,8 +191,11 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M | ||
53 | defer func() { | ||
54 | if e := recover(); e != nil { | ||
55 | // resume same panic if it's not a bailout | ||
56 | - if _, ok := e.(bailout); !ok { | ||
57 | + bail, ok := e.(bailout) | ||
58 | + if !ok { | ||
59 | panic(e) | ||
60 | + } else if bail.msg != "" { | ||
61 | + p.errors.Add(p.file.Position(bail.pos), bail.msg) | ||
62 | } | ||
63 | } | ||
64 | p.errors.Sort() | ||
65 | diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go | ||
66 | index 31a7398..586fe90 100644 | ||
67 | --- a/src/go/parser/parser.go | ||
68 | +++ b/src/go/parser/parser.go | ||
69 | @@ -64,6 +64,10 @@ type parser struct { | ||
70 | unresolved []*ast.Ident // unresolved identifiers | ||
71 | imports []*ast.ImportSpec // list of imports | ||
72 | |||
73 | + // nestLev is used to track and limit the recursion depth | ||
74 | + // during parsing. | ||
75 | + nestLev int | ||
76 | + | ||
77 | // Label scopes | ||
78 | // (maintained by open/close LabelScope) | ||
79 | labelScope *ast.Scope // label scope for current function | ||
80 | @@ -236,6 +240,24 @@ func un(p *parser) { | ||
81 | p.printTrace(")") | ||
82 | } | ||
83 | |||
84 | +// maxNestLev is the deepest we're willing to recurse during parsing | ||
85 | +const maxNestLev int = 1e5 | ||
86 | + | ||
87 | +func incNestLev(p *parser) *parser { | ||
88 | + p.nestLev++ | ||
89 | + if p.nestLev > maxNestLev { | ||
90 | + p.error(p.pos, "exceeded max nesting depth") | ||
91 | + panic(bailout{}) | ||
92 | + } | ||
93 | + return p | ||
94 | +} | ||
95 | + | ||
96 | +// decNestLev is used to track nesting depth during parsing to prevent stack exhaustion. | ||
97 | +// It is used along with incNestLev in a similar fashion to how un and trace are used. | ||
98 | +func decNestLev(p *parser) { | ||
99 | + p.nestLev-- | ||
100 | +} | ||
101 | + | ||
102 | // Advance to the next token. | ||
103 | func (p *parser) next0() { | ||
104 | // Because of one-token look-ahead, print the previous token | ||
105 | @@ -348,8 +370,12 @@ func (p *parser) next() { | ||
106 | } | ||
107 | } | ||
108 | |||
109 | -// A bailout panic is raised to indicate early termination. | ||
110 | -type bailout struct{} | ||
111 | +// A bailout panic is raised to indicate early termination. pos and msg are | ||
112 | +// only populated when bailing out of object resolution. | ||
113 | +type bailout struct { | ||
114 | + pos token.Pos | ||
115 | + msg string | ||
116 | +} | ||
117 | |||
118 | func (p *parser) error(pos token.Pos, msg string) { | ||
119 | epos := p.file.Position(pos) | ||
120 | @@ -1030,6 +1056,8 @@ func (p *parser) parseChanType() *ast.ChanType { | ||
121 | |||
122 | // If the result is an identifier, it is not resolved. | ||
123 | func (p *parser) tryIdentOrType() ast.Expr { | ||
124 | + defer decNestLev(incNestLev(p)) | ||
125 | + | ||
126 | switch p.tok { | ||
127 | case token.IDENT: | ||
128 | return p.parseTypeName() | ||
129 | @@ -1609,7 +1637,13 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { | ||
130 | } | ||
131 | |||
132 | x := p.parseUnaryExpr(lhs) | ||
133 | - for { | ||
134 | + // We track the nesting here rather than at the entry for the function, | ||
135 | + // since it can iteratively produce a nested output, and we want to | ||
136 | + // limit how deep a structure we generate. | ||
137 | + var n int | ||
138 | + defer func() { p.nestLev -= n }() | ||
139 | + for n = 1; ; n++ { | ||
140 | + incNestLev(p) | ||
141 | op, oprec := p.tokPrec() | ||
142 | if oprec < prec1 { | ||
143 | return x | ||
144 | @@ -1628,7 +1662,7 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { | ||
145 | // The result may be a type or even a raw type ([...]int). Callers must | ||
146 | // check the result (using checkExpr or checkExprOrType), depending on | ||
147 | // context. | ||
148 | -func (p *parser) parseExpr(lhs bool) ast.Expr { | ||
149 | +func (p *parser) parseExpr(lhs bool) ast.Expr { | ||
150 | if p.trace { | ||
151 | defer un(trace(p, "Expression")) | ||
152 | } | ||
153 | @@ -1899,6 +1933,8 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { | ||
154 | } | ||
155 | |||
156 | func (p *parser) parseIfStmt() *ast.IfStmt { | ||
157 | + defer decNestLev(incNestLev(p)) | ||
158 | + | ||
159 | if p.trace { | ||
160 | defer un(trace(p, "IfStmt")) | ||
161 | } | ||
162 | @@ -2214,6 +2250,8 @@ func (p *parser) parseForStmt() ast.Stmt { | ||
163 | } | ||
164 | |||
165 | func (p *parser) parseStmt() (s ast.Stmt) { | ||
166 | + defer decNestLev(incNestLev(p)) | ||
167 | + | ||
168 | if p.trace { | ||
169 | defer un(trace(p, "Statement")) | ||
170 | } | ||
171 | diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go | ||
172 | index 25a374e..37a6a2b 100644 | ||
173 | --- a/src/go/parser/parser_test.go | ||
174 | +++ b/src/go/parser/parser_test.go | ||
175 | @@ -10,6 +10,7 @@ import ( | ||
176 | "go/ast" | ||
177 | "go/token" | ||
178 | "os" | ||
179 | + "runtime" | ||
180 | "strings" | ||
181 | "testing" | ||
182 | ) | ||
183 | @@ -569,3 +570,171 @@ type x int // comment | ||
184 | t.Errorf("got %q, want %q", comment, "// comment") | ||
185 | } | ||
186 | } | ||
187 | + | ||
188 | +var parseDepthTests = []struct { | ||
189 | + name string | ||
190 | + format string | ||
191 | + // multipler is used when a single statement may result in more than one | ||
192 | + // change in the depth level, for instance "1+(..." produces a BinaryExpr | ||
193 | + // followed by a UnaryExpr, which increments the depth twice. The test | ||
194 | + // case comment explains which nodes are triggering the multiple depth | ||
195 | + // changes. | ||
196 | + parseMultiplier int | ||
197 | + // scope is true if we should also test the statement for the resolver scope | ||
198 | + // depth limit. | ||
199 | + scope bool | ||
200 | + // scopeMultiplier does the same as parseMultiplier, but for the scope | ||
201 | + // depths. | ||
202 | + scopeMultiplier int | ||
203 | +}{ | ||
204 | + // The format expands the part inside « » many times. | ||
205 | + // A second set of brackets nested inside the first stops the repetition, | ||
206 | + // so that for example «(«1»)» expands to (((...((((1))))...))). | ||
207 | + {name: "array", format: "package main; var x «[1]»int"}, | ||
208 | + {name: "slice", format: "package main; var x «[]»int"}, | ||
209 | + {name: "struct", format: "package main; var x «struct { X «int» }»", scope: true}, | ||
210 | + {name: "pointer", format: "package main; var x «*»int"}, | ||
211 | + {name: "func", format: "package main; var x «func()»int", scope: true}, | ||
212 | + {name: "chan", format: "package main; var x «chan »int"}, | ||
213 | + {name: "chan2", format: "package main; var x «<-chan »int"}, | ||
214 | + {name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType | ||
215 | + {name: "map", format: "package main; var x «map[int]»int"}, | ||
216 | + {name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
217 | + {name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
218 | + {name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
219 | + {name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr | ||
220 | + {name: "dot", format: "package main; var x = «x.»x"}, | ||
221 | + {name: "index", format: "package main; var x = x«[1]»"}, | ||
222 | + {name: "slice", format: "package main; var x = x«[1:2]»"}, | ||
223 | + {name: "slice3", format: "package main; var x = x«[1:2:3]»"}, | ||
224 | + {name: "dottype", format: "package main; var x = x«.(any)»"}, | ||
225 | + {name: "callseq", format: "package main; var x = x«()»"}, | ||
226 | + {name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr | ||
227 | + {name: "binary", format: "package main; var x = «1+»1"}, | ||
228 | + {name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr | ||
229 | + {name: "unary", format: "package main; var x = «^»1"}, | ||
230 | + {name: "addr", format: "package main; var x = «& »x"}, | ||
231 | + {name: "star", format: "package main; var x = «*»x"}, | ||
232 | + {name: "recv", format: "package main; var x = «<-»x"}, | ||
233 | + {name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2}, // Parser nodes: Ident, CallExpr | ||
234 | + {name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr | ||
235 | + {name: "label", format: "package main; func main() { «Label:» }"}, | ||
236 | + {name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt | ||
237 | + {name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true}, | ||
238 | + {name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause | ||
239 | + {name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause | ||
240 | + {name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
241 | + {name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
242 | + {name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
243 | + {name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
244 | + {name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
245 | + {name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
246 | + {name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: GoStmt, FuncLit | ||
247 | + {name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: DeferStmt, FuncLit | ||
248 | + {name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true}, | ||
249 | +} | ||
250 | + | ||
251 | +// split splits pre«mid»post into pre, mid, post. | ||
252 | +// If the string does not have that form, split returns x, "", "". | ||
253 | +func split(x string) (pre, mid, post string) { | ||
254 | + start, end := strings.Index(x, "«"), strings.LastIndex(x, "»") | ||
255 | + if start < 0 || end < 0 { | ||
256 | + return x, "", "" | ||
257 | + } | ||
258 | + return x[:start], x[start+len("«") : end], x[end+len("»"):] | ||
259 | +} | ||
260 | + | ||
261 | +func TestParseDepthLimit(t *testing.T) { | ||
262 | + if runtime.GOARCH == "wasm" { | ||
263 | + t.Skip("causes call stack exhaustion on js/wasm") | ||
264 | + } | ||
265 | + for _, tt := range parseDepthTests { | ||
266 | + for _, size := range []string{"small", "big"} { | ||
267 | + t.Run(tt.name+"/"+size, func(t *testing.T) { | ||
268 | + n := maxNestLev + 1 | ||
269 | + if tt.parseMultiplier > 0 { | ||
270 | + n /= tt.parseMultiplier | ||
271 | + } | ||
272 | + if size == "small" { | ||
273 | + // Decrease the number of statements by 10, in order to check | ||
274 | + // that we do not fail when under the limit. 10 is used to | ||
275 | + // provide some wiggle room for cases where the surrounding | ||
276 | + // scaffolding syntax adds some noise to the depth that changes | ||
277 | + // on a per testcase basis. | ||
278 | + n -= 10 | ||
279 | + } | ||
280 | + | ||
281 | + pre, mid, post := split(tt.format) | ||
282 | + if strings.Contains(mid, "«") { | ||
283 | + left, base, right := split(mid) | ||
284 | + mid = strings.Repeat(left, n) + base + strings.Repeat(right, n) | ||
285 | + } else { | ||
286 | + mid = strings.Repeat(mid, n) | ||
287 | + } | ||
288 | + input := pre + mid + post | ||
289 | + | ||
290 | + fset := token.NewFileSet() | ||
291 | + _, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution) | ||
292 | + if size == "small" { | ||
293 | + if err != nil { | ||
294 | + t.Errorf("ParseFile(...): %v (want success)", err) | ||
295 | + } | ||
296 | + } else { | ||
297 | + expected := "exceeded max nesting depth" | ||
298 | + if err == nil || !strings.HasSuffix(err.Error(), expected) { | ||
299 | + t.Errorf("ParseFile(...) = _, %v, want %q", err, expected) | ||
300 | + } | ||
301 | + } | ||
302 | + }) | ||
303 | + } | ||
304 | + } | ||
305 | +} | ||
306 | + | ||
307 | +func TestScopeDepthLimit(t *testing.T) { | ||
308 | + if runtime.GOARCH == "wasm" { | ||
309 | + t.Skip("causes call stack exhaustion on js/wasm") | ||
310 | + } | ||
311 | + for _, tt := range parseDepthTests { | ||
312 | + if !tt.scope { | ||
313 | + continue | ||
314 | + } | ||
315 | + for _, size := range []string{"small", "big"} { | ||
316 | + t.Run(tt.name+"/"+size, func(t *testing.T) { | ||
317 | + n := maxScopeDepth + 1 | ||
318 | + if tt.scopeMultiplier > 0 { | ||
319 | + n /= tt.scopeMultiplier | ||
320 | + } | ||
321 | + if size == "small" { | ||
322 | + // Decrease the number of statements by 10, in order to check | ||
323 | + // that we do not fail when under the limit. 10 is used to | ||
324 | + // provide some wiggle room for cases where the surrounding | ||
325 | + // scaffolding syntax adds some noise to the depth that changes | ||
326 | + // on a per testcase basis. | ||
327 | + n -= 10 | ||
328 | + } | ||
329 | + | ||
330 | + pre, mid, post := split(tt.format) | ||
331 | + if strings.Contains(mid, "«") { | ||
332 | + left, base, right := split(mid) | ||
333 | + mid = strings.Repeat(left, n) + base + strings.Repeat(right, n) | ||
334 | + } else { | ||
335 | + mid = strings.Repeat(mid, n) | ||
336 | + } | ||
337 | + input := pre + mid + post | ||
338 | + | ||
339 | + fset := token.NewFileSet() | ||
340 | + _, err := ParseFile(fset, "", input, DeclarationErrors) | ||
341 | + if size == "small" { | ||
342 | + if err != nil { | ||
343 | + t.Errorf("ParseFile(...): %v (want success)", err) | ||
344 | + } | ||
345 | + } else { | ||
346 | + expected := "exceeded max scope depth during object resolution" | ||
347 | + if err == nil || !strings.HasSuffix(err.Error(), expected) { | ||
348 | + t.Errorf("ParseFile(...) = _, %v, want %q", err, expected) | ||
349 | + } | ||
350 | + } | ||
351 | + }) | ||
352 | + } | ||
353 | + } | ||
354 | +} | ||
355 | -- | ||
356 | 2.30.2 | ||
357 | |||