mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
[dev.boringcrypto.go1.17] all: merge go1.17.12 into dev.boringcrypto.go1.17
Change-Id: I652dbbb5721fa0c7be09be34b9ac1a987c331f6d
This commit is contained in:
commit
7d5078e3bf
@ -78,6 +78,8 @@ func storeByType(t *types.Type) obj.As {
|
|||||||
return x86.AMOVL
|
return x86.AMOVL
|
||||||
case 8:
|
case 8:
|
||||||
return x86.AMOVQ
|
return x86.AMOVQ
|
||||||
|
case 16:
|
||||||
|
return x86.AMOVUPS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("bad store type %v", t))
|
panic(fmt.Sprintf("bad store type %v", t))
|
||||||
|
@ -91,6 +91,11 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
apdecls = append(apdecls, n)
|
apdecls = append(apdecls, n)
|
||||||
|
if n.Type().Kind() == types.TSSA {
|
||||||
|
// Can happen for TypeInt128 types. This only happens for
|
||||||
|
// spill locations, so not a huge deal.
|
||||||
|
continue
|
||||||
|
}
|
||||||
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
|
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -625,6 +625,12 @@ func PtrDataSize(t *Type) int64 {
|
|||||||
}
|
}
|
||||||
return lastPtrField.Offset + PtrDataSize(lastPtrField.Type)
|
return lastPtrField.Offset + PtrDataSize(lastPtrField.Type)
|
||||||
|
|
||||||
|
case TSSA:
|
||||||
|
if t != TypeInt128 {
|
||||||
|
base.Fatalf("PtrDataSize: unexpected ssa type %v", t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
|
||||||
default:
|
default:
|
||||||
base.Fatalf("PtrDataSize: unexpected type, %v", t)
|
base.Fatalf("PtrDataSize: unexpected type, %v", t)
|
||||||
return 0
|
return 0
|
||||||
|
@ -1661,6 +1661,11 @@ var (
|
|||||||
TypeResultMem = newResults([]*Type{TypeMem})
|
TypeResultMem = newResults([]*Type{TypeMem})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TypeInt128.Width = 16
|
||||||
|
TypeInt128.Align = 8
|
||||||
|
}
|
||||||
|
|
||||||
// NewNamed returns a new named type for the given type name. obj should be an
|
// NewNamed returns a new named type for the given type name. obj should be an
|
||||||
// ir.Name. The new type is incomplete (marked as TFORW kind), and the underlying
|
// ir.Name. The new type is incomplete (marked as TFORW kind), and the underlying
|
||||||
// type should be set later via SetUnderlying(). References to the type are
|
// type should be set later via SetUnderlying(). References to the type are
|
||||||
|
4
src/cmd/dist/test.go
vendored
4
src/cmd/dist/test.go
vendored
@ -1140,9 +1140,9 @@ func (t *tester) cgoTest(dt *distTest) error {
|
|||||||
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
|
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
|
||||||
setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto")
|
setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto")
|
||||||
|
|
||||||
// Skip internal linking cases on linux/arm64 to support GCC-9.4 and above.
|
// Skip internal linking cases on arm64 to support GCC-9.4 and above.
|
||||||
// See issue #39466.
|
// See issue #39466.
|
||||||
skipInternalLink := goarch == "arm64" && goos == "linux"
|
skipInternalLink := goarch == "arm64" && goos != "darwin"
|
||||||
|
|
||||||
if t.internalLink() && !skipInternalLink {
|
if t.internalLink() && !skipInternalLink {
|
||||||
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal")
|
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal")
|
||||||
|
@ -440,7 +440,7 @@ func (r *gitRepo) fetchRefsLocked() error {
|
|||||||
// statLocal returns a RevInfo describing rev in the local git repository.
|
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||||
// It uses version as info.Version.
|
// It uses version as info.Version.
|
||||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||||
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "-n1", "--format=format:%H %ct %D", rev, "--")
|
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "--no-decorate", "-n1", "--format=format:%H %ct %D", rev, "--")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &UnknownRevisionError{Rev: rev}
|
return nil, &UnknownRevisionError{Rev: rev}
|
||||||
}
|
}
|
||||||
|
28
src/cmd/go/testdata/script/mod_download_git_decorate_full.txt
vendored
Normal file
28
src/cmd/go/testdata/script/mod_download_git_decorate_full.txt
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
env GO111MODULE=on
|
||||||
|
|
||||||
|
[!net] skip
|
||||||
|
[!exec:git] skip
|
||||||
|
|
||||||
|
env GOPROXY=direct
|
||||||
|
env HOME=$WORK/home/gopher
|
||||||
|
|
||||||
|
|
||||||
|
go env GOPROXY
|
||||||
|
stdout 'direct'
|
||||||
|
|
||||||
|
exec git config --get log.decorate
|
||||||
|
stdout 'full'
|
||||||
|
|
||||||
|
# Test that Git log with user's global config '~/gitconfig' has log.decorate=full
|
||||||
|
# go mod download has an error 'v1.x.y is not a tag'
|
||||||
|
# with go1.16.14:
|
||||||
|
# `go1.16.14 list -m vcs-test.golang.org/git/gitrepo1.git@v1.2.3`
|
||||||
|
# will output with error:
|
||||||
|
# go list -m: vcs-test.golang.org/git/gitrepo1.git@v1.2.3: invalid version: unknown revision v1.2.3
|
||||||
|
# See golang/go#51312.
|
||||||
|
go list -m vcs-test.golang.org/git/gitrepo1.git@v1.2.3
|
||||||
|
stdout 'vcs-test.golang.org/git/gitrepo1.git v1.2.3'
|
||||||
|
|
||||||
|
-- $WORK/home/gopher/.gitconfig --
|
||||||
|
[log]
|
||||||
|
decorate = full
|
@ -248,42 +248,40 @@ func (z *Reader) Read(p []byte) (n int, err error) {
|
|||||||
return 0, z.err
|
return 0, z.err
|
||||||
}
|
}
|
||||||
|
|
||||||
n, z.err = z.decompressor.Read(p)
|
for n == 0 {
|
||||||
z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
|
n, z.err = z.decompressor.Read(p)
|
||||||
z.size += uint32(n)
|
z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
|
||||||
if z.err != io.EOF {
|
z.size += uint32(n)
|
||||||
// In the normal case we return here.
|
if z.err != io.EOF {
|
||||||
return n, z.err
|
// In the normal case we return here.
|
||||||
|
return n, z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished file; check checksum and size.
|
||||||
|
if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
|
||||||
|
z.err = noEOF(err)
|
||||||
|
return n, z.err
|
||||||
|
}
|
||||||
|
digest := le.Uint32(z.buf[:4])
|
||||||
|
size := le.Uint32(z.buf[4:8])
|
||||||
|
if digest != z.digest || size != z.size {
|
||||||
|
z.err = ErrChecksum
|
||||||
|
return n, z.err
|
||||||
|
}
|
||||||
|
z.digest, z.size = 0, 0
|
||||||
|
|
||||||
|
// File is ok; check if there is another.
|
||||||
|
if !z.multistream {
|
||||||
|
return n, io.EOF
|
||||||
|
}
|
||||||
|
z.err = nil // Remove io.EOF
|
||||||
|
|
||||||
|
if _, z.err = z.readHeader(); z.err != nil {
|
||||||
|
return n, z.err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finished file; check checksum and size.
|
return n, nil
|
||||||
if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
|
|
||||||
z.err = noEOF(err)
|
|
||||||
return n, z.err
|
|
||||||
}
|
|
||||||
digest := le.Uint32(z.buf[:4])
|
|
||||||
size := le.Uint32(z.buf[4:8])
|
|
||||||
if digest != z.digest || size != z.size {
|
|
||||||
z.err = ErrChecksum
|
|
||||||
return n, z.err
|
|
||||||
}
|
|
||||||
z.digest, z.size = 0, 0
|
|
||||||
|
|
||||||
// File is ok; check if there is another.
|
|
||||||
if !z.multistream {
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
z.err = nil // Remove io.EOF
|
|
||||||
|
|
||||||
if _, z.err = z.readHeader(); z.err != nil {
|
|
||||||
return n, z.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read from next file, if necessary.
|
|
||||||
if n > 0 {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
return z.Read(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the Reader. It does not close the underlying io.Reader.
|
// Close closes the Reader. It does not close the underlying io.Reader.
|
||||||
|
@ -515,3 +515,19 @@ func TestTruncatedStreams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCVE202230631(t *testing.T) {
|
||||||
|
var empty = []byte{0x1f, 0x8b, 0x08, 0x00, 0xa7, 0x8f, 0x43, 0x62, 0x00,
|
||||||
|
0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
r := bytes.NewReader(bytes.Repeat(empty, 4e6))
|
||||||
|
z, err := NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewReader: got %v, want nil", err)
|
||||||
|
}
|
||||||
|
// Prior to CVE-2022-30631 fix, this would cause an unrecoverable panic due
|
||||||
|
// to stack exhaustion.
|
||||||
|
_, err = z.Read(make([]byte, 10))
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Errorf("Reader.Read: got %v, want %v", err, io.EOF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -871,8 +871,13 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
|
|||||||
return &op
|
return &op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var maxIgnoreNestingDepth = 10000
|
||||||
|
|
||||||
// decIgnoreOpFor returns the decoding op for a field that has no destination.
|
// decIgnoreOpFor returns the decoding op for a field that has no destination.
|
||||||
func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) *decOp {
|
func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp, depth int) *decOp {
|
||||||
|
if depth > maxIgnoreNestingDepth {
|
||||||
|
error_(errors.New("invalid nesting depth"))
|
||||||
|
}
|
||||||
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
|
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
|
||||||
// Return the pointer to the op we're already building.
|
// Return the pointer to the op we're already building.
|
||||||
if opPtr := inProgress[wireId]; opPtr != nil {
|
if opPtr := inProgress[wireId]; opPtr != nil {
|
||||||
@ -896,7 +901,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp)
|
|||||||
errorf("bad data: undefined type %s", wireId.string())
|
errorf("bad data: undefined type %s", wireId.string())
|
||||||
case wire.ArrayT != nil:
|
case wire.ArrayT != nil:
|
||||||
elemId := wire.ArrayT.Elem
|
elemId := wire.ArrayT.Elem
|
||||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||||
state.dec.ignoreArray(state, *elemOp, wire.ArrayT.Len)
|
state.dec.ignoreArray(state, *elemOp, wire.ArrayT.Len)
|
||||||
}
|
}
|
||||||
@ -904,15 +909,15 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp)
|
|||||||
case wire.MapT != nil:
|
case wire.MapT != nil:
|
||||||
keyId := dec.wireType[wireId].MapT.Key
|
keyId := dec.wireType[wireId].MapT.Key
|
||||||
elemId := dec.wireType[wireId].MapT.Elem
|
elemId := dec.wireType[wireId].MapT.Elem
|
||||||
keyOp := dec.decIgnoreOpFor(keyId, inProgress)
|
keyOp := dec.decIgnoreOpFor(keyId, inProgress, depth+1)
|
||||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||||
state.dec.ignoreMap(state, *keyOp, *elemOp)
|
state.dec.ignoreMap(state, *keyOp, *elemOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
case wire.SliceT != nil:
|
case wire.SliceT != nil:
|
||||||
elemId := wire.SliceT.Elem
|
elemId := wire.SliceT.Elem
|
||||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||||
state.dec.ignoreSlice(state, *elemOp)
|
state.dec.ignoreSlice(state, *elemOp)
|
||||||
}
|
}
|
||||||
@ -1073,7 +1078,7 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de
|
|||||||
func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine {
|
func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine {
|
||||||
engine := new(decEngine)
|
engine := new(decEngine)
|
||||||
engine.instr = make([]decInstr, 1) // one item
|
engine.instr = make([]decInstr, 1) // one item
|
||||||
op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp))
|
op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp), 0)
|
||||||
ovfl := overflow(dec.typeString(remoteId))
|
ovfl := overflow(dec.typeString(remoteId))
|
||||||
engine.instr[0] = decInstr{*op, 0, nil, ovfl}
|
engine.instr[0] = decInstr{*op, 0, nil, ovfl}
|
||||||
engine.numInstr = 1
|
engine.numInstr = 1
|
||||||
@ -1118,7 +1123,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn
|
|||||||
localField, present := srt.FieldByName(wireField.Name)
|
localField, present := srt.FieldByName(wireField.Name)
|
||||||
// TODO(r): anonymous names
|
// TODO(r): anonymous names
|
||||||
if !present || !isExported(wireField.Name) {
|
if !present || !isExported(wireField.Name) {
|
||||||
op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp))
|
op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp), 0)
|
||||||
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
|
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -796,3 +797,26 @@ func TestNetIP(t *testing.T) {
|
|||||||
t.Errorf("decoded to %v, want 1.2.3.4", ip.String())
|
t.Errorf("decoded to %v, want 1.2.3.4", ip.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIngoreDepthLimit(t *testing.T) {
|
||||||
|
// We don't test the actual depth limit because it requires building an
|
||||||
|
// extremely large message, which takes quite a while.
|
||||||
|
oldNestingDepth := maxIgnoreNestingDepth
|
||||||
|
maxIgnoreNestingDepth = 100
|
||||||
|
defer func() { maxIgnoreNestingDepth = oldNestingDepth }()
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
enc := NewEncoder(b)
|
||||||
|
typ := reflect.TypeOf(int(0))
|
||||||
|
nested := reflect.ArrayOf(1, typ)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
nested = reflect.ArrayOf(1, nested)
|
||||||
|
}
|
||||||
|
badStruct := reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}}))
|
||||||
|
enc.Encode(badStruct.Interface())
|
||||||
|
dec := NewDecoder(b)
|
||||||
|
var output struct{ Hello int }
|
||||||
|
expectedErr := "invalid nesting depth"
|
||||||
|
if err := dec.Decode(&output); err == nil || err.Error() != expectedErr {
|
||||||
|
t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -148,7 +148,7 @@ func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
|
|||||||
if val.Kind() != reflect.Ptr {
|
if val.Kind() != reflect.Ptr {
|
||||||
return errors.New("non-pointer passed to Unmarshal")
|
return errors.New("non-pointer passed to Unmarshal")
|
||||||
}
|
}
|
||||||
return d.unmarshal(val.Elem(), start)
|
return d.unmarshal(val.Elem(), start, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An UnmarshalError represents an error in the unmarshaling process.
|
// An UnmarshalError represents an error in the unmarshaling process.
|
||||||
@ -304,8 +304,15 @@ var (
|
|||||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxUnmarshalDepth = 10000
|
||||||
|
|
||||||
|
var errExeceededMaxUnmarshalDepth = errors.New("exceeded max depth")
|
||||||
|
|
||||||
// Unmarshal a single XML element into val.
|
// Unmarshal a single XML element into val.
|
||||||
func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
|
func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) error {
|
||||||
|
if depth >= maxUnmarshalDepth {
|
||||||
|
return errExeceededMaxUnmarshalDepth
|
||||||
|
}
|
||||||
// Find start element if we need it.
|
// Find start element if we need it.
|
||||||
if start == nil {
|
if start == nil {
|
||||||
for {
|
for {
|
||||||
@ -398,7 +405,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
|
|||||||
v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem())))
|
v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem())))
|
||||||
|
|
||||||
// Recur to read element into slice.
|
// Recur to read element into slice.
|
||||||
if err := d.unmarshal(v.Index(n), start); err != nil {
|
if err := d.unmarshal(v.Index(n), start, depth+1); err != nil {
|
||||||
v.SetLen(n)
|
v.SetLen(n)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -521,13 +528,15 @@ Loop:
|
|||||||
case StartElement:
|
case StartElement:
|
||||||
consumed := false
|
consumed := false
|
||||||
if sv.IsValid() {
|
if sv.IsValid() {
|
||||||
consumed, err = d.unmarshalPath(tinfo, sv, nil, &t)
|
// unmarshalPath can call unmarshal, so we need to pass the depth through so that
|
||||||
|
// we can continue to enforce the maximum recusion limit.
|
||||||
|
consumed, err = d.unmarshalPath(tinfo, sv, nil, &t, depth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !consumed && saveAny.IsValid() {
|
if !consumed && saveAny.IsValid() {
|
||||||
consumed = true
|
consumed = true
|
||||||
if err := d.unmarshal(saveAny, &t); err != nil {
|
if err := d.unmarshal(saveAny, &t, depth+1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,7 +681,7 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
|
|||||||
// The consumed result tells whether XML elements have been consumed
|
// The consumed result tells whether XML elements have been consumed
|
||||||
// from the Decoder until start's matching end element, or if it's
|
// from the Decoder until start's matching end element, or if it's
|
||||||
// still untouched because start is uninteresting for sv's fields.
|
// still untouched because start is uninteresting for sv's fields.
|
||||||
func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) {
|
func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement, depth int) (consumed bool, err error) {
|
||||||
recurse := false
|
recurse := false
|
||||||
Loop:
|
Loop:
|
||||||
for i := range tinfo.fields {
|
for i := range tinfo.fields {
|
||||||
@ -687,7 +696,7 @@ Loop:
|
|||||||
}
|
}
|
||||||
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
|
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
|
||||||
// It's a perfect match, unmarshal the field.
|
// It's a perfect match, unmarshal the field.
|
||||||
return true, d.unmarshal(finfo.value(sv, initNilPointers), start)
|
return true, d.unmarshal(finfo.value(sv, initNilPointers), start, depth+1)
|
||||||
}
|
}
|
||||||
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
|
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
|
||||||
// It's a prefix for the field. Break and recurse
|
// It's a prefix for the field. Break and recurse
|
||||||
@ -716,7 +725,9 @@ Loop:
|
|||||||
}
|
}
|
||||||
switch t := tok.(type) {
|
switch t := tok.(type) {
|
||||||
case StartElement:
|
case StartElement:
|
||||||
consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t)
|
// the recursion depth of unmarshalPath is limited to the path length specified
|
||||||
|
// by the struct field tag, so we don't increment the depth here.
|
||||||
|
consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t, depth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
@ -732,12 +743,12 @@ Loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip reads tokens until it has consumed the end element
|
// Skip reads tokens until it has consumed the end element
|
||||||
// matching the most recent start element already consumed.
|
// matching the most recent start element already consumed,
|
||||||
// It recurs if it encounters a start element, so it can be used to
|
// skipping nested structures.
|
||||||
// skip nested structures.
|
|
||||||
// It returns nil if it finds an end element matching the start
|
// It returns nil if it finds an end element matching the start
|
||||||
// element; otherwise it returns an error describing the problem.
|
// element; otherwise it returns an error describing the problem.
|
||||||
func (d *Decoder) Skip() error {
|
func (d *Decoder) Skip() error {
|
||||||
|
var depth int64
|
||||||
for {
|
for {
|
||||||
tok, err := d.Token()
|
tok, err := d.Token()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -745,11 +756,12 @@ func (d *Decoder) Skip() error {
|
|||||||
}
|
}
|
||||||
switch tok.(type) {
|
switch tok.(type) {
|
||||||
case StartElement:
|
case StartElement:
|
||||||
if err := d.Skip(); err != nil {
|
depth++
|
||||||
return err
|
|
||||||
}
|
|
||||||
case EndElement:
|
case EndElement:
|
||||||
return nil
|
if depth == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
depth--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,11 @@
|
|||||||
package xml
|
package xml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -1079,3 +1082,32 @@ func TestUnmarshalWhitespaceAttrs(t *testing.T) {
|
|||||||
t.Fatalf("whitespace attrs: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
|
t.Fatalf("whitespace attrs: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCVE202230633(t *testing.T) {
|
||||||
|
if runtime.GOARCH == "wasm" {
|
||||||
|
t.Skip("causes memory exhaustion on js/wasm")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
p := recover()
|
||||||
|
if p != nil {
|
||||||
|
t.Fatal("Unmarshal panicked")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var example struct {
|
||||||
|
Things []string
|
||||||
|
}
|
||||||
|
Unmarshal(bytes.Repeat([]byte("<a>"), 17_000_000), &example)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCVE202228131(t *testing.T) {
|
||||||
|
type nested struct {
|
||||||
|
Parent *nested `xml:",any"`
|
||||||
|
}
|
||||||
|
var n nested
|
||||||
|
err := Unmarshal(bytes.Repeat([]byte("<a>"), maxUnmarshalDepth+1), &n)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Unmarshal did not fail")
|
||||||
|
} else if !errors.Is(err, errExeceededMaxUnmarshalDepth) {
|
||||||
|
t.Fatalf("Unmarshal unexpected error: got %q, want %q", err, errExeceededMaxUnmarshalDepth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -97,8 +97,11 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
|
|||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
// resume same panic if it's not a bailout
|
// resume same panic if it's not a bailout
|
||||||
if _, ok := e.(bailout); !ok {
|
bail, ok := e.(bailout)
|
||||||
|
if !ok {
|
||||||
panic(e)
|
panic(e)
|
||||||
|
} else if bail.msg != "" {
|
||||||
|
p.errors.Add(p.file.Position(bail.pos), bail.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,8 +206,11 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M
|
|||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
// resume same panic if it's not a bailout
|
// resume same panic if it's not a bailout
|
||||||
if _, ok := e.(bailout); !ok {
|
bail, ok := e.(bailout)
|
||||||
|
if !ok {
|
||||||
panic(e)
|
panic(e)
|
||||||
|
} else if bail.msg != "" {
|
||||||
|
p.errors.Add(p.file.Position(bail.pos), bail.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.errors.Sort()
|
p.errors.Sort()
|
||||||
|
@ -60,6 +60,10 @@ type parser struct {
|
|||||||
inRhs bool // if set, the parser is parsing a rhs expression
|
inRhs bool // if set, the parser is parsing a rhs expression
|
||||||
|
|
||||||
imports []*ast.ImportSpec // list of imports
|
imports []*ast.ImportSpec // list of imports
|
||||||
|
|
||||||
|
// nestLev is used to track and limit the recursion depth
|
||||||
|
// during parsing.
|
||||||
|
nestLev int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
|
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
|
||||||
@ -110,6 +114,24 @@ func un(p *parser) {
|
|||||||
p.printTrace(")")
|
p.printTrace(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maxNestLev is the deepest we're willing to recurse during parsing
|
||||||
|
const maxNestLev int = 1e5
|
||||||
|
|
||||||
|
func incNestLev(p *parser) *parser {
|
||||||
|
p.nestLev++
|
||||||
|
if p.nestLev > maxNestLev {
|
||||||
|
p.error(p.pos, "exceeded max nesting depth")
|
||||||
|
panic(bailout{})
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// decNestLev is used to track nesting depth during parsing to prevent stack exhaustion.
|
||||||
|
// It is used along with incNestLev in a similar fashion to how un and trace are used.
|
||||||
|
func decNestLev(p *parser) {
|
||||||
|
p.nestLev--
|
||||||
|
}
|
||||||
|
|
||||||
// Advance to the next token.
|
// Advance to the next token.
|
||||||
func (p *parser) next0() {
|
func (p *parser) next0() {
|
||||||
// Because of one-token look-ahead, print the previous token
|
// Because of one-token look-ahead, print the previous token
|
||||||
@ -222,8 +244,12 @@ func (p *parser) next() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A bailout panic is raised to indicate early termination.
|
// A bailout panic is raised to indicate early termination. pos and msg are
|
||||||
type bailout struct{}
|
// only populated when bailing out of object resolution.
|
||||||
|
type bailout struct {
|
||||||
|
pos token.Pos
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
func (p *parser) error(pos token.Pos, msg string) {
|
func (p *parser) error(pos token.Pos, msg string) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
@ -1119,6 +1145,8 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) tryIdentOrType() ast.Expr {
|
func (p *parser) tryIdentOrType() ast.Expr {
|
||||||
|
defer decNestLev(incNestLev(p))
|
||||||
|
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.IDENT:
|
case token.IDENT:
|
||||||
typ := p.parseTypeName(nil)
|
typ := p.parseTypeName(nil)
|
||||||
@ -1531,7 +1559,13 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
x = p.parseOperand()
|
x = p.parseOperand()
|
||||||
for {
|
// We track the nesting here rather than at the entry for the function,
|
||||||
|
// since it can iteratively produce a nested output, and we want to
|
||||||
|
// limit how deep a structure we generate.
|
||||||
|
var n int
|
||||||
|
defer func() { p.nestLev -= n }()
|
||||||
|
for n = 1; ; n++ {
|
||||||
|
incNestLev(p)
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.PERIOD:
|
case token.PERIOD:
|
||||||
p.next()
|
p.next()
|
||||||
@ -1591,6 +1625,8 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseUnaryExpr() ast.Expr {
|
func (p *parser) parseUnaryExpr() ast.Expr {
|
||||||
|
defer decNestLev(incNestLev(p))
|
||||||
|
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "UnaryExpr"))
|
defer un(trace(p, "UnaryExpr"))
|
||||||
}
|
}
|
||||||
@ -1673,7 +1709,13 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseUnaryExpr()
|
x := p.parseUnaryExpr()
|
||||||
for {
|
// We track the nesting here rather than at the entry for the function,
|
||||||
|
// since it can iteratively produce a nested output, and we want to
|
||||||
|
// limit how deep a structure we generate.
|
||||||
|
var n int
|
||||||
|
defer func() { p.nestLev -= n }()
|
||||||
|
for n = 1; ; n++ {
|
||||||
|
incNestLev(p)
|
||||||
op, oprec := p.tokPrec()
|
op, oprec := p.tokPrec()
|
||||||
if oprec < prec1 {
|
if oprec < prec1 {
|
||||||
return x
|
return x
|
||||||
@ -1962,6 +2004,8 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseIfStmt() *ast.IfStmt {
|
func (p *parser) parseIfStmt() *ast.IfStmt {
|
||||||
|
defer decNestLev(incNestLev(p))
|
||||||
|
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "IfStmt"))
|
defer un(trace(p, "IfStmt"))
|
||||||
}
|
}
|
||||||
@ -2265,6 +2309,8 @@ func (p *parser) parseForStmt() ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseStmt() (s ast.Stmt) {
|
func (p *parser) parseStmt() (s ast.Stmt) {
|
||||||
|
defer decNestLev(incNestLev(p))
|
||||||
|
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Statement"))
|
defer un(trace(p, "Statement"))
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -577,3 +578,171 @@ type x int // comment
|
|||||||
t.Errorf("got %q, want %q", comment, "// comment")
|
t.Errorf("got %q, want %q", comment, "// comment")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parseDepthTests = []struct {
|
||||||
|
name string
|
||||||
|
format string
|
||||||
|
// multipler is used when a single statement may result in more than one
|
||||||
|
// change in the depth level, for instance "1+(..." produces a BinaryExpr
|
||||||
|
// followed by a UnaryExpr, which increments the depth twice. The test
|
||||||
|
// case comment explains which nodes are triggering the multiple depth
|
||||||
|
// changes.
|
||||||
|
parseMultiplier int
|
||||||
|
// scope is true if we should also test the statement for the resolver scope
|
||||||
|
// depth limit.
|
||||||
|
scope bool
|
||||||
|
// scopeMultiplier does the same as parseMultiplier, but for the scope
|
||||||
|
// depths.
|
||||||
|
scopeMultiplier int
|
||||||
|
}{
|
||||||
|
// The format expands the part inside « » many times.
|
||||||
|
// A second set of brackets nested inside the first stops the repetition,
|
||||||
|
// so that for example «(«1»)» expands to (((...((((1))))...))).
|
||||||
|
{name: "array", format: "package main; var x «[1]»int"},
|
||||||
|
{name: "slice", format: "package main; var x «[]»int"},
|
||||||
|
{name: "struct", format: "package main; var x «struct { X «int» }»", scope: true},
|
||||||
|
{name: "pointer", format: "package main; var x «*»int"},
|
||||||
|
{name: "func", format: "package main; var x «func()»int", scope: true},
|
||||||
|
{name: "chan", format: "package main; var x «chan »int"},
|
||||||
|
{name: "chan2", format: "package main; var x «<-chan »int"},
|
||||||
|
{name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
|
||||||
|
{name: "map", format: "package main; var x «map[int]»int"},
|
||||||
|
{name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
|
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
|
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
|
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr
|
||||||
|
{name: "dot", format: "package main; var x = «x.»x"},
|
||||||
|
{name: "index", format: "package main; var x = x«[1]»"},
|
||||||
|
{name: "slice", format: "package main; var x = x«[1:2]»"},
|
||||||
|
{name: "slice3", format: "package main; var x = x«[1:2:3]»"},
|
||||||
|
{name: "dottype", format: "package main; var x = x«.(any)»"},
|
||||||
|
{name: "callseq", format: "package main; var x = x«()»"},
|
||||||
|
{name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
|
||||||
|
{name: "binary", format: "package main; var x = «1+»1"},
|
||||||
|
{name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
|
||||||
|
{name: "unary", format: "package main; var x = «^»1"},
|
||||||
|
{name: "addr", format: "package main; var x = «& »x"},
|
||||||
|
{name: "star", format: "package main; var x = «*»x"},
|
||||||
|
{name: "recv", format: "package main; var x = «<-»x"},
|
||||||
|
{name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2}, // Parser nodes: Ident, CallExpr
|
||||||
|
{name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
|
||||||
|
{name: "label", format: "package main; func main() { «Label:» }"},
|
||||||
|
{name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
|
||||||
|
{name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
|
||||||
|
{name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
|
||||||
|
{name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
|
||||||
|
{name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
|
||||||
|
{name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
|
||||||
|
{name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
|
||||||
|
{name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
|
||||||
|
{name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
|
||||||
|
{name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
|
||||||
|
{name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: GoStmt, FuncLit
|
||||||
|
{name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: DeferStmt, FuncLit
|
||||||
|
{name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
// split splits pre«mid»post into pre, mid, post.
|
||||||
|
// If the string does not have that form, split returns x, "", "".
|
||||||
|
func split(x string) (pre, mid, post string) {
|
||||||
|
start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
|
||||||
|
if start < 0 || end < 0 {
|
||||||
|
return x, "", ""
|
||||||
|
}
|
||||||
|
return x[:start], x[start+len("«") : end], x[end+len("»"):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDepthLimit(t *testing.T) {
|
||||||
|
if runtime.GOARCH == "wasm" {
|
||||||
|
t.Skip("causes call stack exhaustion on js/wasm")
|
||||||
|
}
|
||||||
|
for _, tt := range parseDepthTests {
|
||||||
|
for _, size := range []string{"small", "big"} {
|
||||||
|
t.Run(tt.name+"/"+size, func(t *testing.T) {
|
||||||
|
n := maxNestLev + 1
|
||||||
|
if tt.parseMultiplier > 0 {
|
||||||
|
n /= tt.parseMultiplier
|
||||||
|
}
|
||||||
|
if size == "small" {
|
||||||
|
// Decrease the number of statements by 10, in order to check
|
||||||
|
// that we do not fail when under the limit. 10 is used to
|
||||||
|
// provide some wiggle room for cases where the surrounding
|
||||||
|
// scaffolding syntax adds some noise to the depth that changes
|
||||||
|
// on a per testcase basis.
|
||||||
|
n -= 10
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, mid, post := split(tt.format)
|
||||||
|
if strings.Contains(mid, "«") {
|
||||||
|
left, base, right := split(mid)
|
||||||
|
mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
|
||||||
|
} else {
|
||||||
|
mid = strings.Repeat(mid, n)
|
||||||
|
}
|
||||||
|
input := pre + mid + post
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
_, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
|
||||||
|
if size == "small" {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ParseFile(...): %v (want success)", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expected := "exceeded max nesting depth"
|
||||||
|
if err == nil || !strings.HasSuffix(err.Error(), expected) {
|
||||||
|
t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScopeDepthLimit(t *testing.T) {
|
||||||
|
if runtime.GOARCH == "wasm" {
|
||||||
|
t.Skip("causes call stack exhaustion on js/wasm")
|
||||||
|
}
|
||||||
|
for _, tt := range parseDepthTests {
|
||||||
|
if !tt.scope {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, size := range []string{"small", "big"} {
|
||||||
|
t.Run(tt.name+"/"+size, func(t *testing.T) {
|
||||||
|
n := maxScopeDepth + 1
|
||||||
|
if tt.scopeMultiplier > 0 {
|
||||||
|
n /= tt.scopeMultiplier
|
||||||
|
}
|
||||||
|
if size == "small" {
|
||||||
|
// Decrease the number of statements by 10, in order to check
|
||||||
|
// that we do not fail when under the limit. 10 is used to
|
||||||
|
// provide some wiggle room for cases where the surrounding
|
||||||
|
// scaffolding syntax adds some noise to the depth that changes
|
||||||
|
// on a per testcase basis.
|
||||||
|
n -= 10
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, mid, post := split(tt.format)
|
||||||
|
if strings.Contains(mid, "«") {
|
||||||
|
left, base, right := split(mid)
|
||||||
|
mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
|
||||||
|
} else {
|
||||||
|
mid = strings.Repeat(mid, n)
|
||||||
|
}
|
||||||
|
input := pre + mid + post
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
_, err := ParseFile(fset, "", input, DeclarationErrors)
|
||||||
|
if size == "small" {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ParseFile(...): %v (want success)", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expected := "exceeded max scope depth during object resolution"
|
||||||
|
if err == nil || !strings.HasSuffix(err.Error(), expected) {
|
||||||
|
t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
|
|||||||
declErr: declErr,
|
declErr: declErr,
|
||||||
topScope: pkgScope,
|
topScope: pkgScope,
|
||||||
pkgScope: pkgScope,
|
pkgScope: pkgScope,
|
||||||
|
depth: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, decl := range file.Decls {
|
for _, decl := range file.Decls {
|
||||||
@ -53,6 +54,8 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
|
|||||||
file.Unresolved = r.unresolved[0:i]
|
file.Unresolved = r.unresolved[0:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxScopeDepth int = 1e3
|
||||||
|
|
||||||
type resolver struct {
|
type resolver struct {
|
||||||
handle *token.File
|
handle *token.File
|
||||||
declErr func(token.Pos, string)
|
declErr func(token.Pos, string)
|
||||||
@ -61,6 +64,7 @@ type resolver struct {
|
|||||||
pkgScope *ast.Scope // pkgScope.Outer == nil
|
pkgScope *ast.Scope // pkgScope.Outer == nil
|
||||||
topScope *ast.Scope // top-most scope; may be pkgScope
|
topScope *ast.Scope // top-most scope; may be pkgScope
|
||||||
unresolved []*ast.Ident // unresolved identifiers
|
unresolved []*ast.Ident // unresolved identifiers
|
||||||
|
depth int // scope depth
|
||||||
|
|
||||||
// Label scopes
|
// Label scopes
|
||||||
// (maintained by open/close LabelScope)
|
// (maintained by open/close LabelScope)
|
||||||
@ -83,6 +87,10 @@ func (r *resolver) sprintf(format string, args ...interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolver) openScope(pos token.Pos) {
|
func (r *resolver) openScope(pos token.Pos) {
|
||||||
|
r.depth++
|
||||||
|
if r.depth > maxScopeDepth {
|
||||||
|
panic(bailout{pos: pos, msg: "exceeded max scope depth during object resolution"})
|
||||||
|
}
|
||||||
if debugResolve {
|
if debugResolve {
|
||||||
r.dump("opening scope @%v", pos)
|
r.dump("opening scope @%v", pos)
|
||||||
}
|
}
|
||||||
@ -90,6 +98,7 @@ func (r *resolver) openScope(pos token.Pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolver) closeScope() {
|
func (r *resolver) closeScope() {
|
||||||
|
r.depth--
|
||||||
if debugResolve {
|
if debugResolve {
|
||||||
r.dump("closing scope")
|
r.dump("closing scope")
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,16 @@ type GlobFS interface {
|
|||||||
// Otherwise, Glob uses ReadDir to traverse the directory tree
|
// Otherwise, Glob uses ReadDir to traverse the directory tree
|
||||||
// and look for matches for the pattern.
|
// and look for matches for the pattern.
|
||||||
func Glob(fsys FS, pattern string) (matches []string, err error) {
|
func Glob(fsys FS, pattern string) (matches []string, err error) {
|
||||||
|
return globWithLimit(fsys, pattern, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func globWithLimit(fsys FS, pattern string, depth int) (matches []string, err error) {
|
||||||
|
// This limit is added to prevent stack exhaustion issues. See
|
||||||
|
// CVE-2022-30630.
|
||||||
|
const pathSeparatorsLimit = 10000
|
||||||
|
if depth > pathSeparatorsLimit {
|
||||||
|
return nil, path.ErrBadPattern
|
||||||
|
}
|
||||||
if fsys, ok := fsys.(GlobFS); ok {
|
if fsys, ok := fsys.(GlobFS); ok {
|
||||||
return fsys.Glob(pattern)
|
return fsys.Glob(pattern)
|
||||||
}
|
}
|
||||||
@ -59,9 +69,9 @@ func Glob(fsys FS, pattern string) (matches []string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var m []string
|
var m []string
|
||||||
m, err = Glob(fsys, dir)
|
m, err = globWithLimit(fsys, dir, depth+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, d := range m {
|
for _, d := range m {
|
||||||
matches, err = glob(fsys, d, file, matches)
|
matches, err = glob(fsys, d, file, matches)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
. "io/fs"
|
. "io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,6 +56,15 @@ func TestGlobError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCVE202230630(t *testing.T) {
|
||||||
|
// Prior to CVE-2022-30630, a stack exhaustion would occur given a large
|
||||||
|
// number of separators. There is now a limit of 10,000.
|
||||||
|
_, err := Glob(os.DirFS("."), "/*"+strings.Repeat("/", 10001))
|
||||||
|
if err != path.ErrBadPattern {
|
||||||
|
t.Fatalf("Glob returned err=%v, want %v", err, path.ErrBadPattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// contains reports whether vector contains the string s.
|
// contains reports whether vector contains the string s.
|
||||||
func contains(vector []string, s string) bool {
|
func contains(vector []string, s string) bool {
|
||||||
for _, elem := range vector {
|
for _, elem := range vector {
|
||||||
|
@ -101,6 +101,12 @@ func (h Header) Clone() Header {
|
|||||||
sv := make([]string, nv) // shared backing array for headers' values
|
sv := make([]string, nv) // shared backing array for headers' values
|
||||||
h2 := make(Header, len(h))
|
h2 := make(Header, len(h))
|
||||||
for k, vv := range h {
|
for k, vv := range h {
|
||||||
|
if vv == nil {
|
||||||
|
// Preserve nil values. ReverseProxy distinguishes
|
||||||
|
// between nil and zero-length header values.
|
||||||
|
h2[k] = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
n := copy(sv, vv)
|
n := copy(sv, vv)
|
||||||
h2[k] = sv[:n:n]
|
h2[k] = sv[:n:n]
|
||||||
sv = sv[n:]
|
sv = sv[n:]
|
||||||
|
@ -235,6 +235,11 @@ func TestCloneOrMakeHeader(t *testing.T) {
|
|||||||
in: Header{"foo": {"bar"}},
|
in: Header{"foo": {"bar"}},
|
||||||
want: Header{"foo": {"bar"}},
|
want: Header{"foo": {"bar"}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "nil value",
|
||||||
|
in: Header{"foo": nil},
|
||||||
|
want: Header{"foo": nil},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -6189,6 +6189,7 @@ func TestUnsupportedTransferEncodingsReturn501(t *testing.T) {
|
|||||||
"fugazi",
|
"fugazi",
|
||||||
"foo-bar",
|
"foo-bar",
|
||||||
"unknown",
|
"unknown",
|
||||||
|
"\rchunked",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, badTE := range unsupportedTEs {
|
for _, badTE := range unsupportedTEs {
|
||||||
|
@ -639,7 +639,7 @@ func (t *transferReader) parseTransferEncoding() error {
|
|||||||
if len(raw) != 1 {
|
if len(raw) != 1 {
|
||||||
return &unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", raw)}
|
return &unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", raw)}
|
||||||
}
|
}
|
||||||
if !ascii.EqualFold(textproto.TrimString(raw[0]), "chunked") {
|
if !ascii.EqualFold(raw[0], "chunked") {
|
||||||
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])}
|
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +241,16 @@ func getEsc(chunk string) (r rune, nchunk string, err error) {
|
|||||||
// The only possible returned error is ErrBadPattern, when pattern
|
// The only possible returned error is ErrBadPattern, when pattern
|
||||||
// is malformed.
|
// is malformed.
|
||||||
func Glob(pattern string) (matches []string, err error) {
|
func Glob(pattern string) (matches []string, err error) {
|
||||||
|
return globWithLimit(pattern, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func globWithLimit(pattern string, depth int) (matches []string, err error) {
|
||||||
|
// This limit is used prevent stack exhaustion issues. See CVE-2022-30632.
|
||||||
|
const pathSeparatorsLimit = 10000
|
||||||
|
if depth == pathSeparatorsLimit {
|
||||||
|
return nil, ErrBadPattern
|
||||||
|
}
|
||||||
|
|
||||||
// Check pattern is well-formed.
|
// Check pattern is well-formed.
|
||||||
if _, err := Match(pattern, ""); err != nil {
|
if _, err := Match(pattern, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -270,7 +280,7 @@ func Glob(pattern string) (matches []string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var m []string
|
var m []string
|
||||||
m, err = Glob(dir)
|
m, err = globWithLimit(dir, depth+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,16 @@ func TestGlob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCVE202230632(t *testing.T) {
|
||||||
|
// Prior to CVE-2022-30632, this would cause a stack exhaustion given a
|
||||||
|
// large number of separators (more than 4,000,000). There is now a limit
|
||||||
|
// of 10,000.
|
||||||
|
_, err := Glob("/*" + strings.Repeat("/", 10001))
|
||||||
|
if err != ErrBadPattern {
|
||||||
|
t.Fatalf("Glob returned err=%v, want ErrBadPattern", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGlobError(t *testing.T) {
|
func TestGlobError(t *testing.T) {
|
||||||
bad := []string{`[]`, `nonexist/[]`}
|
bad := []string{`[]`, `nonexist/[]`}
|
||||||
for _, pattern := range bad {
|
for _, pattern := range bad {
|
||||||
|
@ -321,9 +321,9 @@ func ReadMetricsSlow(memStats *MemStats, samplesp unsafe.Pointer, len, cap int)
|
|||||||
|
|
||||||
// Initialize the metrics beforehand because this could
|
// Initialize the metrics beforehand because this could
|
||||||
// allocate and skew the stats.
|
// allocate and skew the stats.
|
||||||
semacquire(&metricsSema)
|
metricsLock()
|
||||||
initMetrics()
|
initMetrics()
|
||||||
semrelease(&metricsSema)
|
metricsUnlock()
|
||||||
|
|
||||||
systemstack(func() {
|
systemstack(func() {
|
||||||
// Read memstats first. It's going to flush
|
// Read memstats first. It's going to flush
|
||||||
|
@ -175,11 +175,11 @@ func (c *mcache) refill(spc spanClass) {
|
|||||||
// Assume all objects from this span will be allocated in the
|
// Assume all objects from this span will be allocated in the
|
||||||
// mcache. If it gets uncached, we'll adjust this.
|
// mcache. If it gets uncached, we'll adjust this.
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.smallAllocCount[spc.sizeclass()], uintptr(s.nelems)-uintptr(s.allocCount))
|
atomic.Xadd64(&stats.smallAllocCount[spc.sizeclass()], int64(s.nelems)-int64(s.allocCount))
|
||||||
|
|
||||||
// Flush tinyAllocs.
|
// Flush tinyAllocs.
|
||||||
if spc == tinySpanClass {
|
if spc == tinySpanClass {
|
||||||
atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs)
|
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
|
||||||
c.tinyAllocs = 0
|
c.tinyAllocs = 0
|
||||||
}
|
}
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
@ -229,8 +229,8 @@ func (c *mcache) allocLarge(size uintptr, needzero bool, noscan bool) (*mspan, b
|
|||||||
throw("out of memory")
|
throw("out of memory")
|
||||||
}
|
}
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.largeAlloc, npages*pageSize)
|
atomic.Xadd64(&stats.largeAlloc, int64(npages*pageSize))
|
||||||
atomic.Xadduintptr(&stats.largeAllocCount, 1)
|
atomic.Xadd64(&stats.largeAllocCount, 1)
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
|
|
||||||
// Update gcController.heapLive and revise pacing if needed.
|
// Update gcController.heapLive and revise pacing if needed.
|
||||||
@ -261,9 +261,9 @@ func (c *mcache) releaseAll() {
|
|||||||
s := c.alloc[i]
|
s := c.alloc[i]
|
||||||
if s != &emptymspan {
|
if s != &emptymspan {
|
||||||
// Adjust nsmallalloc in case the span wasn't fully allocated.
|
// Adjust nsmallalloc in case the span wasn't fully allocated.
|
||||||
n := uintptr(s.nelems) - uintptr(s.allocCount)
|
n := int64(s.nelems) - int64(s.allocCount)
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.smallAllocCount[spanClass(i).sizeclass()], -n)
|
atomic.Xadd64(&stats.smallAllocCount[spanClass(i).sizeclass()], -n)
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
if s.sweepgen != sg+1 {
|
if s.sweepgen != sg+1 {
|
||||||
// refill conservatively counted unallocated slots in gcController.heapLive.
|
// refill conservatively counted unallocated slots in gcController.heapLive.
|
||||||
@ -273,7 +273,7 @@ func (c *mcache) releaseAll() {
|
|||||||
// gcController.heapLive was totally recomputed since
|
// gcController.heapLive was totally recomputed since
|
||||||
// caching this span, so we don't do this for
|
// caching this span, so we don't do this for
|
||||||
// stale spans.
|
// stale spans.
|
||||||
atomic.Xadd64(&gcController.heapLive, -int64(n)*int64(s.elemsize))
|
atomic.Xadd64(&gcController.heapLive, -n*int64(s.elemsize))
|
||||||
}
|
}
|
||||||
// Release the span to the mcentral.
|
// Release the span to the mcentral.
|
||||||
mheap_.central[i].mcentral.uncacheSpan(s)
|
mheap_.central[i].mcentral.uncacheSpan(s)
|
||||||
@ -286,7 +286,7 @@ func (c *mcache) releaseAll() {
|
|||||||
|
|
||||||
// Flush tinyAllocs.
|
// Flush tinyAllocs.
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs)
|
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
|
||||||
c.tinyAllocs = 0
|
c.tinyAllocs = 0
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
|
|
||||||
|
@ -12,9 +12,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// metrics is a map of runtime/metrics keys to
|
// metrics is a map of runtime/metrics keys to data used by the runtime
|
||||||
// data used by the runtime to sample each metric's
|
// to sample each metric's value. metricsInit indicates it has been
|
||||||
// value.
|
// initialized.
|
||||||
|
//
|
||||||
|
// These fields are protected by metricsSema which should be
|
||||||
|
// locked/unlocked with metricsLock() / metricsUnlock().
|
||||||
metricsSema uint32 = 1
|
metricsSema uint32 = 1
|
||||||
metricsInit bool
|
metricsInit bool
|
||||||
metrics map[string]metricData
|
metrics map[string]metricData
|
||||||
@ -34,6 +37,23 @@ type metricData struct {
|
|||||||
compute func(in *statAggregate, out *metricValue)
|
compute func(in *statAggregate, out *metricValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func metricsLock() {
|
||||||
|
// Acquire the metricsSema but with handoff. Operations are typically
|
||||||
|
// expensive enough that queueing up goroutines and handing off between
|
||||||
|
// them will be noticeably better-behaved.
|
||||||
|
semacquire1(&metricsSema, true, 0, 0)
|
||||||
|
if raceenabled {
|
||||||
|
raceacquire(unsafe.Pointer(&metricsSema))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func metricsUnlock() {
|
||||||
|
if raceenabled {
|
||||||
|
racerelease(unsafe.Pointer(&metricsSema))
|
||||||
|
}
|
||||||
|
semrelease(&metricsSema)
|
||||||
|
}
|
||||||
|
|
||||||
// initMetrics initializes the metrics map if it hasn't been yet.
|
// initMetrics initializes the metrics map if it hasn't been yet.
|
||||||
//
|
//
|
||||||
// metricsSema must be held.
|
// metricsSema must be held.
|
||||||
@ -388,13 +408,13 @@ func (a *heapStatsAggregate) compute() {
|
|||||||
memstats.heapStats.read(&a.heapStatsDelta)
|
memstats.heapStats.read(&a.heapStatsDelta)
|
||||||
|
|
||||||
// Calculate derived stats.
|
// Calculate derived stats.
|
||||||
a.totalAllocs = uint64(a.largeAllocCount)
|
a.totalAllocs = a.largeAllocCount
|
||||||
a.totalFrees = uint64(a.largeFreeCount)
|
a.totalFrees = a.largeFreeCount
|
||||||
a.totalAllocated = uint64(a.largeAlloc)
|
a.totalAllocated = a.largeAlloc
|
||||||
a.totalFreed = uint64(a.largeFree)
|
a.totalFreed = a.largeFree
|
||||||
for i := range a.smallAllocCount {
|
for i := range a.smallAllocCount {
|
||||||
na := uint64(a.smallAllocCount[i])
|
na := a.smallAllocCount[i]
|
||||||
nf := uint64(a.smallFreeCount[i])
|
nf := a.smallFreeCount[i]
|
||||||
a.totalAllocs += na
|
a.totalAllocs += na
|
||||||
a.totalFrees += nf
|
a.totalFrees += nf
|
||||||
a.totalAllocated += na * uint64(class_to_size[i])
|
a.totalAllocated += na * uint64(class_to_size[i])
|
||||||
@ -546,10 +566,7 @@ func readMetrics(samplesp unsafe.Pointer, len int, cap int) {
|
|||||||
sl := slice{samplesp, len, cap}
|
sl := slice{samplesp, len, cap}
|
||||||
samples := *(*[]metricSample)(unsafe.Pointer(&sl))
|
samples := *(*[]metricSample)(unsafe.Pointer(&sl))
|
||||||
|
|
||||||
// Acquire the metricsSema but with handoff. This operation
|
metricsLock()
|
||||||
// is expensive enough that queueing up goroutines and handing
|
|
||||||
// off between them will be noticeably better-behaved.
|
|
||||||
semacquire1(&metricsSema, true, 0, 0)
|
|
||||||
|
|
||||||
// Ensure the map is initialized.
|
// Ensure the map is initialized.
|
||||||
initMetrics()
|
initMetrics()
|
||||||
@ -573,5 +590,5 @@ func readMetrics(samplesp unsafe.Pointer, len int, cap int) {
|
|||||||
data.compute(&agg, &sample.value)
|
data.compute(&agg, &sample.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
semrelease(&metricsSema)
|
metricsUnlock()
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
|
|||||||
// free slots zeroed.
|
// free slots zeroed.
|
||||||
s.needzero = 1
|
s.needzero = 1
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.smallFreeCount[spc.sizeclass()], uintptr(nfreed))
|
atomic.Xadd64(&stats.smallFreeCount[spc.sizeclass()], int64(nfreed))
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
}
|
}
|
||||||
if !preserve {
|
if !preserve {
|
||||||
@ -628,8 +628,8 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
|
|||||||
mheap_.freeSpan(s)
|
mheap_.freeSpan(s)
|
||||||
}
|
}
|
||||||
stats := memstats.heapStats.acquire()
|
stats := memstats.heapStats.acquire()
|
||||||
atomic.Xadduintptr(&stats.largeFreeCount, 1)
|
atomic.Xadd64(&stats.largeFreeCount, 1)
|
||||||
atomic.Xadduintptr(&stats.largeFree, size)
|
atomic.Xadd64(&stats.largeFree, int64(size))
|
||||||
memstats.heapStats.release()
|
memstats.heapStats.release()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime/internal/atomic"
|
"runtime/internal/atomic"
|
||||||
"runtime/internal/sys"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -565,29 +564,29 @@ func updatememstats() {
|
|||||||
memstats.heapStats.unsafeRead(&consStats)
|
memstats.heapStats.unsafeRead(&consStats)
|
||||||
|
|
||||||
// Collect large allocation stats.
|
// Collect large allocation stats.
|
||||||
totalAlloc := uint64(consStats.largeAlloc)
|
totalAlloc := consStats.largeAlloc
|
||||||
memstats.nmalloc += uint64(consStats.largeAllocCount)
|
memstats.nmalloc += consStats.largeAllocCount
|
||||||
totalFree := uint64(consStats.largeFree)
|
totalFree := consStats.largeFree
|
||||||
memstats.nfree += uint64(consStats.largeFreeCount)
|
memstats.nfree += consStats.largeFreeCount
|
||||||
|
|
||||||
// Collect per-sizeclass stats.
|
// Collect per-sizeclass stats.
|
||||||
for i := 0; i < _NumSizeClasses; i++ {
|
for i := 0; i < _NumSizeClasses; i++ {
|
||||||
// Malloc stats.
|
// Malloc stats.
|
||||||
a := uint64(consStats.smallAllocCount[i])
|
a := consStats.smallAllocCount[i]
|
||||||
totalAlloc += a * uint64(class_to_size[i])
|
totalAlloc += a * uint64(class_to_size[i])
|
||||||
memstats.nmalloc += a
|
memstats.nmalloc += a
|
||||||
memstats.by_size[i].nmalloc = a
|
memstats.by_size[i].nmalloc = a
|
||||||
|
|
||||||
// Free stats.
|
// Free stats.
|
||||||
f := uint64(consStats.smallFreeCount[i])
|
f := consStats.smallFreeCount[i]
|
||||||
totalFree += f * uint64(class_to_size[i])
|
totalFree += f * uint64(class_to_size[i])
|
||||||
memstats.nfree += f
|
memstats.nfree += f
|
||||||
memstats.by_size[i].nfree = f
|
memstats.by_size[i].nfree = f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account for tiny allocations.
|
// Account for tiny allocations.
|
||||||
memstats.nfree += uint64(consStats.tinyAllocCount)
|
memstats.nfree += consStats.tinyAllocCount
|
||||||
memstats.nmalloc += uint64(consStats.tinyAllocCount)
|
memstats.nmalloc += consStats.tinyAllocCount
|
||||||
|
|
||||||
// Calculate derived stats.
|
// Calculate derived stats.
|
||||||
memstats.total_alloc = totalAlloc
|
memstats.total_alloc = totalAlloc
|
||||||
@ -703,17 +702,20 @@ type heapStatsDelta struct {
|
|||||||
inPtrScalarBits int64 // byte delta of memory reserved for unrolled GC prog bits
|
inPtrScalarBits int64 // byte delta of memory reserved for unrolled GC prog bits
|
||||||
|
|
||||||
// Allocator stats.
|
// Allocator stats.
|
||||||
tinyAllocCount uintptr // number of tiny allocations
|
//
|
||||||
largeAlloc uintptr // bytes allocated for large objects
|
// These are all uint64 because they're cumulative, and could quickly wrap
|
||||||
largeAllocCount uintptr // number of large object allocations
|
// around otherwise.
|
||||||
smallAllocCount [_NumSizeClasses]uintptr // number of allocs for small objects
|
tinyAllocCount uint64 // number of tiny allocations
|
||||||
largeFree uintptr // bytes freed for large objects (>maxSmallSize)
|
largeAlloc uint64 // bytes allocated for large objects
|
||||||
largeFreeCount uintptr // number of frees for large objects (>maxSmallSize)
|
largeAllocCount uint64 // number of large object allocations
|
||||||
smallFreeCount [_NumSizeClasses]uintptr // number of frees for small objects (<=maxSmallSize)
|
smallAllocCount [_NumSizeClasses]uint64 // number of allocs for small objects
|
||||||
|
largeFree uint64 // bytes freed for large objects (>maxSmallSize)
|
||||||
|
largeFreeCount uint64 // number of frees for large objects (>maxSmallSize)
|
||||||
|
smallFreeCount [_NumSizeClasses]uint64 // number of frees for small objects (<=maxSmallSize)
|
||||||
|
|
||||||
// Add a uint32 to ensure this struct is a multiple of 8 bytes in size.
|
// NOTE: This struct must be a multiple of 8 bytes in size because it
|
||||||
// Only necessary on 32-bit platforms.
|
// is stored in an array. If it's not, atomic accesses to the above
|
||||||
_ [(sys.PtrSize / 4) % 2]uint32
|
// fields may be unaligned and fail on 32-bit platforms.
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge adds in the deltas from b into a.
|
// merge adds in the deltas from b into a.
|
||||||
|
@ -12,12 +12,30 @@ import (
|
|||||||
|
|
||||||
// cbs stores all registered Go callbacks.
|
// cbs stores all registered Go callbacks.
|
||||||
var cbs struct {
|
var cbs struct {
|
||||||
lock mutex
|
lock mutex // use cbsLock / cbsUnlock for race instrumentation.
|
||||||
ctxt [cb_max]winCallback
|
ctxt [cb_max]winCallback
|
||||||
index map[winCallbackKey]int
|
index map[winCallbackKey]int
|
||||||
n int
|
n int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cbsLock() {
|
||||||
|
lock(&cbs.lock)
|
||||||
|
// compileCallback is used by goenvs prior to completion of schedinit.
|
||||||
|
// raceacquire involves a racecallback to get the proc, which is not
|
||||||
|
// safe prior to scheduler initialization. Thus avoid instrumentation
|
||||||
|
// until then.
|
||||||
|
if raceenabled && mainStarted {
|
||||||
|
raceacquire(unsafe.Pointer(&cbs.lock))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cbsUnlock() {
|
||||||
|
if raceenabled && mainStarted {
|
||||||
|
racerelease(unsafe.Pointer(&cbs.lock))
|
||||||
|
}
|
||||||
|
unlock(&cbs.lock)
|
||||||
|
}
|
||||||
|
|
||||||
// winCallback records information about a registered Go callback.
|
// winCallback records information about a registered Go callback.
|
||||||
type winCallback struct {
|
type winCallback struct {
|
||||||
fn *funcval // Go function
|
fn *funcval // Go function
|
||||||
@ -302,11 +320,11 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
|
|||||||
|
|
||||||
key := winCallbackKey{(*funcval)(fn.data), cdecl}
|
key := winCallbackKey{(*funcval)(fn.data), cdecl}
|
||||||
|
|
||||||
lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack.
|
cbsLock()
|
||||||
|
|
||||||
// Check if this callback is already registered.
|
// Check if this callback is already registered.
|
||||||
if n, ok := cbs.index[key]; ok {
|
if n, ok := cbs.index[key]; ok {
|
||||||
unlock(&cbs.lock)
|
cbsUnlock()
|
||||||
return callbackasmAddr(n)
|
return callbackasmAddr(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +334,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
|
|||||||
}
|
}
|
||||||
n := cbs.n
|
n := cbs.n
|
||||||
if n >= len(cbs.ctxt) {
|
if n >= len(cbs.ctxt) {
|
||||||
unlock(&cbs.lock)
|
cbsUnlock()
|
||||||
throw("too many callback functions")
|
throw("too many callback functions")
|
||||||
}
|
}
|
||||||
c := winCallback{key.fn, retPop, abiMap}
|
c := winCallback{key.fn, retPop, abiMap}
|
||||||
@ -324,7 +342,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
|
|||||||
cbs.index[key] = n
|
cbs.index[key] = n
|
||||||
cbs.n++
|
cbs.n++
|
||||||
|
|
||||||
unlock(&cbs.lock)
|
cbsUnlock()
|
||||||
return callbackasmAddr(n)
|
return callbackasmAddr(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
89
test/fixedbugs/issue53454.go
Normal file
89
test/fixedbugs/issue53454.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// compile
|
||||||
|
|
||||||
|
// Copyright 2022 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 main
|
||||||
|
|
||||||
|
type T1 struct {
|
||||||
|
A T5
|
||||||
|
B T2
|
||||||
|
C T7
|
||||||
|
D T4
|
||||||
|
}
|
||||||
|
|
||||||
|
type T2 struct {
|
||||||
|
T3
|
||||||
|
A float64
|
||||||
|
E float64
|
||||||
|
C float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type T3 struct {
|
||||||
|
F float64
|
||||||
|
G float64
|
||||||
|
H float64
|
||||||
|
I float64
|
||||||
|
J float64
|
||||||
|
K float64
|
||||||
|
L float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type T4 struct {
|
||||||
|
M float64
|
||||||
|
N float64
|
||||||
|
O float64
|
||||||
|
P float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type T5 struct {
|
||||||
|
Q float64
|
||||||
|
R float64
|
||||||
|
S float64
|
||||||
|
T float64
|
||||||
|
U float64
|
||||||
|
V float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type T6 struct {
|
||||||
|
T9
|
||||||
|
C T10
|
||||||
|
}
|
||||||
|
|
||||||
|
type T7 struct {
|
||||||
|
T10
|
||||||
|
T11
|
||||||
|
}
|
||||||
|
|
||||||
|
type T8 struct {
|
||||||
|
T9
|
||||||
|
C T7
|
||||||
|
}
|
||||||
|
|
||||||
|
type T9 struct {
|
||||||
|
A T5
|
||||||
|
B T3
|
||||||
|
D T4
|
||||||
|
}
|
||||||
|
|
||||||
|
type T10 struct {
|
||||||
|
W float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type T11 struct {
|
||||||
|
X float64
|
||||||
|
Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func MainTest(x T1, y T8, z T6) float64 {
|
||||||
|
return Test(x.B, x.A, x.D, x.C, y.B, y.A, y.D, y.C, z.B, z.A, z.D,
|
||||||
|
T7{
|
||||||
|
T10: T10{
|
||||||
|
W: z.C.W,
|
||||||
|
},
|
||||||
|
T11: T11{},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
func Test(a T2, b T5, c T4, d T7, e T3, f T5, g T4, h T7, i T3, j T5, k T4, l T7) float64
|
Loading…
x
Reference in New Issue
Block a user