]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: don't panic if unsafe.Sizeof/Offsetof is used with oversize types
authorRobert Griesemer <gri@golang.org>
Thu, 23 Mar 2023 17:43:56 +0000 (10:43 -0700)
committerGopher Robot <gobot@golang.org>
Mon, 27 Mar 2023 16:52:49 +0000 (16:52 +0000)
In the Sizes API, recognize an overflow (to a negative value) as a
consequence of an oversize value, and specify as such in the API.

Adjust the various size computations to take overflow into account.

Recognize a negative size or offset as an error and report it rather
than panicking.

Use the same protocol for results provided by the default (StdSizes)
and external Sizes implementations.

Add a new error code TypeTooLarge for the new errors.

Fixes #59190.
Fixes #59207.

Change-Id: I8c33a9e69932760275100112dde627289ac7695b
Reviewed-on: https://go-review.googlesource.com/c/go/+/478919
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
12 files changed:
src/cmd/compile/internal/noder/sizes.go
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/sizes.go
src/go/types/builtins.go
src/go/types/expr.go
src/go/types/sizes.go
src/internal/types/errors/code_string.go
src/internal/types/errors/codes.go
src/internal/types/testdata/fixedbugs/issue59190.go [new file with mode: 0644]
src/internal/types/testdata/fixedbugs/issue59207.go [new file with mode: 0644]
test/fixedbugs/issue59190.go [new file with mode: 0644]

index 107f4d0adfdf93d51a0f580e2488d36ae28135a5..dff8d7bb9a2360545534e186d79de6b4045e2952 100644 (file)
@@ -78,13 +78,23 @@ func isComplex(T types2.Type) bool {
 
 func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 {
        offsets := make([]int64, len(fields))
-       var o int64
+       var offs int64
        for i, f := range fields {
+               if offs < 0 {
+                       // all remaining offsets are too large
+                       offsets[i] = -1
+                       continue
+               }
+               // offs >= 0
                typ := f.Type()
                a := s.Alignof(typ)
-               o = types.RoundUp(o, a)
-               offsets[i] = o
-               o += s.Sizeof(typ)
+               offs = types.RoundUp(offs, a) // possibly < 0 if align overflows
+               offsets[i] = offs
+               if d := s.Sizeof(typ); d >= 0 && offs >= 0 {
+                       offs += d // ok to overflow to < 0
+               } else {
+                       offs = -1
+               }
        }
        return offsets
 }
@@ -112,7 +122,20 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 {
                }
                // n > 0
                // gc: Size includes alignment padding.
-               return s.Sizeof(t.Elem()) * n
+               esize := s.Sizeof(t.Elem())
+               if esize < 0 {
+                       return -1 // array element too large
+               }
+               if esize == 0 {
+                       return 0 // 0-size element
+               }
+               // esize > 0
+               // Final size is esize * n; and size must be <= maxInt64.
+               const maxInt64 = 1<<63 - 1
+               if esize > maxInt64/n {
+                       return -1 // esize * n overflows
+               }
+               return esize * n
        case *types2.Slice:
                return int64(types.PtrSize) * 3
        case *types2.Struct:
@@ -134,7 +157,7 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 {
                }
 
                // gc: Size includes alignment padding.
-               return types.RoundUp(offsets[n-1]+last, s.Alignof(t))
+               return types.RoundUp(offsets[n-1]+last, s.Alignof(t)) // may overflow to < 0 which is ok
        case *types2.Interface:
                return int64(types.PtrSize) * 2
        case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature:
index 4e4d75665230c7480645f92549dfe80d4bf74c14..94fddca19a76ce8cd6746af52a39bc3a961fcdcd 100644 (file)
@@ -725,8 +725,13 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                                check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
                        }
                } else {
+                       offs := check.conf.offsetof(base, index)
+                       if offs < 0 {
+                               check.errorf(x, TypeTooLarge, "%s is too large", x)
+                               return
+                       }
                        x.mode = constant_
-                       x.val = constant.MakeInt64(check.conf.offsetof(base, index))
+                       x.val = constant.MakeInt64(offs)
                        // result is constant - no need to record signature
                }
                x.typ = Typ[Uintptr]
@@ -744,8 +749,13 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                                check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
                        }
                } else {
+                       size := check.conf.sizeof(x.typ)
+                       if size < 0 {
+                               check.errorf(x, TypeTooLarge, "%s is too large", x)
+                               return
+                       }
                        x.mode = constant_
-                       x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+                       x.val = constant.MakeInt64(size)
                        // result is constant - no need to record signature
                }
                x.typ = Typ[Uintptr]
index 72c01863394a6e4f373cdb94be46bda725667bf8..1217d2fc7ee570a87fa20238f5a3fbe2489aac18 100644 (file)
@@ -313,6 +313,12 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                conf = check.conf
        }
 
+       sizeof := func(T Type) int64 {
+               s := conf.sizeof(T)
+               assert(s == 4 || s == 8)
+               return s
+       }
+
        switch {
        case isInteger(typ):
                x := constant.ToInt(x)
@@ -325,7 +331,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                if x, ok := constant.Int64Val(x); ok {
                        switch typ.kind {
                        case Int:
-                               var s = uint(conf.sizeof(typ)) * 8
+                               var s = uint(sizeof(typ)) * 8
                                return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
                        case Int8:
                                const s = 8
@@ -339,7 +345,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                        case Int64, UntypedInt:
                                return true
                        case Uint, Uintptr:
-                               if s := uint(conf.sizeof(typ)) * 8; s < 64 {
+                               if s := uint(sizeof(typ)) * 8; s < 64 {
                                        return 0 <= x && x <= int64(1)<<s-1
                                }
                                return 0 <= x
@@ -361,7 +367,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                // x does not fit into int64
                switch n := constant.BitLen(x); typ.kind {
                case Uint, Uintptr:
-                       var s = uint(conf.sizeof(typ)) * 8
+                       var s = uint(sizeof(typ)) * 8
                        return constant.Sign(x) >= 0 && n <= int(s)
                case Uint64:
                        return constant.Sign(x) >= 0 && n <= 64
index c99a12b2e9205705d8134fbf38a085fcc1155fc5..59f600a05b10b99f6a4fd11872e81de4af13a13a 100644 (file)
@@ -10,14 +10,17 @@ package types2
 type Sizes interface {
        // Alignof returns the alignment of a variable of type T.
        // Alignof must implement the alignment guarantees required by the spec.
+       // The result must be >= 1.
        Alignof(T Type) int64
 
        // Offsetsof returns the offsets of the given struct fields, in bytes.
        // Offsetsof must implement the offset guarantees required by the spec.
+       // A negative entry in the result indicates that the struct is too large.
        Offsetsof(fields []*Var) []int64
 
        // Sizeof returns the size of a variable of type T.
        // Sizeof must implement the size guarantees required by the spec.
+       // A negative result indicates that T is too large.
        Sizeof(T Type) int64
 }
 
@@ -44,7 +47,11 @@ type StdSizes struct {
        MaxAlign int64 // maximum alignment in bytes - must be >= 1
 }
 
-func (s *StdSizes) Alignof(T Type) int64 {
+func (s *StdSizes) Alignof(T Type) (result int64) {
+       defer func() {
+               assert(result >= 1)
+       }()
+
        // For arrays and structs, alignment is defined in terms
        // of alignment of the elements and fields, respectively.
        switch t := under(T).(type) {
@@ -89,7 +96,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
        case *TypeParam, *Union:
                unreachable()
        }
-       a := s.Sizeof(T) // may be 0
+       a := s.Sizeof(T) // may be 0 or negative
        // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
        if a < 1 {
                return 1
@@ -118,12 +125,22 @@ func IsSyncAtomicAlign64(T Type) bool {
 
 func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
        offsets := make([]int64, len(fields))
-       var o int64
+       var offs int64
        for i, f := range fields {
+               if offs < 0 {
+                       // all remaining offsets are too large
+                       offsets[i] = -1
+                       continue
+               }
+               // offs >= 0
                a := s.Alignof(f.typ)
-               o = align(o, a)
-               offsets[i] = o
-               o += s.Sizeof(f.typ)
+               offs = align(offs, a) // possibly < 0 if align overflows
+               offsets[i] = offs
+               if d := s.Sizeof(f.typ); d >= 0 && offs >= 0 {
+                       offs += d // ok to overflow to < 0
+               } else {
+                       offs = -1 // f.typ or offs is too large
+               }
        }
        return offsets
 }
@@ -163,9 +180,27 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                        return 0
                }
                // n > 0
+               esize := s.Sizeof(t.elem)
+               if esize < 0 {
+                       return -1 // element too large
+               }
+               if esize == 0 {
+                       return 0 // 0-size element
+               }
+               // esize > 0
                a := s.Alignof(t.elem)
-               z := s.Sizeof(t.elem)
-               return align(z, a)*(n-1) + z
+               ea := align(esize, a) // possibly < 0 if align overflows
+               if ea < 0 {
+                       return -1
+               }
+               // ea >= 1
+               n1 := n - 1 // n1 >= 0
+               // Final size is ea*n1 + esize; and size must be <= maxInt64.
+               const maxInt64 = 1<<63 - 1
+               if n1 > 0 && ea > maxInt64/n1 {
+                       return -1 // ea*n1 overflows
+               }
+               return ea*n1 + esize // may still overflow to < 0 which is ok
        case *Slice:
                return s.WordSize * 3
        case *Struct:
@@ -174,7 +209,12 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                        return 0
                }
                offsets := s.Offsetsof(t.fields)
-               return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+               offs := offsets[n-1]
+               size := s.Sizeof(t.fields[n-1].typ)
+               if offs < 0 || size < 0 {
+                       return -1 // type too large
+               }
+               return offs + size // may overflow to < 0 which is ok
        case *Interface:
                // Type parameters lead to variable sizes/alignments;
                // StdSizes.Sizeof won't be called for them.
@@ -235,62 +275,69 @@ func SizesFor(compiler, arch string) Sizes {
 var stdSizes = SizesFor("gc", "amd64")
 
 func (conf *Config) alignof(T Type) int64 {
-       if s := conf.Sizes; s != nil {
-               if a := s.Alignof(T); a >= 1 {
-                       return a
-               }
-               panic("Config.Sizes.Alignof returned an alignment < 1")
+       f := stdSizes.Alignof
+       if conf.Sizes != nil {
+               f = conf.Sizes.Alignof
+       }
+       if a := f(T); a >= 1 {
+               return a
        }
-       return stdSizes.Alignof(T)
+       panic("implementation of alignof returned an alignment < 1")
 }
 
 func (conf *Config) offsetsof(T *Struct) []int64 {
        var offsets []int64
        if T.NumFields() > 0 {
                // compute offsets on demand
-               if s := conf.Sizes; s != nil {
-                       offsets = s.Offsetsof(T.fields)
-                       // sanity checks
-                       if len(offsets) != T.NumFields() {
-                               panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
-                       }
-                       for _, o := range offsets {
-                               if o < 0 {
-                                       panic("Config.Sizes.Offsetsof returned an offset < 0")
-                               }
-                       }
-               } else {
-                       offsets = stdSizes.Offsetsof(T.fields)
+               f := stdSizes.Offsetsof
+               if conf.Sizes != nil {
+                       f = conf.Sizes.Offsetsof
+               }
+               offsets = f(T.fields)
+               // sanity checks
+               if len(offsets) != T.NumFields() {
+                       panic("implementation of offsetsof returned the wrong number of offsets")
                }
        }
        return offsets
 }
 
 // offsetof returns the offset of the field specified via
-// the index sequence relative to typ. All embedded fields
-// must be structs (rather than pointer to structs).
-func (conf *Config) offsetof(typ Type, index []int) int64 {
-       var o int64
+// the index sequence relative to T. All embedded fields
+// must be structs (rather than pointers to structs).
+// If the offset is too large (because T is too large),
+// the result is negative.
+func (conf *Config) offsetof(T Type, index []int) int64 {
+       var offs int64
        for _, i := range index {
-               s := under(typ).(*Struct)
-               o += conf.offsetsof(s)[i]
-               typ = s.fields[i].typ
+               s := under(T).(*Struct)
+               d := conf.offsetsof(s)[i]
+               if d < 0 {
+                       return -1
+               }
+               offs += d
+               if offs < 0 {
+                       return -1
+               }
+               T = s.fields[i].typ
        }
-       return o
+       return offs
 }
 
+// sizeof returns the size of T.
+// If T is too large, the result is negative.
 func (conf *Config) sizeof(T Type) int64 {
-       if s := conf.Sizes; s != nil {
-               if z := s.Sizeof(T); z >= 0 {
-                       return z
-               }
-               panic("Config.Sizes.Sizeof returned a size < 0")
+       f := stdSizes.Sizeof
+       if conf.Sizes != nil {
+               f = conf.Sizes.Sizeof
        }
-       return stdSizes.Sizeof(T)
+       return f(T)
 }
 
 // align returns the smallest y >= x such that y % a == 0.
+// a must be within 1 and 8 and it must be a power of 2.
+// The result may be negative due to overflow.
 func align(x, a int64) int64 {
-       y := x + a - 1
-       return y - y%a
+       assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
+       return (x + a - 1) &^ (a - 1)
 }
index 0783f921eb46f4d0a9f1fab75d9c23aac6a4899a..783e00090b2771ba9811cdea2553c929bfc8bfa4 100644 (file)
@@ -726,8 +726,13 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                                check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
                        }
                } else {
+                       offs := check.conf.offsetof(base, index)
+                       if offs < 0 {
+                               check.errorf(x, TypeTooLarge, "%s is too large", x)
+                               return
+                       }
                        x.mode = constant_
-                       x.val = constant.MakeInt64(check.conf.offsetof(base, index))
+                       x.val = constant.MakeInt64(offs)
                        // result is constant - no need to record signature
                }
                x.typ = Typ[Uintptr]
@@ -745,8 +750,13 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                                check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
                        }
                } else {
+                       size := check.conf.sizeof(x.typ)
+                       if size < 0 {
+                               check.errorf(x, TypeTooLarge, "%s is too large", x)
+                               return
+                       }
                        x.mode = constant_
-                       x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+                       x.val = constant.MakeInt64(size)
                        // result is constant - no need to record signature
                }
                x.typ = Typ[Uintptr]
index 4dff5332eaf5eb3925c6fccce0cb36266cc278c0..3a4b30d2f21ba5e7e12360a1ab7836d0e6c4704f 100644 (file)
@@ -301,6 +301,12 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                conf = check.conf
        }
 
+       sizeof := func(T Type) int64 {
+               s := conf.sizeof(T)
+               assert(s == 4 || s == 8)
+               return s
+       }
+
        switch {
        case isInteger(typ):
                x := constant.ToInt(x)
@@ -313,7 +319,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                if x, ok := constant.Int64Val(x); ok {
                        switch typ.kind {
                        case Int:
-                               var s = uint(conf.sizeof(typ)) * 8
+                               var s = uint(sizeof(typ)) * 8
                                return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
                        case Int8:
                                const s = 8
@@ -327,7 +333,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                        case Int64, UntypedInt:
                                return true
                        case Uint, Uintptr:
-                               if s := uint(conf.sizeof(typ)) * 8; s < 64 {
+                               if s := uint(sizeof(typ)) * 8; s < 64 {
                                        return 0 <= x && x <= int64(1)<<s-1
                                }
                                return 0 <= x
@@ -349,7 +355,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
                // x does not fit into int64
                switch n := constant.BitLen(x); typ.kind {
                case Uint, Uintptr:
-                       var s = uint(conf.sizeof(typ)) * 8
+                       var s = uint(sizeof(typ)) * 8
                        return constant.Sign(x) >= 0 && n <= int(s)
                case Uint64:
                        return constant.Sign(x) >= 0 && n <= 64
index d32866913e75a80b3d18b8c1b71cb5020ead0948..2dcaebe402a4b5e325e673ef632e0c4c3c14f3ab 100644 (file)
@@ -12,14 +12,17 @@ package types
 type Sizes interface {
        // Alignof returns the alignment of a variable of type T.
        // Alignof must implement the alignment guarantees required by the spec.
+       // The result must be >= 1.
        Alignof(T Type) int64
 
        // Offsetsof returns the offsets of the given struct fields, in bytes.
        // Offsetsof must implement the offset guarantees required by the spec.
+       // A negative entry in the result indicates that the struct is too large.
        Offsetsof(fields []*Var) []int64
 
        // Sizeof returns the size of a variable of type T.
        // Sizeof must implement the size guarantees required by the spec.
+       // A negative result indicates that T is too large.
        Sizeof(T Type) int64
 }
 
@@ -46,7 +49,11 @@ type StdSizes struct {
        MaxAlign int64 // maximum alignment in bytes - must be >= 1
 }
 
-func (s *StdSizes) Alignof(T Type) int64 {
+func (s *StdSizes) Alignof(T Type) (result int64) {
+       defer func() {
+               assert(result >= 1)
+       }()
+
        // For arrays and structs, alignment is defined in terms
        // of alignment of the elements and fields, respectively.
        switch t := under(T).(type) {
@@ -91,7 +98,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
        case *TypeParam, *Union:
                unreachable()
        }
-       a := s.Sizeof(T) // may be 0
+       a := s.Sizeof(T) // may be 0 or negative
        // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
        if a < 1 {
                return 1
@@ -120,12 +127,22 @@ func _IsSyncAtomicAlign64(T Type) bool {
 
 func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
        offsets := make([]int64, len(fields))
-       var o int64
+       var offs int64
        for i, f := range fields {
+               if offs < 0 {
+                       // all remaining offsets are too large
+                       offsets[i] = -1
+                       continue
+               }
+               // offs >= 0
                a := s.Alignof(f.typ)
-               o = align(o, a)
-               offsets[i] = o
-               o += s.Sizeof(f.typ)
+               offs = align(offs, a) // possibly < 0 if align overflows
+               offsets[i] = offs
+               if d := s.Sizeof(f.typ); d >= 0 && offs >= 0 {
+                       offs += d // ok to overflow to < 0
+               } else {
+                       offs = -1 // f.typ or offs is too large
+               }
        }
        return offsets
 }
@@ -165,9 +182,27 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                        return 0
                }
                // n > 0
+               esize := s.Sizeof(t.elem)
+               if esize < 0 {
+                       return -1 // element too large
+               }
+               if esize == 0 {
+                       return 0 // 0-size element
+               }
+               // esize > 0
                a := s.Alignof(t.elem)
-               z := s.Sizeof(t.elem)
-               return align(z, a)*(n-1) + z
+               ea := align(esize, a) // possibly < 0 if align overflows
+               if ea < 0 {
+                       return -1
+               }
+               // ea >= 1
+               n1 := n - 1 // n1 >= 0
+               // Final size is ea*n1 + esize; and size must be <= maxInt64.
+               const maxInt64 = 1<<63 - 1
+               if n1 > 0 && ea > maxInt64/n1 {
+                       return -1 // ea*n1 overflows
+               }
+               return ea*n1 + esize // may still overflow to < 0 which is ok
        case *Slice:
                return s.WordSize * 3
        case *Struct:
@@ -176,7 +211,12 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                        return 0
                }
                offsets := s.Offsetsof(t.fields)
-               return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+               offs := offsets[n-1]
+               size := s.Sizeof(t.fields[n-1].typ)
+               if offs < 0 || size < 0 {
+                       return -1 // type too large
+               }
+               return offs + size // may overflow to < 0 which is ok
        case *Interface:
                // Type parameters lead to variable sizes/alignments;
                // StdSizes.Sizeof won't be called for them.
@@ -237,62 +277,69 @@ func SizesFor(compiler, arch string) Sizes {
 var stdSizes = SizesFor("gc", "amd64")
 
 func (conf *Config) alignof(T Type) int64 {
-       if s := conf.Sizes; s != nil {
-               if a := s.Alignof(T); a >= 1 {
-                       return a
-               }
-               panic("Config.Sizes.Alignof returned an alignment < 1")
+       f := stdSizes.Alignof
+       if conf.Sizes != nil {
+               f = conf.Sizes.Alignof
+       }
+       if a := f(T); a >= 1 {
+               return a
        }
-       return stdSizes.Alignof(T)
+       panic("implementation of alignof returned an alignment < 1")
 }
 
 func (conf *Config) offsetsof(T *Struct) []int64 {
        var offsets []int64
        if T.NumFields() > 0 {
                // compute offsets on demand
-               if s := conf.Sizes; s != nil {
-                       offsets = s.Offsetsof(T.fields)
-                       // sanity checks
-                       if len(offsets) != T.NumFields() {
-                               panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
-                       }
-                       for _, o := range offsets {
-                               if o < 0 {
-                                       panic("Config.Sizes.Offsetsof returned an offset < 0")
-                               }
-                       }
-               } else {
-                       offsets = stdSizes.Offsetsof(T.fields)
+               f := stdSizes.Offsetsof
+               if conf.Sizes != nil {
+                       f = conf.Sizes.Offsetsof
+               }
+               offsets = f(T.fields)
+               // sanity checks
+               if len(offsets) != T.NumFields() {
+                       panic("implementation of offsetsof returned the wrong number of offsets")
                }
        }
        return offsets
 }
 
 // offsetof returns the offset of the field specified via
-// the index sequence relative to typ. All embedded fields
-// must be structs (rather than pointer to structs).
-func (conf *Config) offsetof(typ Type, index []int) int64 {
-       var o int64
+// the index sequence relative to T. All embedded fields
+// must be structs (rather than pointers to structs).
+// If the offset is too large (because T is too large),
+// the result is negative.
+func (conf *Config) offsetof(T Type, index []int) int64 {
+       var offs int64
        for _, i := range index {
-               s := under(typ).(*Struct)
-               o += conf.offsetsof(s)[i]
-               typ = s.fields[i].typ
+               s := under(T).(*Struct)
+               d := conf.offsetsof(s)[i]
+               if d < 0 {
+                       return -1
+               }
+               offs += d
+               if offs < 0 {
+                       return -1
+               }
+               T = s.fields[i].typ
        }
-       return o
+       return offs
 }
 
+// sizeof returns the size of T.
+// If T is too large, the result is negative.
 func (conf *Config) sizeof(T Type) int64 {
-       if s := conf.Sizes; s != nil {
-               if z := s.Sizeof(T); z >= 0 {
-                       return z
-               }
-               panic("Config.Sizes.Sizeof returned a size < 0")
+       f := stdSizes.Sizeof
+       if conf.Sizes != nil {
+               f = conf.Sizes.Sizeof
        }
-       return stdSizes.Sizeof(T)
+       return f(T)
 }
 
 // align returns the smallest y >= x such that y % a == 0.
+// a must be within 1 and 8 and it must be a power of 2.
+// The result may be negative due to overflow.
 func align(x, a int64) int64 {
-       y := x + a - 1
-       return y - y%a
+       assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
+       return (x + a - 1) &^ (a - 1)
 }
index 303cc3b388056fefa8c1461ec02e09c1aaef6f37..d00e62bf1d66e74c42ffc330d59e7d965cdab609 100644 (file)
@@ -153,6 +153,7 @@ func _() {
        _ = x[InvalidUnsafeSliceData-145]
        _ = x[InvalidUnsafeString-146]
        _ = x[InvalidClear-148]
+       _ = x[TypeTooLarge-149]
 }
 
 const (
@@ -161,7 +162,7 @@ const (
        _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot"
        _Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl"
        _Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
-       _Code_name_5 = "InvalidClear"
+       _Code_name_5 = "InvalidClearTypeTooLarge"
 )
 
 var (
@@ -169,6 +170,7 @@ var (
        _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756}
        _Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354}
        _Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603}
+       _Code_index_5 = [...]uint8{0, 12, 24}
 )
 
 func (i Code) String() string {
@@ -187,8 +189,9 @@ func (i Code) String() string {
        case 108 <= i && i <= 146:
                i -= 108
                return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]]
-       case i == 148:
-               return _Code_name_5
+       case 148 <= i && i <= 149:
+               i -= 148
+               return _Code_name_5[_Code_index_5[i]:_Code_index_5[i+1]]
        default:
                return "Code(" + strconv.FormatInt(int64(i), 10) + ")"
        }
index db7a4252c123d64346544813dd839985dd8cd7be..0982aeb39741cad5b31fdb9b9cb0bdb1c9334e4f 100644 (file)
@@ -1441,4 +1441,25 @@ const (
        //      clear(x)
        //  }
        InvalidClear
+
+       // TypeTooLarge occurs if unsafe.Sizeof or unsafe.Offsetof is
+       // called with an expression whose type is too large.
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  type E [1 << 31 - 1]int
+       //  var a [1 << 31]E
+       //  var _ = unsafe.Sizeof(a)
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  type E [1 << 31 - 1]int
+       //  var s struct {
+       //      _ [1 << 31]E
+       //      x int
+       //  }
+       // var _ = unsafe.Offsetof(s.x)
+       TypeTooLarge
 )
diff --git a/src/internal/types/testdata/fixedbugs/issue59190.go b/src/internal/types/testdata/fixedbugs/issue59190.go
new file mode 100644 (file)
index 0000000..fd08303
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2023 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 p
+
+import "unsafe"
+
+type E [1 << 30]complex128
+var a [1 << 30]E
+var _ = unsafe.Sizeof(a /* ERROR "too large" */ )
+
+var s struct {
+       _ [1 << 30]E
+       x int
+}
+var _ = unsafe.Offsetof(s /* ERROR "too large" */ .x)
+
+// Test case from issue (modified so it also triggers on 32-bit platforms).
+
+type A [1]int
+type S struct {
+       x A
+       y [1 << 30]A
+       z [1 << 30]struct{}
+}
+type T [1 << 30][1 << 30]S
+
+func _() {
+       var a A
+       var s S
+       var t T
+       _ = unsafe.Sizeof(a)
+       _ = unsafe.Sizeof(s)
+       _ = unsafe.Sizeof(t /* ERROR "too large" */ )
+}
diff --git a/src/internal/types/testdata/fixedbugs/issue59207.go b/src/internal/types/testdata/fixedbugs/issue59207.go
new file mode 100644 (file)
index 0000000..59b36e2
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2023 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 p
+
+import "unsafe"
+
+type E [1 << 32]byte
+
+var a [1 << 32]E // size of a must not overflow to 0
+var _ = unsafe.Sizeof(a /* ERROR "too large" */ )
diff --git a/test/fixedbugs/issue59190.go b/test/fixedbugs/issue59190.go
new file mode 100644 (file)
index 0000000..48273ea
--- /dev/null
@@ -0,0 +1,39 @@
+// errorcheck
+
+// Copyright 2023 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 p
+
+import "unsafe"
+
+type E [1 << 30]complex128
+
+var a [1 << 30]E
+var _ = unsafe.Sizeof(a) // ERROR "too large"
+
+var s struct {
+       _ [1 << 30]E
+       x int
+}
+var _ = unsafe.Offsetof(s.x) // ERROR "too large"
+
+// Test case from issue (modified so it also triggers on 32-bit platforms).
+
+type A [1]int
+type S struct {
+       x A
+       y [1 << 30]A
+       z [1 << 30]struct{}
+}
+type T [1 << 30][1 << 30]S
+
+func _() {
+       var a A
+       var s S
+       var t T
+       _ = unsafe.Sizeof(a)
+       _ = unsafe.Sizeof(s)
+       _ = unsafe.Sizeof(t) // ERROR "too large"
+}