]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: limit termlist lengths
authorRobert Findley <rfindley@google.com>
Mon, 16 Aug 2021 01:43:12 +0000 (21:43 -0400)
committerRobert Findley <rfindley@google.com>
Mon, 16 Aug 2021 14:27:31 +0000 (14:27 +0000)
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 <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/testdata/check/unions.go2 [new file with mode: 0644]
src/go/types/typeset.go
src/go/types/typeset_test.go [new file with mode: 0644]
src/go/types/union.go

diff --git a/src/go/types/testdata/check/unions.go2 b/src/go/types/testdata/check/unions.go2
new file mode 100644 (file)
index 0000000..bcd7de6
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Check that overlong unions don't bog down type checking.
+// Disallow them for now.
+
+package p
+
+type t int
+
+type (
+       t00 t; t01 t; t02 t; t03 t; t04 t; t05 t; t06 t; t07 t; t08 t; t09 t
+       t10 t; t11 t; t12 t; t13 t; t14 t; t15 t; t16 t; t17 t; t18 t; t19 t
+       t20 t; t21 t; t22 t; t23 t; t24 t; t25 t; t26 t; t27 t; t28 t; t29 t
+       t30 t; t31 t; t32 t; t33 t; t34 t; t35 t; t36 t; t37 t; t38 t; t39 t
+       t40 t; t41 t; t42 t; t43 t; t44 t; t45 t; t46 t; t47 t; t48 t; t49 t
+       t50 t; t51 t; t52 t; t53 t; t54 t; t55 t; t56 t; t57 t; t58 t; t59 t
+       t60 t; t61 t; t62 t; t63 t; t64 t; t65 t; t66 t; t67 t; t68 t; t69 t
+       t70 t; t71 t; t72 t; t73 t; t74 t; t75 t; t76 t; t77 t; t78 t; t79 t
+       t80 t; t81 t; t82 t; t83 t; t84 t; t85 t; t86 t; t87 t; t88 t; t89 t
+       t90 t; t91 t; t92 t; t93 t; t94 t; t95 t; t96 t; t97 t; t98 t; t99 t
+)
+
+type u99 interface {
+       t00|t01|t02|t03|t04|t05|t06|t07|t08|t09|
+       t10|t11|t12|t13|t14|t15|t16|t17|t18|t19|
+       t20|t21|t22|t23|t24|t25|t26|t27|t28|t29|
+       t30|t31|t32|t33|t34|t35|t36|t37|t38|t39|
+       t40|t41|t42|t43|t44|t45|t46|t47|t48|t49|
+       t50|t51|t52|t53|t54|t55|t56|t57|t58|t59|
+       t60|t61|t62|t63|t64|t65|t66|t67|t68|t69|
+       t70|t71|t72|t73|t74|t75|t76|t77|t78|t79|
+       t80|t81|t82|t83|t84|t85|t86|t87|t88|t89|
+       t90|t91|t92|t93|t94|t95|t96|t97|t98
+}
+
+type u100a interface {
+       u99|float32
+}
+
+type u100b interface {
+       u99|float64
+}
+
+type u101 interface {
+       t00|t01|t02|t03|t04|t05|t06|t07|t08|t09|
+       t10|t11|t12|t13|t14|t15|t16|t17|t18|t19|
+       t20|t21|t22|t23|t24|t25|t26|t27|t28|t29|
+       t30|t31|t32|t33|t34|t35|t36|t37|t38|t39|
+       t40|t41|t42|t43|t44|t45|t46|t47|t48|t49|
+       t50|t51|t52|t53|t54|t55|t56|t57|t58|t59|
+       t60|t61|t62|t63|t64|t65|t66|t67|t68|t69|
+       t70|t71|t72|t73|t74|t75|t76|t77|t78|t79|
+       t80|t81|t82|t83|t84|t85|t86|t87|t88|t89|
+       t90|t91|t92|t93|t94|t95|t96|t97|t98|t99|
+        int // ERROR cannot handle more than 100 union terms
+}
+
+type u102 interface {
+        int /* ERROR cannot handle more than 100 union terms */ |string|u100a
+}
+
+type u200 interface {
+        u100a /* ERROR cannot handle more than 100 union terms */ |u100b
+}
index fae5196e86eedbbcfa87f2d5371568db39145fc3..7bdc708d4cc154877e202f85db4ccd4a24ef2b84 100644 (file)
@@ -266,9 +266,9 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        pos = (*ityp.embedPos)[i]
                }
                var terms termlist
-               switch t := under(typ).(type) {
+               switch u := under(typ).(type) {
                case *Interface:
-                       tset := computeInterfaceTypeSet(check, pos, t)
+                       tset := computeInterfaceTypeSet(check, pos, u)
                        if tset.comparable {
                                ityp.tset.comparable = true
                        }
@@ -277,7 +277,10 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        }
                        terms = tset.terms
                case *Union:
-                       tset := computeUnionTypeSet(check, pos, t)
+                       tset := computeUnionTypeSet(check, pos, u)
+                       if tset == &invalidTypeSet {
+                               continue // ignore invalid unions
+                       }
                        terms = tset.terms
                case *TypeParam:
                        // Embedding stand-alone type parameters is not permitted.
@@ -295,6 +298,8 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                }
                // The type set of an interface is the intersection
                // of the type sets of all its elements.
+               // Intersection cannot produce longer termlists and
+               // thus cannot overflow.
                allTerms = allTerms.intersect(terms)
        }
        ityp.embedPos = nil // not needed anymore (errors have been reported)
@@ -337,7 +342,13 @@ func (a byUniqueMethodName) Len() int           { return len(a) }
 func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
 func (a byUniqueMethodName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 
+// invalidTypeSet is a singleton type set to signal an invalid type set
+// due to an error. It's also a valid empty type set, so consumers of
+// type sets may choose to ignore it.
+var invalidTypeSet _TypeSet
+
 // computeUnionTypeSet may be called with check == nil.
+// The result is &invalidTypeSet if the union overflows.
 func computeUnionTypeSet(check *Checker, pos token.Pos, utyp *Union) *_TypeSet {
        if utyp.tset != nil {
                return utyp.tset
@@ -357,11 +368,21 @@ func computeUnionTypeSet(check *Checker, pos token.Pos, utyp *Union) *_TypeSet {
                        // This case is handled during union parsing.
                        unreachable()
                default:
+                       if t.typ == Typ[Invalid] {
+                               continue
+                       }
                        terms = termlist{(*term)(t)}
                }
                // The type set of a union expression is the union
                // of the type sets of each term.
                allTerms = allTerms.union(terms)
+               if len(allTerms) > maxTermCount {
+                       if check != nil {
+                               check.errorf(atPos(pos), _Todo, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+                       }
+                       utyp.tset = &invalidTypeSet
+                       return utyp.tset
+               }
        }
        utyp.tset.terms = allTerms
 
diff --git a/src/go/types/typeset_test.go b/src/go/types/typeset_test.go
new file mode 100644 (file)
index 0000000..4fd1aa2
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import "testing"
+
+func TestInvalidTypeSet(t *testing.T) {
+       if !invalidTypeSet.IsEmpty() {
+               t.Error("invalidTypeSet is not empty")
+       }
+}
+
+// TODO(gri) add more tests
index 6038b2db2e1ea3d3987deb6c5322ea5c5dea7bd7..a0cf33c93874648df626174472b69bc55529efcf 100644 (file)
@@ -46,12 +46,22 @@ func (t *Term) String() string { return (*term)(t).String() }
 // ----------------------------------------------------------------------------
 // Implementation
 
+// Avoid excessive type-checking times due to quadratic termlist operations.
+const maxTermCount = 100
+
+// parseUnion parses the given list of type expressions tlist as a union of
+// those expressions. The result is a Union type, or Typ[Invalid] for some
+// errors.
 func parseUnion(check *Checker, tlist []ast.Expr) Type {
        var terms []*Term
        for _, x := range tlist {
                tilde, typ := parseTilde(check, x)
                if len(tlist) == 1 && !tilde {
-                       return typ // single type
+                       return typ // single type (optimization)
+               }
+               if len(terms) >= maxTermCount {
+                       check.errorf(x, _Todo, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+                       return Typ[Invalid]
                }
                terms = append(terms, NewTerm(tilde, typ))
        }