Skip to content

Commit fe489c8

Browse files
committed
go/types: limit termlist lengths
This is a port of CL 340254 to go/types, with minor adjustments for errors and positions. Change-Id: I49ea1d1de8d6e27484f167b813267615d142d31c Reviewed-on: https://go-review.googlesource.com/c/go/+/342438 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent b9f135d commit fe489c8

File tree

4 files changed

+116
-4
lines changed

4 files changed

+116
-4
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Check that overlong unions don't bog down type checking.
6+
// Disallow them for now.
7+
8+
package p
9+
10+
type t int
11+
12+
type (
13+
t00 t; t01 t; t02 t; t03 t; t04 t; t05 t; t06 t; t07 t; t08 t; t09 t
14+
t10 t; t11 t; t12 t; t13 t; t14 t; t15 t; t16 t; t17 t; t18 t; t19 t
15+
t20 t; t21 t; t22 t; t23 t; t24 t; t25 t; t26 t; t27 t; t28 t; t29 t
16+
t30 t; t31 t; t32 t; t33 t; t34 t; t35 t; t36 t; t37 t; t38 t; t39 t
17+
t40 t; t41 t; t42 t; t43 t; t44 t; t45 t; t46 t; t47 t; t48 t; t49 t
18+
t50 t; t51 t; t52 t; t53 t; t54 t; t55 t; t56 t; t57 t; t58 t; t59 t
19+
t60 t; t61 t; t62 t; t63 t; t64 t; t65 t; t66 t; t67 t; t68 t; t69 t
20+
t70 t; t71 t; t72 t; t73 t; t74 t; t75 t; t76 t; t77 t; t78 t; t79 t
21+
t80 t; t81 t; t82 t; t83 t; t84 t; t85 t; t86 t; t87 t; t88 t; t89 t
22+
t90 t; t91 t; t92 t; t93 t; t94 t; t95 t; t96 t; t97 t; t98 t; t99 t
23+
)
24+
25+
type u99 interface {
26+
t00|t01|t02|t03|t04|t05|t06|t07|t08|t09|
27+
t10|t11|t12|t13|t14|t15|t16|t17|t18|t19|
28+
t20|t21|t22|t23|t24|t25|t26|t27|t28|t29|
29+
t30|t31|t32|t33|t34|t35|t36|t37|t38|t39|
30+
t40|t41|t42|t43|t44|t45|t46|t47|t48|t49|
31+
t50|t51|t52|t53|t54|t55|t56|t57|t58|t59|
32+
t60|t61|t62|t63|t64|t65|t66|t67|t68|t69|
33+
t70|t71|t72|t73|t74|t75|t76|t77|t78|t79|
34+
t80|t81|t82|t83|t84|t85|t86|t87|t88|t89|
35+
t90|t91|t92|t93|t94|t95|t96|t97|t98
36+
}
37+
38+
type u100a interface {
39+
u99|float32
40+
}
41+
42+
type u100b interface {
43+
u99|float64
44+
}
45+
46+
type u101 interface {
47+
t00|t01|t02|t03|t04|t05|t06|t07|t08|t09|
48+
t10|t11|t12|t13|t14|t15|t16|t17|t18|t19|
49+
t20|t21|t22|t23|t24|t25|t26|t27|t28|t29|
50+
t30|t31|t32|t33|t34|t35|t36|t37|t38|t39|
51+
t40|t41|t42|t43|t44|t45|t46|t47|t48|t49|
52+
t50|t51|t52|t53|t54|t55|t56|t57|t58|t59|
53+
t60|t61|t62|t63|t64|t65|t66|t67|t68|t69|
54+
t70|t71|t72|t73|t74|t75|t76|t77|t78|t79|
55+
t80|t81|t82|t83|t84|t85|t86|t87|t88|t89|
56+
t90|t91|t92|t93|t94|t95|t96|t97|t98|t99|
57+
int // ERROR cannot handle more than 100 union terms
58+
}
59+
60+
type u102 interface {
61+
int /* ERROR cannot handle more than 100 union terms */ |string|u100a
62+
}
63+
64+
type u200 interface {
65+
u100a /* ERROR cannot handle more than 100 union terms */ |u100b
66+
}

src/go/types/typeset.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,9 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
266266
pos = (*ityp.embedPos)[i]
267267
}
268268
var terms termlist
269-
switch t := under(typ).(type) {
269+
switch u := under(typ).(type) {
270270
case *Interface:
271-
tset := computeInterfaceTypeSet(check, pos, t)
271+
tset := computeInterfaceTypeSet(check, pos, u)
272272
if tset.comparable {
273273
ityp.tset.comparable = true
274274
}
@@ -277,7 +277,10 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
277277
}
278278
terms = tset.terms
279279
case *Union:
280-
tset := computeUnionTypeSet(check, pos, t)
280+
tset := computeUnionTypeSet(check, pos, u)
281+
if tset == &invalidTypeSet {
282+
continue // ignore invalid unions
283+
}
281284
terms = tset.terms
282285
case *TypeParam:
283286
// Embedding stand-alone type parameters is not permitted.
@@ -295,6 +298,8 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
295298
}
296299
// The type set of an interface is the intersection
297300
// of the type sets of all its elements.
301+
// Intersection cannot produce longer termlists and
302+
// thus cannot overflow.
298303
allTerms = allTerms.intersect(terms)
299304
}
300305
ityp.embedPos = nil // not needed anymore (errors have been reported)
@@ -337,7 +342,13 @@ func (a byUniqueMethodName) Len() int { return len(a) }
337342
func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
338343
func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
339344

345+
// invalidTypeSet is a singleton type set to signal an invalid type set
346+
// due to an error. It's also a valid empty type set, so consumers of
347+
// type sets may choose to ignore it.
348+
var invalidTypeSet _TypeSet
349+
340350
// computeUnionTypeSet may be called with check == nil.
351+
// The result is &invalidTypeSet if the union overflows.
341352
func computeUnionTypeSet(check *Checker, pos token.Pos, utyp *Union) *_TypeSet {
342353
if utyp.tset != nil {
343354
return utyp.tset
@@ -357,11 +368,21 @@ func computeUnionTypeSet(check *Checker, pos token.Pos, utyp *Union) *_TypeSet {
357368
// This case is handled during union parsing.
358369
unreachable()
359370
default:
371+
if t.typ == Typ[Invalid] {
372+
continue
373+
}
360374
terms = termlist{(*term)(t)}
361375
}
362376
// The type set of a union expression is the union
363377
// of the type sets of each term.
364378
allTerms = allTerms.union(terms)
379+
if len(allTerms) > maxTermCount {
380+
if check != nil {
381+
check.errorf(atPos(pos), _Todo, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
382+
}
383+
utyp.tset = &invalidTypeSet
384+
return utyp.tset
385+
}
365386
}
366387
utyp.tset.terms = allTerms
367388

src/go/types/typeset_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package types
6+
7+
import "testing"
8+
9+
func TestInvalidTypeSet(t *testing.T) {
10+
if !invalidTypeSet.IsEmpty() {
11+
t.Error("invalidTypeSet is not empty")
12+
}
13+
}
14+
15+
// TODO(gri) add more tests

src/go/types/union.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,22 @@ func (t *Term) String() string { return (*term)(t).String() }
4646
// ----------------------------------------------------------------------------
4747
// Implementation
4848

49+
// Avoid excessive type-checking times due to quadratic termlist operations.
50+
const maxTermCount = 100
51+
52+
// parseUnion parses the given list of type expressions tlist as a union of
53+
// those expressions. The result is a Union type, or Typ[Invalid] for some
54+
// errors.
4955
func parseUnion(check *Checker, tlist []ast.Expr) Type {
5056
var terms []*Term
5157
for _, x := range tlist {
5258
tilde, typ := parseTilde(check, x)
5359
if len(tlist) == 1 && !tilde {
54-
return typ // single type
60+
return typ // single type (optimization)
61+
}
62+
if len(terms) >= maxTermCount {
63+
check.errorf(x, _Todo, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
64+
return Typ[Invalid]
5565
}
5666
terms = append(terms, NewTerm(tilde, typ))
5767
}

0 commit comments

Comments
 (0)