cmd/compile: don't panic if unsafe.Sizeof/Offsetof is used with oversize types

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>
This commit is contained in:
Robert Griesemer 2023-03-23 10:43:56 -07:00 committed by Gopher Robot
parent a0c9d153e0
commit 171850f169
12 changed files with 367 additions and 107 deletions

View File

@ -78,13 +78,23 @@ func isComplex(T types2.Type) bool {
func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 { func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 {
offsets := make([]int64, len(fields)) offsets := make([]int64, len(fields))
var o int64 var offs int64
for i, f := range fields { for i, f := range fields {
if offs < 0 {
// all remaining offsets are too large
offsets[i] = -1
continue
}
// offs >= 0
typ := f.Type() typ := f.Type()
a := s.Alignof(typ) a := s.Alignof(typ)
o = types.RoundUp(o, a) offs = types.RoundUp(offs, a) // possibly < 0 if align overflows
offsets[i] = o offsets[i] = offs
o += s.Sizeof(typ) if d := s.Sizeof(typ); d >= 0 && offs >= 0 {
offs += d // ok to overflow to < 0
} else {
offs = -1
}
} }
return offsets return offsets
} }
@ -112,7 +122,20 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 {
} }
// n > 0 // n > 0
// gc: Size includes alignment padding. // 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: case *types2.Slice:
return int64(types.PtrSize) * 3 return int64(types.PtrSize) * 3
case *types2.Struct: case *types2.Struct:
@ -134,7 +157,7 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 {
} }
// gc: Size includes alignment padding. // 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: case *types2.Interface:
return int64(types.PtrSize) * 2 return int64(types.PtrSize) * 2
case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature: case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature:

View 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())) check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
} }
} else { } else {
offs := check.conf.offsetof(base, index)
if offs < 0 {
check.errorf(x, TypeTooLarge, "%s is too large", x)
return
}
x.mode = constant_ 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 // result is constant - no need to record signature
} }
x.typ = Typ[Uintptr] 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)) check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
} }
} else { } else {
size := check.conf.sizeof(x.typ)
if size < 0 {
check.errorf(x, TypeTooLarge, "%s is too large", x)
return
}
x.mode = constant_ 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 // result is constant - no need to record signature
} }
x.typ = Typ[Uintptr] x.typ = Typ[Uintptr]

View File

@ -313,6 +313,12 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
conf = check.conf conf = check.conf
} }
sizeof := func(T Type) int64 {
s := conf.sizeof(T)
assert(s == 4 || s == 8)
return s
}
switch { switch {
case isInteger(typ): case isInteger(typ):
x := constant.ToInt(x) 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 { if x, ok := constant.Int64Val(x); ok {
switch typ.kind { switch typ.kind {
case Int: 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 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
case Int8: case Int8:
const s = 8 const s = 8
@ -339,7 +345,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
case Int64, UntypedInt: case Int64, UntypedInt:
return true return true
case Uint, Uintptr: 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 && x <= int64(1)<<s-1
} }
return 0 <= x return 0 <= x
@ -361,7 +367,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
// x does not fit into int64 // x does not fit into int64
switch n := constant.BitLen(x); typ.kind { switch n := constant.BitLen(x); typ.kind {
case Uint, Uintptr: case Uint, Uintptr:
var s = uint(conf.sizeof(typ)) * 8 var s = uint(sizeof(typ)) * 8
return constant.Sign(x) >= 0 && n <= int(s) return constant.Sign(x) >= 0 && n <= int(s)
case Uint64: case Uint64:
return constant.Sign(x) >= 0 && n <= 64 return constant.Sign(x) >= 0 && n <= 64

View File

@ -10,14 +10,17 @@ package types2
type Sizes interface { type Sizes interface {
// Alignof returns the alignment of a variable of type T. // Alignof returns the alignment of a variable of type T.
// Alignof must implement the alignment guarantees required by the spec. // Alignof must implement the alignment guarantees required by the spec.
// The result must be >= 1.
Alignof(T Type) int64 Alignof(T Type) int64
// Offsetsof returns the offsets of the given struct fields, in bytes. // Offsetsof returns the offsets of the given struct fields, in bytes.
// Offsetsof must implement the offset guarantees required by the spec. // 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 Offsetsof(fields []*Var) []int64
// Sizeof returns the size of a variable of type T. // Sizeof returns the size of a variable of type T.
// Sizeof must implement the size guarantees required by the spec. // Sizeof must implement the size guarantees required by the spec.
// A negative result indicates that T is too large.
Sizeof(T Type) int64 Sizeof(T Type) int64
} }
@ -44,7 +47,11 @@ type StdSizes struct {
MaxAlign int64 // maximum alignment in bytes - must be >= 1 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 // For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively. // of alignment of the elements and fields, respectively.
switch t := under(T).(type) { switch t := under(T).(type) {
@ -89,7 +96,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
case *TypeParam, *Union: case *TypeParam, *Union:
unreachable() 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." // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
if a < 1 { if a < 1 {
return 1 return 1
@ -118,12 +125,22 @@ func IsSyncAtomicAlign64(T Type) bool {
func (s *StdSizes) Offsetsof(fields []*Var) []int64 { func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
offsets := make([]int64, len(fields)) offsets := make([]int64, len(fields))
var o int64 var offs int64
for i, f := range fields { 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) a := s.Alignof(f.typ)
o = align(o, a) offs = align(offs, a) // possibly < 0 if align overflows
offsets[i] = o offsets[i] = offs
o += s.Sizeof(f.typ) 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 return offsets
} }
@ -163,9 +180,27 @@ func (s *StdSizes) Sizeof(T Type) int64 {
return 0 return 0
} }
// n > 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) a := s.Alignof(t.elem)
z := s.Sizeof(t.elem) ea := align(esize, a) // possibly < 0 if align overflows
return align(z, a)*(n-1) + z 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: case *Slice:
return s.WordSize * 3 return s.WordSize * 3
case *Struct: case *Struct:
@ -174,7 +209,12 @@ func (s *StdSizes) Sizeof(T Type) int64 {
return 0 return 0
} }
offsets := s.Offsetsof(t.fields) 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: case *Interface:
// Type parameters lead to variable sizes/alignments; // Type parameters lead to variable sizes/alignments;
// StdSizes.Sizeof won't be called for them. // StdSizes.Sizeof won't be called for them.
@ -235,62 +275,69 @@ func SizesFor(compiler, arch string) Sizes {
var stdSizes = SizesFor("gc", "amd64") var stdSizes = SizesFor("gc", "amd64")
func (conf *Config) alignof(T Type) int64 { func (conf *Config) alignof(T Type) int64 {
if s := conf.Sizes; s != nil { f := stdSizes.Alignof
if a := s.Alignof(T); a >= 1 { if conf.Sizes != nil {
return a f = conf.Sizes.Alignof
}
panic("Config.Sizes.Alignof returned an alignment < 1")
} }
return stdSizes.Alignof(T) if a := f(T); a >= 1 {
return a
}
panic("implementation of alignof returned an alignment < 1")
} }
func (conf *Config) offsetsof(T *Struct) []int64 { func (conf *Config) offsetsof(T *Struct) []int64 {
var offsets []int64 var offsets []int64
if T.NumFields() > 0 { if T.NumFields() > 0 {
// compute offsets on demand // compute offsets on demand
if s := conf.Sizes; s != nil { f := stdSizes.Offsetsof
offsets = s.Offsetsof(T.fields) if conf.Sizes != nil {
// sanity checks f = conf.Sizes.Offsetsof
if len(offsets) != T.NumFields() { }
panic("Config.Sizes.Offsetsof returned the wrong number of offsets") offsets = f(T.fields)
} // sanity checks
for _, o := range offsets { if len(offsets) != T.NumFields() {
if o < 0 { panic("implementation of offsetsof returned the wrong number of offsets")
panic("Config.Sizes.Offsetsof returned an offset < 0")
}
}
} else {
offsets = stdSizes.Offsetsof(T.fields)
} }
} }
return offsets return offsets
} }
// offsetof returns the offset of the field specified via // offsetof returns the offset of the field specified via
// the index sequence relative to typ. All embedded fields // the index sequence relative to T. All embedded fields
// must be structs (rather than pointer to structs). // must be structs (rather than pointers to structs).
func (conf *Config) offsetof(typ Type, index []int) int64 { // If the offset is too large (because T is too large),
var o int64 // the result is negative.
func (conf *Config) offsetof(T Type, index []int) int64 {
var offs int64
for _, i := range index { for _, i := range index {
s := under(typ).(*Struct) s := under(T).(*Struct)
o += conf.offsetsof(s)[i] d := conf.offsetsof(s)[i]
typ = s.fields[i].typ 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 { func (conf *Config) sizeof(T Type) int64 {
if s := conf.Sizes; s != nil { f := stdSizes.Sizeof
if z := s.Sizeof(T); z >= 0 { if conf.Sizes != nil {
return z f = conf.Sizes.Sizeof
}
panic("Config.Sizes.Sizeof returned a size < 0")
} }
return stdSizes.Sizeof(T) return f(T)
} }
// align returns the smallest y >= x such that y % a == 0. // 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 { func align(x, a int64) int64 {
y := x + a - 1 assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
return y - y%a return (x + a - 1) &^ (a - 1)
} }

View 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())) check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
} }
} else { } else {
offs := check.conf.offsetof(base, index)
if offs < 0 {
check.errorf(x, TypeTooLarge, "%s is too large", x)
return
}
x.mode = constant_ 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 // result is constant - no need to record signature
} }
x.typ = Typ[Uintptr] 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)) check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
} }
} else { } else {
size := check.conf.sizeof(x.typ)
if size < 0 {
check.errorf(x, TypeTooLarge, "%s is too large", x)
return
}
x.mode = constant_ 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 // result is constant - no need to record signature
} }
x.typ = Typ[Uintptr] x.typ = Typ[Uintptr]

View File

@ -301,6 +301,12 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
conf = check.conf conf = check.conf
} }
sizeof := func(T Type) int64 {
s := conf.sizeof(T)
assert(s == 4 || s == 8)
return s
}
switch { switch {
case isInteger(typ): case isInteger(typ):
x := constant.ToInt(x) 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 { if x, ok := constant.Int64Val(x); ok {
switch typ.kind { switch typ.kind {
case Int: 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 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
case Int8: case Int8:
const s = 8 const s = 8
@ -327,7 +333,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
case Int64, UntypedInt: case Int64, UntypedInt:
return true return true
case Uint, Uintptr: 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 && x <= int64(1)<<s-1
} }
return 0 <= x return 0 <= x
@ -349,7 +355,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
// x does not fit into int64 // x does not fit into int64
switch n := constant.BitLen(x); typ.kind { switch n := constant.BitLen(x); typ.kind {
case Uint, Uintptr: case Uint, Uintptr:
var s = uint(conf.sizeof(typ)) * 8 var s = uint(sizeof(typ)) * 8
return constant.Sign(x) >= 0 && n <= int(s) return constant.Sign(x) >= 0 && n <= int(s)
case Uint64: case Uint64:
return constant.Sign(x) >= 0 && n <= 64 return constant.Sign(x) >= 0 && n <= 64

View File

@ -12,14 +12,17 @@ package types
type Sizes interface { type Sizes interface {
// Alignof returns the alignment of a variable of type T. // Alignof returns the alignment of a variable of type T.
// Alignof must implement the alignment guarantees required by the spec. // Alignof must implement the alignment guarantees required by the spec.
// The result must be >= 1.
Alignof(T Type) int64 Alignof(T Type) int64
// Offsetsof returns the offsets of the given struct fields, in bytes. // Offsetsof returns the offsets of the given struct fields, in bytes.
// Offsetsof must implement the offset guarantees required by the spec. // 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 Offsetsof(fields []*Var) []int64
// Sizeof returns the size of a variable of type T. // Sizeof returns the size of a variable of type T.
// Sizeof must implement the size guarantees required by the spec. // Sizeof must implement the size guarantees required by the spec.
// A negative result indicates that T is too large.
Sizeof(T Type) int64 Sizeof(T Type) int64
} }
@ -46,7 +49,11 @@ type StdSizes struct {
MaxAlign int64 // maximum alignment in bytes - must be >= 1 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 // For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively. // of alignment of the elements and fields, respectively.
switch t := under(T).(type) { switch t := under(T).(type) {
@ -91,7 +98,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
case *TypeParam, *Union: case *TypeParam, *Union:
unreachable() 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." // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
if a < 1 { if a < 1 {
return 1 return 1
@ -120,12 +127,22 @@ func _IsSyncAtomicAlign64(T Type) bool {
func (s *StdSizes) Offsetsof(fields []*Var) []int64 { func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
offsets := make([]int64, len(fields)) offsets := make([]int64, len(fields))
var o int64 var offs int64
for i, f := range fields { 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) a := s.Alignof(f.typ)
o = align(o, a) offs = align(offs, a) // possibly < 0 if align overflows
offsets[i] = o offsets[i] = offs
o += s.Sizeof(f.typ) 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 return offsets
} }
@ -165,9 +182,27 @@ func (s *StdSizes) Sizeof(T Type) int64 {
return 0 return 0
} }
// n > 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) a := s.Alignof(t.elem)
z := s.Sizeof(t.elem) ea := align(esize, a) // possibly < 0 if align overflows
return align(z, a)*(n-1) + z 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: case *Slice:
return s.WordSize * 3 return s.WordSize * 3
case *Struct: case *Struct:
@ -176,7 +211,12 @@ func (s *StdSizes) Sizeof(T Type) int64 {
return 0 return 0
} }
offsets := s.Offsetsof(t.fields) 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: case *Interface:
// Type parameters lead to variable sizes/alignments; // Type parameters lead to variable sizes/alignments;
// StdSizes.Sizeof won't be called for them. // StdSizes.Sizeof won't be called for them.
@ -237,62 +277,69 @@ func SizesFor(compiler, arch string) Sizes {
var stdSizes = SizesFor("gc", "amd64") var stdSizes = SizesFor("gc", "amd64")
func (conf *Config) alignof(T Type) int64 { func (conf *Config) alignof(T Type) int64 {
if s := conf.Sizes; s != nil { f := stdSizes.Alignof
if a := s.Alignof(T); a >= 1 { if conf.Sizes != nil {
return a f = conf.Sizes.Alignof
}
panic("Config.Sizes.Alignof returned an alignment < 1")
} }
return stdSizes.Alignof(T) if a := f(T); a >= 1 {
return a
}
panic("implementation of alignof returned an alignment < 1")
} }
func (conf *Config) offsetsof(T *Struct) []int64 { func (conf *Config) offsetsof(T *Struct) []int64 {
var offsets []int64 var offsets []int64
if T.NumFields() > 0 { if T.NumFields() > 0 {
// compute offsets on demand // compute offsets on demand
if s := conf.Sizes; s != nil { f := stdSizes.Offsetsof
offsets = s.Offsetsof(T.fields) if conf.Sizes != nil {
// sanity checks f = conf.Sizes.Offsetsof
if len(offsets) != T.NumFields() { }
panic("Config.Sizes.Offsetsof returned the wrong number of offsets") offsets = f(T.fields)
} // sanity checks
for _, o := range offsets { if len(offsets) != T.NumFields() {
if o < 0 { panic("implementation of offsetsof returned the wrong number of offsets")
panic("Config.Sizes.Offsetsof returned an offset < 0")
}
}
} else {
offsets = stdSizes.Offsetsof(T.fields)
} }
} }
return offsets return offsets
} }
// offsetof returns the offset of the field specified via // offsetof returns the offset of the field specified via
// the index sequence relative to typ. All embedded fields // the index sequence relative to T. All embedded fields
// must be structs (rather than pointer to structs). // must be structs (rather than pointers to structs).
func (conf *Config) offsetof(typ Type, index []int) int64 { // If the offset is too large (because T is too large),
var o int64 // the result is negative.
func (conf *Config) offsetof(T Type, index []int) int64 {
var offs int64
for _, i := range index { for _, i := range index {
s := under(typ).(*Struct) s := under(T).(*Struct)
o += conf.offsetsof(s)[i] d := conf.offsetsof(s)[i]
typ = s.fields[i].typ 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 { func (conf *Config) sizeof(T Type) int64 {
if s := conf.Sizes; s != nil { f := stdSizes.Sizeof
if z := s.Sizeof(T); z >= 0 { if conf.Sizes != nil {
return z f = conf.Sizes.Sizeof
}
panic("Config.Sizes.Sizeof returned a size < 0")
} }
return stdSizes.Sizeof(T) return f(T)
} }
// align returns the smallest y >= x such that y % a == 0. // 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 { func align(x, a int64) int64 {
y := x + a - 1 assert(x >= 0 && 1 <= a && a <= 8 && a&(a-1) == 0)
return y - y%a return (x + a - 1) &^ (a - 1)
} }

View File

@ -153,6 +153,7 @@ func _() {
_ = x[InvalidUnsafeSliceData-145] _ = x[InvalidUnsafeSliceData-145]
_ = x[InvalidUnsafeString-146] _ = x[InvalidUnsafeString-146]
_ = x[InvalidClear-148] _ = x[InvalidClear-148]
_ = x[TypeTooLarge-149]
} }
const ( const (
@ -161,7 +162,7 @@ const (
_Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot" _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot"
_Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl" _Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl"
_Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString" _Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
_Code_name_5 = "InvalidClear" _Code_name_5 = "InvalidClearTypeTooLarge"
) )
var ( 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_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_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_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 { func (i Code) String() string {
@ -187,8 +189,9 @@ func (i Code) String() string {
case 108 <= i && i <= 146: case 108 <= i && i <= 146:
i -= 108 i -= 108
return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]] return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]]
case i == 148: case 148 <= i && i <= 149:
return _Code_name_5 i -= 148
return _Code_name_5[_Code_index_5[i]:_Code_index_5[i+1]]
default: default:
return "Code(" + strconv.FormatInt(int64(i), 10) + ")" return "Code(" + strconv.FormatInt(int64(i), 10) + ")"
} }

View File

@ -1441,4 +1441,25 @@ const (
// clear(x) // clear(x)
// } // }
InvalidClear 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
) )

View File

@ -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" */ )
}

View File

@ -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" */ )

View File

@ -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"
}