mirror of
https://github.com/golang/go.git
synced 2025-05-09 09:33:03 +00:00
Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d04e3cbc92 | ||
|
bb8230f805 | ||
|
fdb8413fe5 | ||
|
1dde0b4844 | ||
|
3417000c69 | ||
|
1576793c51 | ||
|
59b7d40774 | ||
|
69c8cfe29b | ||
|
194de8fbfa | ||
|
5164a865e3 | ||
|
25f042daec | ||
|
be062b7f61 | ||
|
d8adc6c4c7 | ||
|
847cb6f9ca | ||
|
777f43ab27 | ||
|
3726f07c46 | ||
|
c390a1c22e | ||
|
1207de4f6c | ||
|
a0d15cb9c8 | ||
|
958f3a0309 | ||
|
6ba3a8a6ba | ||
|
5472853843 | ||
|
cfe0ae0b70 | ||
|
58babf6e0b | ||
|
8d79bf799b | ||
|
35c010ad6d | ||
|
6495ce0495 | ||
|
7fc8312673 | ||
|
cc16cdf48f | ||
|
9563300f6e | ||
|
f8080edefd | ||
|
ed07b321ae | ||
|
3b2e846e11 | ||
|
fbddfae62f | ||
|
c8c6f9abfb | ||
|
a74951c5af | ||
|
e6598e7baa | ||
|
82575f76b8 | ||
|
a886959aa2 | ||
|
80ff7cd35a | ||
|
69234ded30 | ||
|
032ac075c2 | ||
|
fa8ff1a46d | ||
|
53487e5477 | ||
|
3d1f1f27cf | ||
|
6de5a7180c | ||
|
9625a7faae | ||
|
9c939a1e60 | ||
|
7afe17bbdb | ||
|
8002845759 | ||
|
9166d2feec | ||
|
76346b3543 | ||
|
3c9340557c | ||
|
dbecb416d1 | ||
|
6885bad7dd | ||
|
ec7d6094e6 | ||
|
63b0f805cd | ||
|
7adb012205 | ||
|
c9940fe2a9 | ||
|
3509415eca | ||
|
559c77592f | ||
|
f5e4e45ef7 | ||
|
30b6fd60a6 | ||
|
7e4d6c2bcb | ||
|
8bd4ed6cbb | ||
|
7dff7439dc | ||
|
62c3a6350b | ||
|
eba9e08766 | ||
|
f3bdcda88a | ||
|
362f22d2d2 |
@ -1 +1,2 @@
|
|||||||
branch: master
|
branch: release-branch.go1.23
|
||||||
|
parent-branch: master
|
||||||
|
@ -2579,6 +2579,11 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
|||||||
if dt.BitSize > 0 {
|
if dt.BitSize > 0 {
|
||||||
fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype)
|
fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||||
|
t.Align = c.ptrSize
|
||||||
|
}
|
||||||
|
|
||||||
switch t.Size {
|
switch t.Size {
|
||||||
default:
|
default:
|
||||||
fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype)
|
fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype)
|
||||||
@ -2595,9 +2600,8 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
|||||||
Len: c.intExpr(t.Size),
|
Len: c.intExpr(t.Size),
|
||||||
Elt: c.uint8,
|
Elt: c.uint8,
|
||||||
}
|
}
|
||||||
}
|
// t.Align is the alignment of the Go type.
|
||||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
t.Align = 1
|
||||||
t.Align = c.ptrSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *dwarf.PtrType:
|
case *dwarf.PtrType:
|
||||||
@ -2826,6 +2830,11 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
|||||||
if dt.BitSize > 0 {
|
if dt.BitSize > 0 {
|
||||||
fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype)
|
fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||||
|
t.Align = c.ptrSize
|
||||||
|
}
|
||||||
|
|
||||||
switch t.Size {
|
switch t.Size {
|
||||||
default:
|
default:
|
||||||
fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype)
|
fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype)
|
||||||
@ -2842,9 +2851,8 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
|||||||
Len: c.intExpr(t.Size),
|
Len: c.intExpr(t.Size),
|
||||||
Elt: c.uint8,
|
Elt: c.uint8,
|
||||||
}
|
}
|
||||||
}
|
// t.Align is the alignment of the Go type.
|
||||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
t.Align = 1
|
||||||
t.Align = c.ptrSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *dwarf.VoidType:
|
case *dwarf.VoidType:
|
||||||
@ -3110,10 +3118,11 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Round off up to talign, assumed to be a power of 2.
|
// Round off up to talign, assumed to be a power of 2.
|
||||||
|
origOff := off
|
||||||
off = (off + talign - 1) &^ (talign - 1)
|
off = (off + talign - 1) &^ (talign - 1)
|
||||||
|
|
||||||
if f.ByteOffset > off {
|
if f.ByteOffset > off {
|
||||||
fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
|
fld, sizes = c.pad(fld, sizes, f.ByteOffset-origOff)
|
||||||
off = f.ByteOffset
|
off = f.ByteOffset
|
||||||
}
|
}
|
||||||
if f.ByteOffset < off {
|
if f.ByteOffset < off {
|
||||||
|
@ -70,6 +70,7 @@ func Test31891(t *testing.T) { test31891(t) }
|
|||||||
func Test42018(t *testing.T) { test42018(t) }
|
func Test42018(t *testing.T) { test42018(t) }
|
||||||
func Test45451(t *testing.T) { test45451(t) }
|
func Test45451(t *testing.T) { test45451(t) }
|
||||||
func Test49633(t *testing.T) { test49633(t) }
|
func Test49633(t *testing.T) { test49633(t) }
|
||||||
|
func Test69086(t *testing.T) { test69086(t) }
|
||||||
func TestAlign(t *testing.T) { testAlign(t) }
|
func TestAlign(t *testing.T) { testAlign(t) }
|
||||||
func TestAtol(t *testing.T) { testAtol(t) }
|
func TestAtol(t *testing.T) { testAtol(t) }
|
||||||
func TestBlocking(t *testing.T) { testBlocking(t) }
|
func TestBlocking(t *testing.T) { testBlocking(t) }
|
||||||
|
@ -940,6 +940,19 @@ typedef struct {
|
|||||||
} issue67517struct;
|
} issue67517struct;
|
||||||
static void issue67517(issue67517struct* p) {}
|
static void issue67517(issue67517struct* p) {}
|
||||||
|
|
||||||
|
// Issue 69086.
|
||||||
|
// GCC added the __int128 type in GCC 4.6, released in 2011.
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
#ifdef __SIZEOF_INT128__
|
||||||
|
unsigned __int128 b;
|
||||||
|
#else
|
||||||
|
uint64_t b;
|
||||||
|
#endif
|
||||||
|
unsigned char c;
|
||||||
|
} issue69086struct;
|
||||||
|
static int issue690861(issue69086struct* p) { p->b = 1234; return p->c; }
|
||||||
|
static int issue690862(unsigned long ul1, unsigned long ul2, unsigned int u, issue69086struct s) { return (int)(s.b); }
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@ -2349,3 +2362,24 @@ func issue67517() {
|
|||||||
b: nil,
|
b: nil,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 69086.
|
||||||
|
func test69086(t *testing.T) {
|
||||||
|
var s C.issue69086struct
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(s)
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
f := typ.Field(i)
|
||||||
|
t.Logf("field %d: name %s size %d align %d offset %d", i, f.Name, f.Type.Size(), f.Type.Align(), f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.c = 1
|
||||||
|
got := C.issue690861(&s)
|
||||||
|
if got != 1 {
|
||||||
|
t.Errorf("field: got %d, want 1", got)
|
||||||
|
}
|
||||||
|
got = C.issue690862(1, 2, 3, s)
|
||||||
|
if got != 1234 {
|
||||||
|
t.Errorf("call: got %d, want 1234", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalSkip = func(t *testing.T) {}
|
var globalSkip = func(t testing.TB) {}
|
||||||
|
|
||||||
// Program to run.
|
// Program to run.
|
||||||
var bin []string
|
var bin []string
|
||||||
@ -59,12 +59,12 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
func testMain(m *testing.M) int {
|
func testMain(m *testing.M) int {
|
||||||
if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
|
if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
|
||||||
globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
|
globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
|
||||||
return m.Run()
|
return m.Run()
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
if _, err := os.Stat("/etc/alpine-release"); err == nil {
|
if _, err := os.Stat("/etc/alpine-release"); err == nil {
|
||||||
globalSkip = func(t *testing.T) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
|
globalSkip = func(t testing.TB) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
|
||||||
return m.Run()
|
return m.Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1291,8 +1291,8 @@ func TestPreemption(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue 59294. Test calling Go function from C after using some
|
// Issue 59294 and 68285. Test calling Go function from C after with
|
||||||
// stack space.
|
// various stack space.
|
||||||
func TestDeepStack(t *testing.T) {
|
func TestDeepStack(t *testing.T) {
|
||||||
globalSkip(t)
|
globalSkip(t)
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
@ -1350,6 +1350,53 @@ func TestDeepStack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkCgoCallbackMainThread(b *testing.B) {
|
||||||
|
// Benchmark for calling into Go fron C main thread.
|
||||||
|
// See issue #68587.
|
||||||
|
//
|
||||||
|
// It uses a subprocess, which is a C binary that calls
|
||||||
|
// Go on the main thread b.N times. There is some overhead
|
||||||
|
// for launching the subprocess. It is probably fine when
|
||||||
|
// b.N is large.
|
||||||
|
|
||||||
|
globalSkip(b)
|
||||||
|
testenv.MustHaveGoBuild(b)
|
||||||
|
testenv.MustHaveCGO(b)
|
||||||
|
testenv.MustHaveBuildMode(b, "c-archive")
|
||||||
|
|
||||||
|
if !testWork {
|
||||||
|
defer func() {
|
||||||
|
os.Remove("testp10" + exeSuffix)
|
||||||
|
os.Remove("libgo10.a")
|
||||||
|
os.Remove("libgo10.h")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo10.a", "./libgo10")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
b.Logf("%v\n%s", cmd.Args, out)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ccArgs := append(cc, "-o", "testp10"+exeSuffix, "main10.c", "libgo10.a")
|
||||||
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
|
b.Logf("%v\n%s", ccArgs, out)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
argv := cmdToRun("./testp10")
|
||||||
|
argv = append(argv, fmt.Sprint(b.N))
|
||||||
|
cmd = exec.Command(argv[0], argv[1:]...)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSharedObject(t *testing.T) {
|
func TestSharedObject(t *testing.T) {
|
||||||
// Test that we can put a Go c-archive into a C shared object.
|
// Test that we can put a Go c-archive into a C shared object.
|
||||||
globalSkip(t)
|
globalSkip(t)
|
||||||
|
12
src/cmd/cgo/internal/testcarchive/testdata/libgo10/a.go
vendored
Normal file
12
src/cmd/cgo/internal/testcarchive/testdata/libgo10/a.go
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2024 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
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
//export GoF
|
||||||
|
func GoF() {}
|
||||||
|
|
||||||
|
func main() {}
|
@ -6,9 +6,29 @@ package main
|
|||||||
|
|
||||||
import "runtime"
|
import "runtime"
|
||||||
|
|
||||||
|
// extern void callGoWithVariousStack(int);
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
func main() {}
|
func main() {}
|
||||||
|
|
||||||
//export GoF
|
//export GoF
|
||||||
func GoF() { runtime.GC() }
|
func GoF(p int32) {
|
||||||
|
runtime.GC()
|
||||||
|
if p != 0 {
|
||||||
|
panic("panic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export callGoWithVariousStackAndGoFrame
|
||||||
|
func callGoWithVariousStackAndGoFrame(p int32) {
|
||||||
|
if p != 0 {
|
||||||
|
defer func() {
|
||||||
|
e := recover()
|
||||||
|
if e == nil {
|
||||||
|
panic("did not panic")
|
||||||
|
}
|
||||||
|
runtime.GC()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
C.callGoWithVariousStack(C.int(p));
|
||||||
|
}
|
||||||
|
22
src/cmd/cgo/internal/testcarchive/testdata/main10.c
vendored
Normal file
22
src/cmd/cgo/internal/testcarchive/testdata/main10.c
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "libgo10.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int n, i;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
perror("wrong arg");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
n = atoi(argv[1]);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
GoF();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -6,19 +6,27 @@
|
|||||||
|
|
||||||
void use(int *x) { (*x)++; }
|
void use(int *x) { (*x)++; }
|
||||||
|
|
||||||
void callGoFWithDeepStack() {
|
void callGoFWithDeepStack(int p) {
|
||||||
int x[10000];
|
int x[10000];
|
||||||
|
|
||||||
use(&x[0]);
|
use(&x[0]);
|
||||||
use(&x[9999]);
|
use(&x[9999]);
|
||||||
|
|
||||||
GoF();
|
GoF(p);
|
||||||
|
|
||||||
use(&x[0]);
|
use(&x[0]);
|
||||||
use(&x[9999]);
|
use(&x[9999]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
void callGoWithVariousStack(int p) {
|
||||||
GoF(); // call GoF without using much stack
|
GoF(0); // call GoF without using much stack
|
||||||
callGoFWithDeepStack(); // call GoF with a deep stack
|
callGoFWithDeepStack(p); // call GoF with a deep stack
|
||||||
|
GoF(0); // again on a shallow stack
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
callGoWithVariousStack(0);
|
||||||
|
|
||||||
|
callGoWithVariousStackAndGoFrame(0); // normal execution
|
||||||
|
callGoWithVariousStackAndGoFrame(1); // panic and recover
|
||||||
}
|
}
|
||||||
|
@ -318,9 +318,10 @@ func containsClosure(f, c *ir.Func) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closures within function Foo are named like "Foo.funcN..."
|
for p := c.ClosureParent; p != nil; p = p.ClosureParent {
|
||||||
// TODO(mdempsky): Better way to recognize this.
|
if p == f {
|
||||||
fn := f.Sym().Name
|
return true
|
||||||
cn := c.Sym().Name
|
}
|
||||||
return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
@ -582,6 +582,23 @@ func TestIssue25596(t *testing.T) {
|
|||||||
compileAndImportPkg(t, "issue25596")
|
compileAndImportPkg(t, "issue25596")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue70394(t *testing.T) {
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := compileAndImportPkg(t, "alias")
|
||||||
|
obj := lookupObj(t, pkg.Scope(), "A")
|
||||||
|
|
||||||
|
typ := obj.Type()
|
||||||
|
if _, ok := typ.(*types2.Alias); !ok {
|
||||||
|
t.Fatalf("type of %s is %s, wanted an alias", obj, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func importPkg(t *testing.T, path, srcDir string) *types2.Package {
|
func importPkg(t *testing.T, path, srcDir string) *types2.Package {
|
||||||
pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
|
pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
7
src/cmd/compile/internal/importer/testdata/alias.go
vendored
Normal file
7
src/cmd/compile/internal/importer/testdata/alias.go
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2024 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 testdata
|
||||||
|
|
||||||
|
type A = int32
|
@ -9,6 +9,7 @@ import (
|
|||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"cmd/compile/internal/types2"
|
"cmd/compile/internal/types2"
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
|
"internal/buildcfg"
|
||||||
"internal/pkgbits"
|
"internal/pkgbits"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,9 +31,7 @@ func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input
|
|||||||
|
|
||||||
ctxt: ctxt,
|
ctxt: ctxt,
|
||||||
imports: imports,
|
imports: imports,
|
||||||
// Currently, the compiler panics when using Alias types.
|
enableAlias: true,
|
||||||
// TODO(gri) set to true once this is fixed (issue #66873)
|
|
||||||
enableAlias: false,
|
|
||||||
|
|
||||||
posBases: make([]*syntax.PosBase, input.NumElems(pkgbits.RelocPosBase)),
|
posBases: make([]*syntax.PosBase, input.NumElems(pkgbits.RelocPosBase)),
|
||||||
pkgs: make([]*types2.Package, input.NumElems(pkgbits.RelocPkg)),
|
pkgs: make([]*types2.Package, input.NumElems(pkgbits.RelocPkg)),
|
||||||
@ -411,6 +410,14 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
|
|||||||
panic("weird")
|
panic("weird")
|
||||||
|
|
||||||
case pkgbits.ObjAlias:
|
case pkgbits.ObjAlias:
|
||||||
|
if buildcfg.Experiment.AliasTypeParams && len(r.dict.bounds) > 0 {
|
||||||
|
// Temporary work-around for issue #68526: rather than panicking
|
||||||
|
// with an non-descriptive index-out-of-bounds panic when trying
|
||||||
|
// to access a missing type parameter, instead panic with a more
|
||||||
|
// descriptive error. Only needed for Go 1.23; Go 1.24 will have
|
||||||
|
// the correct implementation.
|
||||||
|
panic("importing generic type aliases is not supported in Go 1.23 (see issue #68526)")
|
||||||
|
}
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
typ := r.typ()
|
typ := r.typ()
|
||||||
return newAliasTypeName(pr.enableAlias, pos, objPkg, objName, typ)
|
return newAliasTypeName(pr.enableAlias, pos, objPkg, objName, typ)
|
||||||
|
@ -51,6 +51,8 @@ import (
|
|||||||
// the generated ODCLFUNC, but there is no
|
// the generated ODCLFUNC, but there is no
|
||||||
// pointer from the Func back to the OMETHVALUE.
|
// pointer from the Func back to the OMETHVALUE.
|
||||||
type Func struct {
|
type Func struct {
|
||||||
|
// if you add or remove a field, don't forget to update sizeof_test.go
|
||||||
|
|
||||||
miniNode
|
miniNode
|
||||||
Body Nodes
|
Body Nodes
|
||||||
|
|
||||||
@ -76,6 +78,9 @@ type Func struct {
|
|||||||
// Populated during walk.
|
// Populated during walk.
|
||||||
Closures []*Func
|
Closures []*Func
|
||||||
|
|
||||||
|
// Parent of a closure
|
||||||
|
ClosureParent *Func
|
||||||
|
|
||||||
// Parents records the parent scope of each scope within a
|
// Parents records the parent scope of each scope within a
|
||||||
// function. The root scope (0) has no parent, so the i'th
|
// function. The root scope (0) has no parent, so the i'th
|
||||||
// scope's parent is stored at Parents[i-1].
|
// scope's parent is stored at Parents[i-1].
|
||||||
@ -512,6 +517,7 @@ func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func,
|
|||||||
|
|
||||||
fn.Nname.Defn = fn
|
fn.Nname.Defn = fn
|
||||||
pkg.Funcs = append(pkg.Funcs, fn)
|
pkg.Funcs = append(pkg.Funcs, fn)
|
||||||
|
fn.ClosureParent = outerfn
|
||||||
|
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
_32bit uintptr // size on 32bit platforms
|
_32bit uintptr // size on 32bit platforms
|
||||||
_64bit uintptr // size on 64bit platforms
|
_64bit uintptr // size on 64bit platforms
|
||||||
}{
|
}{
|
||||||
{Func{}, 176, 296},
|
{Func{}, 180, 304},
|
||||||
{Name{}, 96, 168},
|
{Name{}, 96, 168},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2099,3 +2099,27 @@ func TestTwoLevelReturnCheck(t *testing.T) {
|
|||||||
t.Errorf("Expected y=3, got y=%d\n", y)
|
t.Errorf("Expected y=3, got y=%d\n", y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Bug70035(s1, s2, s3 []string) string {
|
||||||
|
var c1 string
|
||||||
|
for v1 := range slices.Values(s1) {
|
||||||
|
var c2 string
|
||||||
|
for v2 := range slices.Values(s2) {
|
||||||
|
var c3 string
|
||||||
|
for v3 := range slices.Values(s3) {
|
||||||
|
c3 = c3 + v3
|
||||||
|
}
|
||||||
|
c2 = c2 + v2 + c3
|
||||||
|
}
|
||||||
|
c1 = c1 + v1 + c2
|
||||||
|
}
|
||||||
|
return c1
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test70035(t *testing.T) {
|
||||||
|
got := Bug70035([]string{"1", "2", "3"}, []string{"a", "b", "c"}, []string{"A", "B", "C"})
|
||||||
|
want := "1aABCbABCcABC2aABCbABCcABC3aABCbABCcABC"
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -134,10 +134,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
|||||||
// newAliasInstance creates a new alias instance for the given origin and type
|
// newAliasInstance creates a new alias instance for the given origin and type
|
||||||
// arguments, recording pos as the position of its synthetic object (for error
|
// arguments, recording pos as the position of its synthetic object (for error
|
||||||
// reporting).
|
// reporting).
|
||||||
func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, expanding *Named, ctxt *Context) *Alias {
|
||||||
assert(len(targs) > 0)
|
assert(len(targs) > 0)
|
||||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
|
||||||
res := check.newAlias(obj, rhs)
|
res := check.newAlias(obj, rhs)
|
||||||
res.orig = orig
|
res.orig = orig
|
||||||
res.tparams = orig.tparams
|
res.tparams = orig.tparams
|
||||||
|
@ -2900,20 +2900,46 @@ func TestFileVersions(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"", "", ""}, // no versions specified
|
{"", "", ""}, // no versions specified
|
||||||
{"go1.19", "", "go1.19"}, // module version specified
|
{"go1.19", "", "go1.19"}, // module version specified
|
||||||
{"", "go1.20", ""}, // file upgrade ignored
|
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
|
{"go1", "", "go1"}, // no file version specified
|
||||||
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
|
{"go1", "goo1.22", "go1"}, // invalid file version specified
|
||||||
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.19", "", "go1.19"}, // no file version specified
|
||||||
|
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
|
||||||
|
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.20", "", "go1.20"}, // no file version specified
|
||||||
|
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
|
||||||
|
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.21", "", "go1.21"}, // no file version specified
|
||||||
|
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
|
||||||
|
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.22", "", "go1.22"}, // no file version specified
|
||||||
|
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
|
||||||
|
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
|
||||||
// versions containing release numbers
|
// versions containing release numbers
|
||||||
// (file versions containing release numbers are considered invalid)
|
// (file versions containing release numbers are considered invalid)
|
||||||
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
||||||
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
|
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
|
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
|
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
|
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
|
||||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
|
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
|
||||||
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
|
||||||
} {
|
} {
|
||||||
var src string
|
var src string
|
||||||
if test.fileVersion != "" {
|
if test.fileVersion != "" {
|
||||||
|
@ -327,7 +327,6 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
|||||||
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
||||||
check.version, go_current)
|
check.version, go_current)
|
||||||
}
|
}
|
||||||
downgradeOk := check.version.cmp(go1_21) >= 0
|
|
||||||
|
|
||||||
// determine Go version for each file
|
// determine Go version for each file
|
||||||
for _, file := range check.files {
|
for _, file := range check.files {
|
||||||
@ -336,33 +335,18 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
|||||||
// unlike file versions which are Go language versions only, if valid.)
|
// unlike file versions which are Go language versions only, if valid.)
|
||||||
v := check.conf.GoVersion
|
v := check.conf.GoVersion
|
||||||
|
|
||||||
fileVersion := asGoVersion(file.GoVersion)
|
// If the file specifies a version, use max(fileVersion, go1.21).
|
||||||
if fileVersion.isValid() {
|
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
|
||||||
// use the file version, if applicable
|
// Go 1.21 introduced the feature of allowing //go:build lines
|
||||||
// (file versions are either the empty string or of the form go1.dd)
|
// to sometimes set the Go version in a given file. Versions Go 1.21 and later
|
||||||
if pkgVersionOk {
|
// can be set backwards compatibly as that was the first version
|
||||||
cmp := fileVersion.cmp(check.version)
|
// files with go1.21 or later build tags could be built with.
|
||||||
// Go 1.21 introduced the feature of setting the go.mod
|
|
||||||
// go line to an early version of Go and allowing //go:build lines
|
|
||||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
|
||||||
// We can do that backwards compatibly.
|
|
||||||
//
|
//
|
||||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
// downgrade to a version before go1.22, where the for loop semantics
|
||||||
// That can't be done compatibly in general, since before the
|
// change was made, while being backwards compatible with versions of
|
||||||
// build lines were ignored and code got the module's Go version.
|
// go before the new //go:build semantics were introduced.
|
||||||
// To work around this, downgrades are only allowed when the
|
v = string(versionMax(fileVersion, go1_21))
|
||||||
// module's Go version is Go 1.21 or later.
|
|
||||||
//
|
|
||||||
// If there is no valid check.version, then we don't really know what
|
|
||||||
// Go version to apply.
|
|
||||||
// Legacy tools may do this, and they historically have accepted everything.
|
|
||||||
// Preserve that behavior by ignoring //go:build constraints entirely in that
|
|
||||||
// case (!pkgVersionOk).
|
|
||||||
if cmp > 0 || cmp < 0 && downgradeOk {
|
|
||||||
v = file.GoVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report a specific error for each tagged file that's too new.
|
// Report a specific error for each tagged file that's too new.
|
||||||
// (Normally the build system will have filtered files by version,
|
// (Normally the build system will have filtered files by version,
|
||||||
@ -377,6 +361,13 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func versionMax(a, b goVersion) goVersion {
|
||||||
|
if a.cmp(b) > 0 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// A bailout panic is used for early termination.
|
// A bailout panic is used for early termination.
|
||||||
type bailout struct{}
|
type bailout struct{}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/buildcfg"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -126,8 +127,9 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
|||||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||||
|
|
||||||
case *Alias:
|
case *Alias:
|
||||||
// TODO(gri) is this correct?
|
if !buildcfg.Experiment.AliasTypeParams {
|
||||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||||
|
}
|
||||||
|
|
||||||
tparams := orig.TypeParams()
|
tparams := orig.TypeParams()
|
||||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||||
@ -138,7 +140,7 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
|||||||
return orig // nothing to do (minor optimization)
|
return orig // nothing to do (minor optimization)
|
||||||
}
|
}
|
||||||
|
|
||||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||||
|
@ -1121,3 +1121,23 @@ func f(x int) {
|
|||||||
t.Errorf("got: %s want: %s", got, want)
|
t.Errorf("got: %s want: %s", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue68877(t *testing.T) {
|
||||||
|
const src = `
|
||||||
|
package p
|
||||||
|
|
||||||
|
type (
|
||||||
|
S struct{}
|
||||||
|
A = S
|
||||||
|
T A
|
||||||
|
)`
|
||||||
|
|
||||||
|
conf := Config{EnableAlias: true}
|
||||||
|
pkg := mustTypecheck(src, &conf, nil)
|
||||||
|
T := pkg.Scope().Lookup("T").(*TypeName)
|
||||||
|
got := T.String() // this must not panic (was issue)
|
||||||
|
const want = "type p.T struct{}"
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -282,7 +282,7 @@ func (t *Named) cleanup() {
|
|||||||
if t.TypeArgs().Len() == 0 {
|
if t.TypeArgs().Len() == 0 {
|
||||||
panic("nil underlying")
|
panic("nil underlying")
|
||||||
}
|
}
|
||||||
case *Named:
|
case *Named, *Alias:
|
||||||
t.under() // t.under may add entries to check.cleaners
|
t.under() // t.under may add entries to check.cleaners
|
||||||
}
|
}
|
||||||
t.check = nil
|
t.check = nil
|
||||||
|
@ -115,7 +115,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// that has a type argument for it.
|
// that has a type argument for it.
|
||||||
targs, updated := subst.typeList(t.TypeArgs().list())
|
targs, updated := subst.typeList(t.TypeArgs().list())
|
||||||
if updated {
|
if updated {
|
||||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
|
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Array:
|
case *Array:
|
||||||
|
@ -131,8 +131,8 @@ func (s *_TypeSet) underIs(f func(Type) bool) bool {
|
|||||||
}
|
}
|
||||||
for _, t := range s.terms {
|
for _, t := range s.terms {
|
||||||
assert(t.typ != nil)
|
assert(t.typ != nil)
|
||||||
// x == under(x) for ~x terms
|
// Unalias(x) == under(x) for ~x terms
|
||||||
u := t.typ
|
u := Unalias(t.typ)
|
||||||
if !t.tilde {
|
if !t.tilde {
|
||||||
u = under(u)
|
u = under(u)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ require (
|
|||||||
golang.org/x/mod v0.19.0
|
golang.org/x/mod v0.19.0
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.7.0
|
||||||
golang.org/x/sys v0.22.0
|
golang.org/x/sys v0.22.0
|
||||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701
|
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147
|
||||||
golang.org/x/term v0.20.0
|
golang.org/x/term v0.20.0
|
||||||
golang.org/x/tools v0.22.1-0.20240618181713-f2d2ebe43e72
|
golang.org/x/tools v0.22.1-0.20240618181713-f2d2ebe43e72
|
||||||
)
|
)
|
||||||
|
@ -16,8 +16,8 @@ golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
|||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701 h1:+bltxAtk8YFEQ61B/lcYQM8e+7XjLwSDbpspVaVYkz8=
|
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147 h1:Lj8KbuZmoFUbI6pQ28G3Diz/5bRYD2UY5vfAmhrLZWo=
|
||||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701/go.mod h1:amNmu/SBSm2GAF3X+9U2C0epLocdh+r5Z+7oMYO5cLM=
|
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147/go.mod h1:amNmu/SBSm2GAF3X+9U2C0epLocdh+r5Z+7oMYO5cLM=
|
||||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
@ -805,13 +805,19 @@ func elfwritefreebsdsig(out *OutBuf) int {
|
|||||||
return int(sh.Size)
|
return int(sh.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addbuildinfo(val string) {
|
func addbuildinfo(ctxt *Link) {
|
||||||
|
val := *flagHostBuildid
|
||||||
if val == "gobuildid" {
|
if val == "gobuildid" {
|
||||||
buildID := *flagBuildid
|
buildID := *flagBuildid
|
||||||
if buildID == "" {
|
if buildID == "" {
|
||||||
Exitf("-B gobuildid requires a Go build ID supplied via -buildid")
|
Exitf("-B gobuildid requires a Go build ID supplied via -buildid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctxt.IsDarwin() {
|
||||||
|
buildinfo = uuidFromGoBuildId(buildID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
hashedBuildID := notsha256.Sum256([]byte(buildID))
|
hashedBuildID := notsha256.Sum256([]byte(buildID))
|
||||||
buildinfo = hashedBuildID[:20]
|
buildinfo = hashedBuildID[:20]
|
||||||
|
|
||||||
@ -821,11 +827,13 @@ func addbuildinfo(val string) {
|
|||||||
if !strings.HasPrefix(val, "0x") {
|
if !strings.HasPrefix(val, "0x") {
|
||||||
Exitf("-B argument must start with 0x: %s", val)
|
Exitf("-B argument must start with 0x: %s", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
ov := val
|
ov := val
|
||||||
val = val[2:]
|
val = val[2:]
|
||||||
|
|
||||||
const maxLen = 32
|
maxLen := 32
|
||||||
|
if ctxt.IsDarwin() {
|
||||||
|
maxLen = 16
|
||||||
|
}
|
||||||
if hex.DecodedLen(len(val)) > maxLen {
|
if hex.DecodedLen(len(val)) > maxLen {
|
||||||
Exitf("-B option too long (max %d digits): %s", maxLen, ov)
|
Exitf("-B option too long (max %d digits): %s", maxLen, ov)
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,8 @@ func getMachoHdr() *MachoHdr {
|
|||||||
return &machohdr
|
return &machohdr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new Mach-O load command. ndata is the number of 32-bit words for
|
||||||
|
// the data (not including the load command header).
|
||||||
func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
|
func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
|
||||||
if arch.PtrSize == 8 && (ndata&1 != 0) {
|
if arch.PtrSize == 8 && (ndata&1 != 0) {
|
||||||
ndata++
|
ndata++
|
||||||
@ -849,6 +851,20 @@ func asmbMacho(ctxt *Link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctxt.IsInternal() && len(buildinfo) > 0 {
|
||||||
|
ml := newMachoLoad(ctxt.Arch, LC_UUID, 4)
|
||||||
|
// Mach-O UUID is 16 bytes
|
||||||
|
if len(buildinfo) < 16 {
|
||||||
|
buildinfo = append(buildinfo, make([]byte, 16)...)
|
||||||
|
}
|
||||||
|
// By default, buildinfo is already in UUIDv3 format
|
||||||
|
// (see uuidFromGoBuildId).
|
||||||
|
ml.data[0] = ctxt.Arch.ByteOrder.Uint32(buildinfo)
|
||||||
|
ml.data[1] = ctxt.Arch.ByteOrder.Uint32(buildinfo[4:])
|
||||||
|
ml.data[2] = ctxt.Arch.ByteOrder.Uint32(buildinfo[8:])
|
||||||
|
ml.data[3] = ctxt.Arch.ByteOrder.Uint32(buildinfo[12:])
|
||||||
|
}
|
||||||
|
|
||||||
if ctxt.IsInternal() && ctxt.NeedCodeSign() {
|
if ctxt.IsInternal() && ctxt.NeedCodeSign() {
|
||||||
ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
|
ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
|
||||||
ml.data[0] = uint32(codesigOff)
|
ml.data[0] = uint32(codesigOff)
|
||||||
|
@ -42,7 +42,7 @@ func uuidFromGoBuildId(buildID string) []byte {
|
|||||||
// to use this UUID flavor than any of the others. This is similar
|
// to use this UUID flavor than any of the others. This is similar
|
||||||
// to how other linkers handle this (for example this code in lld:
|
// to how other linkers handle this (for example this code in lld:
|
||||||
// https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524).
|
// https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524).
|
||||||
rv[6] &= 0xcf
|
rv[6] &= 0x0f
|
||||||
rv[6] |= 0x30
|
rv[6] |= 0x30
|
||||||
rv[8] &= 0x3f
|
rv[8] &= 0x3f
|
||||||
rv[8] |= 0xc0
|
rv[8] |= 0xc0
|
||||||
|
@ -95,6 +95,7 @@ var (
|
|||||||
flagN = flag.Bool("n", false, "no-op (deprecated)")
|
flagN = flag.Bool("n", false, "no-op (deprecated)")
|
||||||
FlagS = flag.Bool("s", false, "disable symbol table")
|
FlagS = flag.Bool("s", false, "disable symbol table")
|
||||||
flag8 bool // use 64-bit addresses in symbol table
|
flag8 bool // use 64-bit addresses in symbol table
|
||||||
|
flagHostBuildid = flag.String("B", "", "set ELF NT_GNU_BUILD_ID `note` or Mach-O UUID; use \"gobuildid\" to generate it from the Go build ID")
|
||||||
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
|
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
|
||||||
flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references")
|
flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references")
|
||||||
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
|
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
|
||||||
@ -196,7 +197,6 @@ func Main(arch *sys.Arch, theArch Arch) {
|
|||||||
flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
|
flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
|
||||||
flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
|
flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
|
||||||
flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
|
flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
|
||||||
objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF; use \"gobuildid\" to generate it from the Go build ID", addbuildinfo)
|
|
||||||
objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
|
objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
|
||||||
objabi.AddVersionFlag() // -V
|
objabi.AddVersionFlag() // -V
|
||||||
objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
|
objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
|
||||||
@ -294,6 +294,10 @@ func Main(arch *sys.Arch, theArch Arch) {
|
|||||||
*flagBuildid = "go-openbsd"
|
*flagBuildid = "go-openbsd"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *flagHostBuildid != "" {
|
||||||
|
addbuildinfo(ctxt)
|
||||||
|
}
|
||||||
|
|
||||||
// enable benchmarking
|
// enable benchmarking
|
||||||
var bench *benchmark.Metrics
|
var bench *benchmark.Metrics
|
||||||
if len(*benchmarkFlag) != 0 {
|
if len(*benchmarkFlag) != 0 {
|
||||||
|
@ -257,6 +257,10 @@ func (gs *gState[R]) stop(ts trace.Time, stack trace.Stack, ctx *traceContext) {
|
|||||||
if gs.lastStopStack != trace.NoStack {
|
if gs.lastStopStack != trace.NoStack {
|
||||||
stk = ctx.Stack(viewerFrames(gs.lastStopStack))
|
stk = ctx.Stack(viewerFrames(gs.lastStopStack))
|
||||||
}
|
}
|
||||||
|
var endStk int
|
||||||
|
if stack != trace.NoStack {
|
||||||
|
endStk = ctx.Stack(viewerFrames(stack))
|
||||||
|
}
|
||||||
// Check invariants.
|
// Check invariants.
|
||||||
if gs.startRunningTime == 0 {
|
if gs.startRunningTime == 0 {
|
||||||
panic("silently broken trace or generator invariant (startRunningTime != 0) not held")
|
panic("silently broken trace or generator invariant (startRunningTime != 0) not held")
|
||||||
@ -270,6 +274,7 @@ func (gs *gState[R]) stop(ts trace.Time, stack trace.Stack, ctx *traceContext) {
|
|||||||
Dur: ts.Sub(gs.startRunningTime),
|
Dur: ts.Sub(gs.startRunningTime),
|
||||||
Resource: uint64(gs.executing),
|
Resource: uint64(gs.executing),
|
||||||
Stack: stk,
|
Stack: stk,
|
||||||
|
EndStack: endStk,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Flush completed ranges.
|
// Flush completed ranges.
|
||||||
|
11
src/cmd/vendor/golang.org/x/telemetry/internal/configstore/download.go
generated
vendored
11
src/cmd/vendor/golang.org/x/telemetry/internal/configstore/download.go
generated
vendored
@ -16,6 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"golang.org/x/telemetry/internal/telemetry"
|
"golang.org/x/telemetry/internal/telemetry"
|
||||||
)
|
)
|
||||||
@ -29,12 +30,22 @@ const (
|
|||||||
// creation flag.
|
// creation flag.
|
||||||
var needNoConsole = func(cmd *exec.Cmd) {}
|
var needNoConsole = func(cmd *exec.Cmd) {}
|
||||||
|
|
||||||
|
var downloads int64
|
||||||
|
|
||||||
|
// Downloads reports, for testing purposes, the number of times [Download] has
|
||||||
|
// been called.
|
||||||
|
func Downloads() int64 {
|
||||||
|
return atomic.LoadInt64(&downloads)
|
||||||
|
}
|
||||||
|
|
||||||
// Download fetches the requested telemetry UploadConfig using "go mod
|
// Download fetches the requested telemetry UploadConfig using "go mod
|
||||||
// download". If envOverlay is provided, it is appended to the environment used
|
// download". If envOverlay is provided, it is appended to the environment used
|
||||||
// for invoking the go command.
|
// for invoking the go command.
|
||||||
//
|
//
|
||||||
// The second result is the canonical version of the requested configuration.
|
// The second result is the canonical version of the requested configuration.
|
||||||
func Download(version string, envOverlay []string) (*telemetry.UploadConfig, string, error) {
|
func Download(version string, envOverlay []string) (*telemetry.UploadConfig, string, error) {
|
||||||
|
atomic.AddInt64(&downloads, 1)
|
||||||
|
|
||||||
if version == "" {
|
if version == "" {
|
||||||
version = "latest"
|
version = "latest"
|
||||||
}
|
}
|
||||||
|
4
src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go
generated
vendored
4
src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go
generated
vendored
@ -21,12 +21,12 @@ import (
|
|||||||
"golang.org/x/telemetry/internal/counter"
|
"golang.org/x/telemetry/internal/counter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Supported reports whether the runtime supports [runtime.SetCrashOutput].
|
// Supported reports whether the runtime supports [runtime/debug.SetCrashOutput].
|
||||||
//
|
//
|
||||||
// TODO(adonovan): eliminate once go1.23+ is assured.
|
// TODO(adonovan): eliminate once go1.23+ is assured.
|
||||||
func Supported() bool { return setCrashOutput != nil }
|
func Supported() bool { return setCrashOutput != nil }
|
||||||
|
|
||||||
var setCrashOutput func(*os.File) error // = runtime.SetCrashOutput on go1.23+
|
var setCrashOutput func(*os.File) error // = runtime/debug.SetCrashOutput on go1.23+
|
||||||
|
|
||||||
// Parent sets up the parent side of the crashmonitor. It requires
|
// Parent sets up the parent side of the crashmonitor. It requires
|
||||||
// exclusive use of a writable pipe connected to the child process's stdin.
|
// exclusive use of a writable pipe connected to the child process's stdin.
|
||||||
|
17
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
17
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
@ -112,10 +112,25 @@ func newUploader(rcfg RunConfig) (*uploader, error) {
|
|||||||
logger := log.New(logWriter, "", log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
logger := log.New(logWriter, "", log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||||
|
|
||||||
// Fetch the upload config, if it is not provided.
|
// Fetch the upload config, if it is not provided.
|
||||||
config, configVersion, err := configstore.Download("latest", rcfg.Env)
|
var (
|
||||||
|
config *telemetry.UploadConfig
|
||||||
|
configVersion string
|
||||||
|
)
|
||||||
|
|
||||||
|
if mode, _ := dir.Mode(); mode == "on" {
|
||||||
|
// golang/go#68946: only download the upload config if it will be used.
|
||||||
|
//
|
||||||
|
// TODO(rfindley): This is a narrow change aimed at minimally fixing the
|
||||||
|
// associated bug. In the future, we should read the mode only once during
|
||||||
|
// the upload process.
|
||||||
|
config, configVersion, err = configstore.Download("latest", rcfg.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
config = &telemetry.UploadConfig{}
|
||||||
|
configVersion = "v0.0.0-0"
|
||||||
|
}
|
||||||
|
|
||||||
// Set the start time, if it is not provided.
|
// Set the start time, if it is not provided.
|
||||||
startTime := time.Now().UTC()
|
startTime := time.Now().UTC()
|
||||||
|
19
src/cmd/vendor/golang.org/x/telemetry/start.go
generated
vendored
19
src/cmd/vendor/golang.org/x/telemetry/start.go
generated
vendored
@ -206,7 +206,8 @@ func startChild(reportCrashes, upload bool, result *StartResult) {
|
|||||||
fd, err := os.Stat(telemetry.Default.DebugDir())
|
fd, err := os.Stat(telemetry.Default.DebugDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
log.Fatalf("failed to stat debug directory: %v", err)
|
log.Printf("failed to stat debug directory: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else if fd.IsDir() {
|
} else if fd.IsDir() {
|
||||||
// local/debug exists and is a directory. Set stderr to a log file path
|
// local/debug exists and is a directory. Set stderr to a log file path
|
||||||
@ -214,23 +215,31 @@ func startChild(reportCrashes, upload bool, result *StartResult) {
|
|||||||
childLogPath := filepath.Join(telemetry.Default.DebugDir(), "sidecar.log")
|
childLogPath := filepath.Join(telemetry.Default.DebugDir(), "sidecar.log")
|
||||||
childLog, err := os.OpenFile(childLogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
childLog, err := os.OpenFile(childLogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("opening sidecar log file for child: %v", err)
|
log.Printf("opening sidecar log file for child: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer childLog.Close()
|
defer childLog.Close()
|
||||||
cmd.Stderr = childLog
|
cmd.Stderr = childLog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var crashOutputFile *os.File
|
||||||
if reportCrashes {
|
if reportCrashes {
|
||||||
pipe, err := cmd.StdinPipe()
|
pipe, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("StdinPipe: %v", err)
|
log.Printf("StdinPipe: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
crashmonitor.Parent(pipe.(*os.File)) // (this conversion is safe)
|
crashOutputFile = pipe.(*os.File) // (this conversion is safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
log.Fatalf("can't start telemetry child process: %v", err)
|
// The child couldn't be started. Log the failure.
|
||||||
|
log.Printf("can't start telemetry child process: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reportCrashes {
|
||||||
|
crashmonitor.Parent(crashOutputFile)
|
||||||
}
|
}
|
||||||
result.wg.Add(1)
|
result.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@ -45,7 +45,7 @@ golang.org/x/sync/semaphore
|
|||||||
golang.org/x/sys/plan9
|
golang.org/x/sys/plan9
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
# golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701
|
# golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147
|
||||||
## explicit; go 1.20
|
## explicit; go 1.20
|
||||||
golang.org/x/telemetry
|
golang.org/x/telemetry
|
||||||
golang.org/x/telemetry/counter
|
golang.org/x/telemetry/counter
|
||||||
|
@ -852,6 +852,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
MaxVersion: version,
|
MaxVersion: version,
|
||||||
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
||||||
Certificates: testConfig.Certificates,
|
Certificates: testConfig.Certificates,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
|
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
|
||||||
@ -868,6 +869,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
ClientSessionCache: NewLRUClientSessionCache(32),
|
ClientSessionCache: NewLRUClientSessionCache(32),
|
||||||
RootCAs: rootCAs,
|
RootCAs: rootCAs,
|
||||||
ServerName: "example.golang",
|
ServerName: "example.golang",
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
testResumeState := func(test string, didResume bool) {
|
testResumeState := func(test string, didResume bool) {
|
||||||
@ -914,7 +916,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
|
|
||||||
// An old session ticket is replaced with a ticket encrypted with a fresh key.
|
// An old session ticket is replaced with a ticket encrypted with a fresh key.
|
||||||
ticket = getTicket()
|
ticket = getTicket()
|
||||||
serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) }
|
serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) }
|
||||||
testResumeState("ResumeWithOldTicket", true)
|
testResumeState("ResumeWithOldTicket", true)
|
||||||
if bytes.Equal(ticket, getTicket()) {
|
if bytes.Equal(ticket, getTicket()) {
|
||||||
t.Fatal("old first ticket matches the fresh one")
|
t.Fatal("old first ticket matches the fresh one")
|
||||||
@ -922,13 +924,13 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
|
|
||||||
// Once the session master secret is expired, a full handshake should occur.
|
// Once the session master secret is expired, a full handshake should occur.
|
||||||
ticket = getTicket()
|
ticket = getTicket()
|
||||||
serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) }
|
serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + time.Minute) }
|
||||||
testResumeState("ResumeWithExpiredTicket", false)
|
testResumeState("ResumeWithExpiredTicket", false)
|
||||||
if bytes.Equal(ticket, getTicket()) {
|
if bytes.Equal(ticket, getTicket()) {
|
||||||
t.Fatal("expired first ticket matches the fresh one")
|
t.Fatal("expired first ticket matches the fresh one")
|
||||||
}
|
}
|
||||||
|
|
||||||
serverConfig.Time = func() time.Time { return time.Now() } // reset the time back
|
serverConfig.Time = testTime // reset the time back
|
||||||
key1 := randomKey()
|
key1 := randomKey()
|
||||||
serverConfig.SetSessionTicketKeys([][32]byte{key1})
|
serverConfig.SetSessionTicketKeys([][32]byte{key1})
|
||||||
|
|
||||||
@ -945,11 +947,11 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
testResumeState("KeyChangeFinish", true)
|
testResumeState("KeyChangeFinish", true)
|
||||||
|
|
||||||
// Age the session ticket a bit, but not yet expired.
|
// Age the session ticket a bit, but not yet expired.
|
||||||
serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) }
|
serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) }
|
||||||
testResumeState("OldSessionTicket", true)
|
testResumeState("OldSessionTicket", true)
|
||||||
ticket = getTicket()
|
ticket = getTicket()
|
||||||
// Expire the session ticket, which would force a full handshake.
|
// Expire the session ticket, which would force a full handshake.
|
||||||
serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) }
|
serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + 2*time.Minute) }
|
||||||
testResumeState("ExpiredSessionTicket", false)
|
testResumeState("ExpiredSessionTicket", false)
|
||||||
if bytes.Equal(ticket, getTicket()) {
|
if bytes.Equal(ticket, getTicket()) {
|
||||||
t.Fatal("new ticket wasn't provided after old ticket expired")
|
t.Fatal("new ticket wasn't provided after old ticket expired")
|
||||||
@ -957,7 +959,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
|
|
||||||
// Age the session ticket a bit at a time, but don't expire it.
|
// Age the session ticket a bit at a time, but don't expire it.
|
||||||
d := 0 * time.Hour
|
d := 0 * time.Hour
|
||||||
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
|
serverConfig.Time = func() time.Time { return testTime().Add(d) }
|
||||||
deleteTicket()
|
deleteTicket()
|
||||||
testResumeState("GetFreshSessionTicket", false)
|
testResumeState("GetFreshSessionTicket", false)
|
||||||
for i := 0; i < 13; i++ {
|
for i := 0; i < 13; i++ {
|
||||||
@ -968,7 +970,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
// handshake occurs for TLS 1.2. Resumption should still occur for
|
// handshake occurs for TLS 1.2. Resumption should still occur for
|
||||||
// TLS 1.3 since the client should be using a fresh ticket sent over
|
// TLS 1.3 since the client should be using a fresh ticket sent over
|
||||||
// by the server.
|
// by the server.
|
||||||
d += 12 * time.Hour
|
d += 12*time.Hour + time.Minute
|
||||||
if version == VersionTLS13 {
|
if version == VersionTLS13 {
|
||||||
testResumeState("ExpiredSessionTicket", true)
|
testResumeState("ExpiredSessionTicket", true)
|
||||||
} else {
|
} else {
|
||||||
@ -984,6 +986,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
MaxVersion: version,
|
MaxVersion: version,
|
||||||
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
||||||
Certificates: testConfig.Certificates,
|
Certificates: testConfig.Certificates,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
serverConfig.SetSessionTicketKeys([][32]byte{key2})
|
serverConfig.SetSessionTicketKeys([][32]byte{key2})
|
||||||
|
|
||||||
@ -1009,6 +1012,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
CurvePreferences: []CurveID{CurveP521, CurveP384, CurveP256},
|
CurvePreferences: []CurveID{CurveP521, CurveP384, CurveP256},
|
||||||
MaxVersion: version,
|
MaxVersion: version,
|
||||||
Certificates: testConfig.Certificates,
|
Certificates: testConfig.Certificates,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
testResumeState("InitialHandshake", false)
|
testResumeState("InitialHandshake", false)
|
||||||
testResumeState("WithHelloRetryRequest", true)
|
testResumeState("WithHelloRetryRequest", true)
|
||||||
@ -1018,6 +1022,7 @@ func testResumption(t *testing.T, version uint16) {
|
|||||||
MaxVersion: version,
|
MaxVersion: version,
|
||||||
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
|
||||||
Certificates: testConfig.Certificates,
|
Certificates: testConfig.Certificates,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1736,6 +1741,7 @@ func testVerifyConnection(t *testing.T, version uint16) {
|
|||||||
serverConfig := &Config{
|
serverConfig := &Config{
|
||||||
MaxVersion: version,
|
MaxVersion: version,
|
||||||
Certificates: []Certificate{testConfig.Certificates[0]},
|
Certificates: []Certificate{testConfig.Certificates[0]},
|
||||||
|
Time: testTime,
|
||||||
ClientCAs: rootCAs,
|
ClientCAs: rootCAs,
|
||||||
NextProtos: []string{"protocol1"},
|
NextProtos: []string{"protocol1"},
|
||||||
}
|
}
|
||||||
@ -1749,6 +1755,7 @@ func testVerifyConnection(t *testing.T, version uint16) {
|
|||||||
RootCAs: rootCAs,
|
RootCAs: rootCAs,
|
||||||
ServerName: "example.golang",
|
ServerName: "example.golang",
|
||||||
Certificates: []Certificate{testConfig.Certificates[0]},
|
Certificates: []Certificate{testConfig.Certificates[0]},
|
||||||
|
Time: testTime,
|
||||||
NextProtos: []string{"protocol1"},
|
NextProtos: []string{"protocol1"},
|
||||||
}
|
}
|
||||||
test.configureClient(clientConfig, &clientCalled)
|
test.configureClient(clientConfig, &clientCalled)
|
||||||
@ -1791,8 +1798,6 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) {
|
|||||||
rootCAs := x509.NewCertPool()
|
rootCAs := x509.NewCertPool()
|
||||||
rootCAs.AddCert(issuer)
|
rootCAs.AddCert(issuer)
|
||||||
|
|
||||||
now := func() time.Time { return time.Unix(1476984729, 0) }
|
|
||||||
|
|
||||||
sentinelErr := errors.New("TestVerifyPeerCertificate")
|
sentinelErr := errors.New("TestVerifyPeerCertificate")
|
||||||
|
|
||||||
verifyPeerCertificateCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
|
verifyPeerCertificateCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
|
||||||
@ -2038,7 +2043,7 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) {
|
|||||||
config.ServerName = "example.golang"
|
config.ServerName = "example.golang"
|
||||||
config.ClientAuth = RequireAndVerifyClientCert
|
config.ClientAuth = RequireAndVerifyClientCert
|
||||||
config.ClientCAs = rootCAs
|
config.ClientCAs = rootCAs
|
||||||
config.Time = now
|
config.Time = testTime
|
||||||
config.MaxVersion = version
|
config.MaxVersion = version
|
||||||
config.Certificates = make([]Certificate, 1)
|
config.Certificates = make([]Certificate, 1)
|
||||||
config.Certificates[0].Certificate = [][]byte{testRSACertificate}
|
config.Certificates[0].Certificate = [][]byte{testRSACertificate}
|
||||||
@ -2055,7 +2060,7 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) {
|
|||||||
config := testConfig.Clone()
|
config := testConfig.Clone()
|
||||||
config.ServerName = "example.golang"
|
config.ServerName = "example.golang"
|
||||||
config.RootCAs = rootCAs
|
config.RootCAs = rootCAs
|
||||||
config.Time = now
|
config.Time = testTime
|
||||||
config.MaxVersion = version
|
config.MaxVersion = version
|
||||||
test.configureClient(config, &clientCalled)
|
test.configureClient(config, &clientCalled)
|
||||||
clientErr := Client(c, config).Handshake()
|
clientErr := Client(c, config).Handshake()
|
||||||
@ -2368,7 +2373,7 @@ func testGetClientCertificate(t *testing.T, version uint16) {
|
|||||||
serverConfig.RootCAs = x509.NewCertPool()
|
serverConfig.RootCAs = x509.NewCertPool()
|
||||||
serverConfig.RootCAs.AddCert(issuer)
|
serverConfig.RootCAs.AddCert(issuer)
|
||||||
serverConfig.ClientCAs = serverConfig.RootCAs
|
serverConfig.ClientCAs = serverConfig.RootCAs
|
||||||
serverConfig.Time = func() time.Time { return time.Unix(1476984729, 0) }
|
serverConfig.Time = testTime
|
||||||
serverConfig.MaxVersion = version
|
serverConfig.MaxVersion = version
|
||||||
|
|
||||||
clientConfig := testConfig.Clone()
|
clientConfig := testConfig.Clone()
|
||||||
@ -2539,6 +2544,7 @@ func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) {
|
|||||||
ClientSessionCache: NewLRUClientSessionCache(32),
|
ClientSessionCache: NewLRUClientSessionCache(32),
|
||||||
ServerName: "example.golang",
|
ServerName: "example.golang",
|
||||||
RootCAs: roots,
|
RootCAs: roots,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
serverConfig := testConfig.Clone()
|
serverConfig := testConfig.Clone()
|
||||||
serverConfig.MaxVersion = ver
|
serverConfig.MaxVersion = ver
|
||||||
|
@ -501,6 +501,7 @@ func testCrossVersionResume(t *testing.T, version uint16) {
|
|||||||
serverConfig := &Config{
|
serverConfig := &Config{
|
||||||
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
|
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
|
||||||
Certificates: testConfig.Certificates,
|
Certificates: testConfig.Certificates,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
clientConfig := &Config{
|
clientConfig := &Config{
|
||||||
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
|
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
|
||||||
@ -508,6 +509,7 @@ func testCrossVersionResume(t *testing.T, version uint16) {
|
|||||||
ClientSessionCache: NewLRUClientSessionCache(1),
|
ClientSessionCache: NewLRUClientSessionCache(1),
|
||||||
ServerName: "servername",
|
ServerName: "servername",
|
||||||
MinVersion: VersionTLS12,
|
MinVersion: VersionTLS12,
|
||||||
|
Time: testTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establish a session at TLS 1.3.
|
// Establish a session at TLS 1.3.
|
||||||
|
@ -491,9 +491,10 @@ func testHandshake(t *testing.T, clientConfig, serverConfig *Config) (serverStat
|
|||||||
if got := string(buf); got != sentinel {
|
if got := string(buf); got != sentinel {
|
||||||
t.Errorf("read %q from TLS connection, but expected %q", got, sentinel)
|
t.Errorf("read %q from TLS connection, but expected %q", got, sentinel)
|
||||||
}
|
}
|
||||||
if err := cli.Close(); err != nil {
|
// We discard the error because after ReadAll returns the server must
|
||||||
t.Errorf("failed to call cli.Close: %v", err)
|
// have already closed the connection. Sending data (the closeNotify
|
||||||
}
|
// alert) can cause a reset, that will make Close return an error.
|
||||||
|
cli.Close()
|
||||||
}()
|
}()
|
||||||
server := Server(s, serverConfig)
|
server := Server(s, serverConfig)
|
||||||
err = server.Handshake()
|
err = server.Handshake()
|
||||||
@ -518,6 +519,11 @@ func fromHex(s string) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testTime is 2016-10-20T17:32:09.000Z, which is within the validity period of
|
||||||
|
// [testRSACertificate], [testRSACertificateIssuer], [testRSA2048Certificate],
|
||||||
|
// [testRSA2048CertificateIssuer], and [testECDSACertificate].
|
||||||
|
var testTime = func() time.Time { return time.Unix(1476984729, 0) }
|
||||||
|
|
||||||
var testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234f1d193e596d9747bf3589f6c613cc0b041d4d92b2b2423775b1c3bbd755dce2054cfa163871d1e24c4f31d1a508baab61443ed97a77562f414c852d70203010001a38193308190300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d0e041204109f91161f43433e49a6de6db680d79f60301b0603551d230414301280104813494d137e1631bba301d5acab6e7b30190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b0500038181009d30cc402b5b50a061cbbae55358e1ed8328a9581aa938a495a1ac315a1a84663d43d32dd90bf297dfd320643892243a00bccf9c7db74020015faad3166109a276fd13c3cce10c5ceeb18782f16c04ed73bbb343778d0c1cf10fa1d8408361c94c722b9daedb4606064df4c1b33ec0d1bd42d4dbfe3d1360845c21d33be9fae7")
|
var testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234f1d193e596d9747bf3589f6c613cc0b041d4d92b2b2423775b1c3bbd755dce2054cfa163871d1e24c4f31d1a508baab61443ed97a77562f414c852d70203010001a38193308190300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d0e041204109f91161f43433e49a6de6db680d79f60301b0603551d230414301280104813494d137e1631bba301d5acab6e7b30190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b0500038181009d30cc402b5b50a061cbbae55358e1ed8328a9581aa938a495a1ac315a1a84663d43d32dd90bf297dfd320643892243a00bccf9c7db74020015faad3166109a276fd13c3cce10c5ceeb18782f16c04ed73bbb343778d0c1cf10fa1d8408361c94c722b9daedb4606064df4c1b33ec0d1bd42d4dbfe3d1360845c21d33be9fae7")
|
||||||
|
|
||||||
var testRSACertificateIssuer = fromHex("3082021930820182a003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100d667b378bb22f34143b6cd2008236abefaf2852adf3ab05e01329e2c14834f5105df3f3073f99dab5442d45ee5f8f57b0111c8cb682fbb719a86944eebfffef3406206d898b8c1b1887797c9c5006547bb8f00e694b7a063f10839f269f2c34fff7a1f4b21fbcd6bfdfb13ac792d1d11f277b5c5b48600992203059f2a8f8cc50203010001a35d305b300e0603551d0f0101ff040403020204301d0603551d250416301406082b0601050507030106082b06010505070302300f0603551d130101ff040530030101ff30190603551d0e041204104813494d137e1631bba301d5acab6e7b300d06092a864886f70d01010b050003818100c1154b4bab5266221f293766ae4138899bd4c5e36b13cee670ceeaa4cbdf4f6679017e2fe649765af545749fe4249418a56bd38a04b81e261f5ce86b8d5c65413156a50d12449554748c59a30c515bc36a59d38bddf51173e899820b282e40aa78c806526fd184fb6b4cf186ec728edffa585440d2b3225325f7ab580e87dd76")
|
var testRSACertificateIssuer = fromHex("3082021930820182a003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100d667b378bb22f34143b6cd2008236abefaf2852adf3ab05e01329e2c14834f5105df3f3073f99dab5442d45ee5f8f57b0111c8cb682fbb719a86944eebfffef3406206d898b8c1b1887797c9c5006547bb8f00e694b7a063f10839f269f2c34fff7a1f4b21fbcd6bfdfb13ac792d1d11f277b5c5b48600992203059f2a8f8cc50203010001a35d305b300e0603551d0f0101ff040403020204301d0603551d250416301406082b0601050507030106082b06010505070302300f0603551d130101ff040530030101ff30190603551d0e041204104813494d137e1631bba301d5acab6e7b300d06092a864886f70d01010b050003818100c1154b4bab5266221f293766ae4138899bd4c5e36b13cee670ceeaa4cbdf4f6679017e2fe649765af545749fe4249418a56bd38a04b81e261f5ce86b8d5c65413156a50d12449554748c59a30c515bc36a59d38bddf51173e899820b282e40aa78c806526fd184fb6b4cf186ec728edffa585440d2b3225325f7ab580e87dd76")
|
||||||
|
@ -1112,8 +1112,6 @@ func TestConnectionState(t *testing.T) {
|
|||||||
rootCAs := x509.NewCertPool()
|
rootCAs := x509.NewCertPool()
|
||||||
rootCAs.AddCert(issuer)
|
rootCAs.AddCert(issuer)
|
||||||
|
|
||||||
now := func() time.Time { return time.Unix(1476984729, 0) }
|
|
||||||
|
|
||||||
const alpnProtocol = "golang"
|
const alpnProtocol = "golang"
|
||||||
const serverName = "example.golang"
|
const serverName = "example.golang"
|
||||||
var scts = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")}
|
var scts = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")}
|
||||||
@ -1129,7 +1127,7 @@ func TestConnectionState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Time: now,
|
Time: testTime,
|
||||||
Rand: zeroSource{},
|
Rand: zeroSource{},
|
||||||
Certificates: make([]Certificate, 1),
|
Certificates: make([]Certificate, 1),
|
||||||
MaxVersion: v,
|
MaxVersion: v,
|
||||||
@ -1760,7 +1758,7 @@ func testVerifyCertificates(t *testing.T, version uint16) {
|
|||||||
var serverVerifyPeerCertificates, clientVerifyPeerCertificates bool
|
var serverVerifyPeerCertificates, clientVerifyPeerCertificates bool
|
||||||
|
|
||||||
clientConfig := testConfig.Clone()
|
clientConfig := testConfig.Clone()
|
||||||
clientConfig.Time = func() time.Time { return time.Unix(1476984729, 0) }
|
clientConfig.Time = testTime
|
||||||
clientConfig.MaxVersion = version
|
clientConfig.MaxVersion = version
|
||||||
clientConfig.MinVersion = version
|
clientConfig.MinVersion = version
|
||||||
clientConfig.RootCAs = rootCAs
|
clientConfig.RootCAs = rootCAs
|
||||||
|
@ -1607,6 +1607,23 @@ var nameConstraintsTests = []nameConstraintsTest{
|
|||||||
leaf: leafSpec{sans: []string{"dns:.example.com"}},
|
leaf: leafSpec{sans: []string{"dns:.example.com"}},
|
||||||
expectedError: "cannot parse dnsName \".example.com\"",
|
expectedError: "cannot parse dnsName \".example.com\"",
|
||||||
},
|
},
|
||||||
|
// #86: URIs with IPv6 addresses with zones and ports are rejected
|
||||||
|
{
|
||||||
|
roots: []constraintsSpec{
|
||||||
|
{
|
||||||
|
ok: []string{"uri:example.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
intermediates: [][]constraintsSpec{
|
||||||
|
{
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
leaf: leafSpec{
|
||||||
|
sans: []string{"uri:http://[2006:abcd::1%25.example.com]:16/"},
|
||||||
|
},
|
||||||
|
expectedError: "URI with IP",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) {
|
func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -434,8 +435,10 @@ func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") ||
|
// netip.ParseAddr will reject the URI IPv6 literal form "[...]", so we
|
||||||
net.ParseIP(host) != nil {
|
// check if _either_ the string parses as an IP, or if it is enclosed in
|
||||||
|
// square brackets.
|
||||||
|
if _, err := netip.ParseAddr(host); err == nil || (strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]")) {
|
||||||
return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
|
return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1368,8 +1368,8 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
|
|||||||
|
|
||||||
db.waitDuration.Add(int64(time.Since(waitStart)))
|
db.waitDuration.Add(int64(time.Since(waitStart)))
|
||||||
|
|
||||||
// If we failed to delete it, that means something else
|
// If we failed to delete it, that means either the DB was closed or
|
||||||
// grabbed it and is about to send on it.
|
// something else grabbed it and is about to send on it.
|
||||||
if !deleted {
|
if !deleted {
|
||||||
// TODO(bradfitz): rather than this best effort select, we
|
// TODO(bradfitz): rather than this best effort select, we
|
||||||
// should probably start a goroutine to read from req. This best
|
// should probably start a goroutine to read from req. This best
|
||||||
@ -3594,6 +3594,7 @@ type connRequestAndIndex struct {
|
|||||||
// and clears the set.
|
// and clears the set.
|
||||||
func (s *connRequestSet) CloseAndRemoveAll() {
|
func (s *connRequestSet) CloseAndRemoveAll() {
|
||||||
for _, v := range s.s {
|
for _, v := range s.s {
|
||||||
|
*v.curIdx = -1
|
||||||
close(v.req)
|
close(v.req)
|
||||||
}
|
}
|
||||||
s.s = nil
|
s.s = nil
|
||||||
|
@ -4920,6 +4920,17 @@ func TestConnRequestSet(t *testing.T) {
|
|||||||
t.Error("wasn't random")
|
t.Error("wasn't random")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.Run("close-delete", func(t *testing.T) {
|
||||||
|
reset()
|
||||||
|
ch := make(chan connRequest)
|
||||||
|
dh := s.Add(ch)
|
||||||
|
wantLen(1)
|
||||||
|
s.CloseAndRemoveAll()
|
||||||
|
wantLen(0)
|
||||||
|
if s.Delete(dh) {
|
||||||
|
t.Error("unexpected delete after CloseAndRemoveAll")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkConnRequestSet(b *testing.B) {
|
func BenchmarkConnRequestSet(b *testing.B) {
|
||||||
|
@ -911,8 +911,11 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
|
|||||||
var maxIgnoreNestingDepth = 10000
|
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, depth int) *decOp {
|
func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) *decOp {
|
||||||
if depth > maxIgnoreNestingDepth {
|
// Track how deep we've recursed trying to skip nested ignored fields.
|
||||||
|
dec.ignoreDepth++
|
||||||
|
defer func() { dec.ignoreDepth-- }()
|
||||||
|
if dec.ignoreDepth > maxIgnoreNestingDepth {
|
||||||
error_(errors.New("invalid nesting depth"))
|
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).
|
||||||
@ -938,7 +941,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, depth+1)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -946,15 +949,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, depth+1)
|
keyOp := dec.decIgnoreOpFor(keyId, inProgress)
|
||||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||||
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, depth+1)
|
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -1115,7 +1118,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), 0)
|
op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp))
|
||||||
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
|
||||||
@ -1160,7 +1163,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), 0)
|
op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp))
|
||||||
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
|
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ type Decoder struct {
|
|||||||
freeList *decoderState // list of free decoderStates; avoids reallocation
|
freeList *decoderState // list of free decoderStates; avoids reallocation
|
||||||
countBuf []byte // used for decoding integers while parsing messages
|
countBuf []byte // used for decoding integers while parsing messages
|
||||||
err error
|
err error
|
||||||
|
// ignoreDepth tracks the depth of recursively parsed ignored fields
|
||||||
|
ignoreDepth int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDecoder returns a new decoder that reads from the [io.Reader].
|
// NewDecoder returns a new decoder that reads from the [io.Reader].
|
||||||
|
@ -806,6 +806,8 @@ func TestIgnoreDepthLimit(t *testing.T) {
|
|||||||
defer func() { maxIgnoreNestingDepth = oldNestingDepth }()
|
defer func() { maxIgnoreNestingDepth = oldNestingDepth }()
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
enc := NewEncoder(b)
|
enc := NewEncoder(b)
|
||||||
|
|
||||||
|
// Nested slice
|
||||||
typ := reflect.TypeFor[int]()
|
typ := reflect.TypeFor[int]()
|
||||||
nested := reflect.ArrayOf(1, typ)
|
nested := reflect.ArrayOf(1, typ)
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
@ -819,4 +821,16 @@ func TestIgnoreDepthLimit(t *testing.T) {
|
|||||||
if err := dec.Decode(&output); err == nil || err.Error() != expectedErr {
|
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)
|
t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nested struct
|
||||||
|
nested = reflect.StructOf([]reflect.StructField{{Name: "F", Type: typ}})
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
nested = reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}})
|
||||||
|
}
|
||||||
|
badStruct = reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}}))
|
||||||
|
enc.Encode(badStruct.Interface())
|
||||||
|
dec = NewDecoder(b)
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maxSize is a limit used to control the complexity of expressions, in order
|
||||||
|
// to prevent stack exhaustion issues due to recursion.
|
||||||
|
const maxSize = 1000
|
||||||
|
|
||||||
// An Expr is a build tag constraint expression.
|
// An Expr is a build tag constraint expression.
|
||||||
// The underlying concrete type is *[AndExpr], *[OrExpr], *[NotExpr], or *[TagExpr].
|
// The underlying concrete type is *[AndExpr], *[OrExpr], *[NotExpr], or *[TagExpr].
|
||||||
type Expr interface {
|
type Expr interface {
|
||||||
@ -151,7 +155,7 @@ func Parse(line string) (Expr, error) {
|
|||||||
return parseExpr(text)
|
return parseExpr(text)
|
||||||
}
|
}
|
||||||
if text, ok := splitPlusBuild(line); ok {
|
if text, ok := splitPlusBuild(line); ok {
|
||||||
return parsePlusBuildExpr(text), nil
|
return parsePlusBuildExpr(text)
|
||||||
}
|
}
|
||||||
return nil, errNotConstraint
|
return nil, errNotConstraint
|
||||||
}
|
}
|
||||||
@ -201,6 +205,8 @@ type exprParser struct {
|
|||||||
tok string // last token read
|
tok string // last token read
|
||||||
isTag bool
|
isTag bool
|
||||||
pos int // position (start) of last token
|
pos int // position (start) of last token
|
||||||
|
|
||||||
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseExpr parses a boolean build tag expression.
|
// parseExpr parses a boolean build tag expression.
|
||||||
@ -249,6 +255,10 @@ func (p *exprParser) and() Expr {
|
|||||||
// On entry, the next input token has not yet been lexed.
|
// On entry, the next input token has not yet been lexed.
|
||||||
// On exit, the next input token has been lexed and is in p.tok.
|
// On exit, the next input token has been lexed and is in p.tok.
|
||||||
func (p *exprParser) not() Expr {
|
func (p *exprParser) not() Expr {
|
||||||
|
p.size++
|
||||||
|
if p.size > maxSize {
|
||||||
|
panic(&SyntaxError{Offset: p.pos, Err: "build expression too large"})
|
||||||
|
}
|
||||||
p.lex()
|
p.lex()
|
||||||
if p.tok == "!" {
|
if p.tok == "!" {
|
||||||
p.lex()
|
p.lex()
|
||||||
@ -388,7 +398,13 @@ func splitPlusBuild(line string) (expr string, ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parsePlusBuildExpr parses a legacy build tag expression (as used with “// +build”).
|
// parsePlusBuildExpr parses a legacy build tag expression (as used with “// +build”).
|
||||||
func parsePlusBuildExpr(text string) Expr {
|
func parsePlusBuildExpr(text string) (Expr, error) {
|
||||||
|
// Only allow up to 100 AND/OR operators for "old" syntax.
|
||||||
|
// This is much less than the limit for "new" syntax,
|
||||||
|
// but uses of old syntax were always very simple.
|
||||||
|
const maxOldSize = 100
|
||||||
|
size := 0
|
||||||
|
|
||||||
var x Expr
|
var x Expr
|
||||||
for _, clause := range strings.Fields(text) {
|
for _, clause := range strings.Fields(text) {
|
||||||
var y Expr
|
var y Expr
|
||||||
@ -414,19 +430,25 @@ func parsePlusBuildExpr(text string) Expr {
|
|||||||
if y == nil {
|
if y == nil {
|
||||||
y = z
|
y = z
|
||||||
} else {
|
} else {
|
||||||
|
if size++; size > maxOldSize {
|
||||||
|
return nil, errComplex
|
||||||
|
}
|
||||||
y = and(y, z)
|
y = and(y, z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if x == nil {
|
if x == nil {
|
||||||
x = y
|
x = y
|
||||||
} else {
|
} else {
|
||||||
|
if size++; size > maxOldSize {
|
||||||
|
return nil, errComplex
|
||||||
|
}
|
||||||
x = or(x, y)
|
x = or(x, y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if x == nil {
|
if x == nil {
|
||||||
x = tag("ignore")
|
x = tag("ignore")
|
||||||
}
|
}
|
||||||
return x
|
return x, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isValidTag reports whether the word is a valid build tag.
|
// isValidTag reports whether the word is a valid build tag.
|
||||||
|
@ -222,7 +222,7 @@ var parsePlusBuildExprTests = []struct {
|
|||||||
func TestParsePlusBuildExpr(t *testing.T) {
|
func TestParsePlusBuildExpr(t *testing.T) {
|
||||||
for i, tt := range parsePlusBuildExprTests {
|
for i, tt := range parsePlusBuildExprTests {
|
||||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
x := parsePlusBuildExpr(tt.in)
|
x, _ := parsePlusBuildExpr(tt.in)
|
||||||
if x.String() != tt.x.String() {
|
if x.String() != tt.x.String() {
|
||||||
t.Errorf("parsePlusBuildExpr(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
|
t.Errorf("parsePlusBuildExpr(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
|
||||||
}
|
}
|
||||||
@ -319,3 +319,66 @@ func TestPlusBuildLines(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSizeLimits(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
expr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "go:build or limit",
|
||||||
|
expr: "//go:build " + strings.Repeat("a || ", maxSize+2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go:build and limit",
|
||||||
|
expr: "//go:build " + strings.Repeat("a && ", maxSize+2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go:build and depth limit",
|
||||||
|
expr: "//go:build " + strings.Repeat("(a &&", maxSize+2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go:build or depth limit",
|
||||||
|
expr: "//go:build " + strings.Repeat("(a ||", maxSize+2),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, err := Parse(tc.expr)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expression did not trigger limit")
|
||||||
|
} else if syntaxErr, ok := err.(*SyntaxError); !ok || syntaxErr.Err != "build expression too large" {
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("unexpected syntax error: %s", syntaxErr.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlusSizeLimits(t *testing.T) {
|
||||||
|
maxOldSize := 100
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
expr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "+build or limit",
|
||||||
|
expr: "// +build " + strings.Repeat("a ", maxOldSize+2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "+build and limit",
|
||||||
|
expr: "// +build " + strings.Repeat("a,", maxOldSize+2),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, err := Parse(tc.expr)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expression did not trigger limit")
|
||||||
|
} else if err != errComplex {
|
||||||
|
t.Errorf("unexpected error: got %q, want %q", err, errComplex)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1676,6 +1676,8 @@ func (p *parser) parseElementList() (list []ast.Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
|
func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
|
||||||
|
defer decNestLev(incNestLev(p))
|
||||||
|
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "LiteralValue"))
|
defer un(trace(p, "LiteralValue"))
|
||||||
}
|
}
|
||||||
|
@ -598,10 +598,11 @@ var parseDepthTests = []struct {
|
|||||||
{name: "chan2", 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: "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: "map", format: "package main; var x «map[int]»int"},
|
||||||
{name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
{name: "slicelit", format: "package main; var x = []any{«[]any{«»}»}", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||||
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr
|
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 3}, // Parser nodes: CompositeLit, KeyValueExpr
|
||||||
|
{name: "element", format: "package main; var x = struct{x any}{x: «{«»}»}"},
|
||||||
{name: "dot", format: "package main; var x = «x.»x"},
|
{name: "dot", format: "package main; var x = «x.»x"},
|
||||||
{name: "index", format: "package main; var x = x«[1]»"},
|
{name: "index", format: "package main; var x = x«[1]»"},
|
||||||
{name: "slice", format: "package main; var x = x«[1:2]»"},
|
{name: "slice", format: "package main; var x = x«[1:2]»"},
|
||||||
|
@ -137,10 +137,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
|||||||
// newAliasInstance creates a new alias instance for the given origin and type
|
// newAliasInstance creates a new alias instance for the given origin and type
|
||||||
// arguments, recording pos as the position of its synthetic object (for error
|
// arguments, recording pos as the position of its synthetic object (for error
|
||||||
// reporting).
|
// reporting).
|
||||||
func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, expanding *Named, ctxt *Context) *Alias {
|
||||||
assert(len(targs) > 0)
|
assert(len(targs) > 0)
|
||||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
|
||||||
res := check.newAlias(obj, rhs)
|
res := check.newAlias(obj, rhs)
|
||||||
res.orig = orig
|
res.orig = orig
|
||||||
res.tparams = orig.tparams
|
res.tparams = orig.tparams
|
||||||
|
@ -2906,20 +2906,46 @@ func TestFileVersions(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"", "", ""}, // no versions specified
|
{"", "", ""}, // no versions specified
|
||||||
{"go1.19", "", "go1.19"}, // module version specified
|
{"go1.19", "", "go1.19"}, // module version specified
|
||||||
{"", "go1.20", ""}, // file upgrade ignored
|
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
|
{"go1", "", "go1"}, // no file version specified
|
||||||
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
|
{"go1", "goo1.22", "go1"}, // invalid file version specified
|
||||||
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.19", "", "go1.19"}, // no file version specified
|
||||||
|
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
|
||||||
|
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.20", "", "go1.20"}, // no file version specified
|
||||||
|
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
|
||||||
|
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.21", "", "go1.21"}, // no file version specified
|
||||||
|
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
|
||||||
|
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
{"go1.22", "", "go1.22"}, // no file version specified
|
||||||
|
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
|
||||||
|
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||||
|
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||||
|
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||||
|
|
||||||
// versions containing release numbers
|
// versions containing release numbers
|
||||||
// (file versions containing release numbers are considered invalid)
|
// (file versions containing release numbers are considered invalid)
|
||||||
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
||||||
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
|
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
|
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
|
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
|
||||||
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
|
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
|
||||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
|
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
|
||||||
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
|
||||||
} {
|
} {
|
||||||
var src string
|
var src string
|
||||||
if test.fileVersion != "" {
|
if test.fileVersion != "" {
|
||||||
|
@ -349,7 +349,6 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||||||
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
||||||
check.version, go_current)
|
check.version, go_current)
|
||||||
}
|
}
|
||||||
downgradeOk := check.version.cmp(go1_21) >= 0
|
|
||||||
|
|
||||||
// determine Go version for each file
|
// determine Go version for each file
|
||||||
for _, file := range check.files {
|
for _, file := range check.files {
|
||||||
@ -358,33 +357,19 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||||||
// unlike file versions which are Go language versions only, if valid.)
|
// unlike file versions which are Go language versions only, if valid.)
|
||||||
v := check.conf.GoVersion
|
v := check.conf.GoVersion
|
||||||
|
|
||||||
fileVersion := asGoVersion(file.GoVersion)
|
// If the file specifies a version, use max(fileVersion, go1.21).
|
||||||
if fileVersion.isValid() {
|
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
|
||||||
// use the file version, if applicable
|
|
||||||
// (file versions are either the empty string or of the form go1.dd)
|
|
||||||
if pkgVersionOk {
|
|
||||||
cmp := fileVersion.cmp(check.version)
|
|
||||||
// Go 1.21 introduced the feature of setting the go.mod
|
// Go 1.21 introduced the feature of setting the go.mod
|
||||||
// go line to an early version of Go and allowing //go:build lines
|
// go line to an early version of Go and allowing //go:build lines
|
||||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
// to set the Go version in a given file. Versions Go 1.21 and later
|
||||||
// We can do that backwards compatibly.
|
// can be set backwards compatibly as that was the first version
|
||||||
|
// files with go1.21 or later build tags could be built with.
|
||||||
//
|
//
|
||||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
// downgrade to a version before go1.22, where the for loop semantics
|
||||||
// That can't be done compatibly in general, since before the
|
// change was made, while being backwards compatible with versions of
|
||||||
// build lines were ignored and code got the module's Go version.
|
// go before the new //go:build semantics were introduced.
|
||||||
// To work around this, downgrades are only allowed when the
|
v = string(versionMax(fileVersion, go1_21))
|
||||||
// module's Go version is Go 1.21 or later.
|
|
||||||
//
|
|
||||||
// If there is no valid check.version, then we don't really know what
|
|
||||||
// Go version to apply.
|
|
||||||
// Legacy tools may do this, and they historically have accepted everything.
|
|
||||||
// Preserve that behavior by ignoring //go:build constraints entirely in that
|
|
||||||
// case (!pkgVersionOk).
|
|
||||||
if cmp > 0 || cmp < 0 && downgradeOk {
|
|
||||||
v = file.GoVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report a specific error for each tagged file that's too new.
|
// Report a specific error for each tagged file that's too new.
|
||||||
// (Normally the build system will have filtered files by version,
|
// (Normally the build system will have filtered files by version,
|
||||||
@ -399,6 +384,13 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func versionMax(a, b goVersion) goVersion {
|
||||||
|
if a.cmp(b) < 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
// A bailout panic is used for early termination.
|
// A bailout panic is used for early termination.
|
||||||
type bailout struct{}
|
type bailout struct{}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"internal/buildcfg"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -129,8 +130,9 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
|||||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||||
|
|
||||||
case *Alias:
|
case *Alias:
|
||||||
// TODO(gri) is this correct?
|
if !buildcfg.Experiment.AliasTypeParams {
|
||||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||||
|
}
|
||||||
|
|
||||||
tparams := orig.TypeParams()
|
tparams := orig.TypeParams()
|
||||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||||
@ -141,7 +143,7 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
|||||||
return orig // nothing to do (minor optimization)
|
return orig // nothing to do (minor optimization)
|
||||||
}
|
}
|
||||||
|
|
||||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||||
|
@ -1131,3 +1131,23 @@ func f(x int) {
|
|||||||
t.Errorf("got: %s want: %s", got, want)
|
t.Errorf("got: %s want: %s", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue68877(t *testing.T) {
|
||||||
|
const src = `
|
||||||
|
package p
|
||||||
|
|
||||||
|
type (
|
||||||
|
S struct{}
|
||||||
|
A = S
|
||||||
|
T A
|
||||||
|
)`
|
||||||
|
|
||||||
|
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||||
|
pkg := mustTypecheck(src, nil, nil)
|
||||||
|
T := pkg.Scope().Lookup("T").(*TypeName)
|
||||||
|
got := T.String() // this must not panic (was issue)
|
||||||
|
const want = "type p.T struct{}"
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -285,7 +285,7 @@ func (t *Named) cleanup() {
|
|||||||
if t.TypeArgs().Len() == 0 {
|
if t.TypeArgs().Len() == 0 {
|
||||||
panic("nil underlying")
|
panic("nil underlying")
|
||||||
}
|
}
|
||||||
case *Named:
|
case *Named, *Alias:
|
||||||
t.under() // t.under may add entries to check.cleaners
|
t.under() // t.under may add entries to check.cleaners
|
||||||
}
|
}
|
||||||
t.check = nil
|
t.check = nil
|
||||||
|
@ -118,7 +118,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||||||
// that has a type argument for it.
|
// that has a type argument for it.
|
||||||
targs, updated := subst.typeList(t.TypeArgs().list())
|
targs, updated := subst.typeList(t.TypeArgs().list())
|
||||||
if updated {
|
if updated {
|
||||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
|
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Array:
|
case *Array:
|
||||||
|
@ -134,8 +134,8 @@ func (s *_TypeSet) underIs(f func(Type) bool) bool {
|
|||||||
}
|
}
|
||||||
for _, t := range s.terms {
|
for _, t := range s.terms {
|
||||||
assert(t.typ != nil)
|
assert(t.typ != nil)
|
||||||
// x == under(x) for ~x terms
|
// Unalias(x) == under(x) for ~x terms
|
||||||
u := t.typ
|
u := Unalias(t.typ)
|
||||||
if !t.tilde {
|
if !t.tilde {
|
||||||
u = under(u)
|
u = under(u)
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,15 @@ func TypeOf(a any) *Type {
|
|||||||
return (*Type)(NoEscape(unsafe.Pointer(eface.Type)))
|
return (*Type)(NoEscape(unsafe.Pointer(eface.Type)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TypeFor returns the abi.Type for a type parameter.
|
||||||
|
func TypeFor[T any]() *Type {
|
||||||
|
var v T
|
||||||
|
if t := TypeOf(v); t != nil {
|
||||||
|
return t // optimize for T being a non-interface kind
|
||||||
|
}
|
||||||
|
return TypeOf((*T)(nil)).Elem() // only for an interface kind
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Type) Kind() Kind { return t.Kind_ & KindMask }
|
func (t *Type) Kind() Kind { return t.Kind_ & KindMask }
|
||||||
|
|
||||||
func (t *Type) HasName() bool {
|
func (t *Type) HasName() bool {
|
||||||
|
@ -32,28 +32,46 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
|
|||||||
if int64(n) > remain {
|
if int64(n) > remain {
|
||||||
n = int(remain)
|
n = int(remain)
|
||||||
}
|
}
|
||||||
|
m := n
|
||||||
pos1 := pos
|
pos1 := pos
|
||||||
n, err = syscall.Sendfile(dst, src, &pos1, n)
|
n, err = syscall.Sendfile(dst, src, &pos1, n)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
pos += int64(n)
|
pos += int64(n)
|
||||||
written += int64(n)
|
written += int64(n)
|
||||||
remain -= int64(n)
|
remain -= int64(n)
|
||||||
}
|
// (n, nil) indicates that sendfile(2) has transferred
|
||||||
if err == syscall.EINTR {
|
// the exact number of bytes we requested, or some unretryable
|
||||||
|
// error have occurred with partial bytes sent. Either way, we
|
||||||
|
// don't need to go through the following logic to check EINTR
|
||||||
|
// or fell into dstFD.pd.waitWrite, just continue to send the
|
||||||
|
// next chunk or break the loop.
|
||||||
|
if n == m {
|
||||||
continue
|
continue
|
||||||
|
} else if err != syscall.EAGAIN &&
|
||||||
|
err != syscall.EINTR &&
|
||||||
|
err != syscall.EBUSY {
|
||||||
|
// Particularly, EPIPE. Errors like that would normally lead
|
||||||
|
// the subsequent sendfile(2) call to (-1, EBADF).
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
} else if err != syscall.EAGAIN && err != syscall.EINTR {
|
||||||
// This includes syscall.ENOSYS (no kernel
|
// This includes syscall.ENOSYS (no kernel
|
||||||
// support) and syscall.EINVAL (fd types which
|
// support) and syscall.EINVAL (fd types which
|
||||||
// don't implement sendfile), and other errors.
|
// don't implement sendfile), and other errors.
|
||||||
// We should end the loop when there is no error
|
// We should end the loop when there is no error
|
||||||
// returned from sendfile(2) or it is not a retryable error.
|
// returned from sendfile(2) or it is not a retryable error.
|
||||||
if err != syscall.EAGAIN {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if err == syscall.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err = dstFD.pd.waitWrite(dstFD.isFile); err != nil {
|
if err = dstFD.pd.waitWrite(dstFD.isFile); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
if err == syscall.EAGAIN {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL && err != syscall.EOPNOTSUPP && err != syscall.ENOTSUP)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,9 @@ func SendFile(dstFD *FD, src int, remain int64) (written int64, err error, handl
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err == syscall.EAGAIN {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err == syscall.EAGAIN {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,4 @@ type Slice []byte
|
|||||||
type Array [8]byte
|
type Array [8]byte
|
||||||
|
|
||||||
var s Slice
|
var s Slice
|
||||||
var p = (Array)(s /* ok because Go 1.20 ignored the //go:build go1.19 */)
|
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)
|
||||||
|
@ -14,4 +14,4 @@ type Slice []byte
|
|||||||
type Array [8]byte
|
type Array [8]byte
|
||||||
|
|
||||||
var s Slice
|
var s Slice
|
||||||
var p = (Array)(s /* ERROR "requires go1.20 or later" */)
|
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)
|
||||||
|
16
src/internal/types/testdata/check/go1_21_22.go
vendored
Normal file
16
src/internal/types/testdata/check/go1_21_22.go
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// -lang=go1.21
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Check Go language version-specific errors.
|
||||||
|
|
||||||
|
//go:build go1.22
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
for _ = range /* ok because of upgrade to 1.22 */ 10 {
|
||||||
|
}
|
||||||
|
}
|
16
src/internal/types/testdata/check/go1_22_21.go
vendored
Normal file
16
src/internal/types/testdata/check/go1_22_21.go
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// -lang=go1.22
|
||||||
|
|
||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Check Go language version-specific errors.
|
||||||
|
|
||||||
|
//go:build go1.21
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
for _ = range 10 /* ERROR "requires go1.22 or later" */ {
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,9 @@
|
|||||||
// -lang=go1.21
|
// -lang=go1.13
|
||||||
|
|
||||||
// Copyright 2024 The Go Authors. All rights reserved.
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Note: Downgrading to go1.13 requires at least go1.21,
|
|
||||||
// hence the need for -lang=go1.21 at the top.
|
|
||||||
|
|
||||||
//go:build go1.13
|
|
||||||
|
|
||||||
package p
|
package p
|
||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
24
src/internal/types/testdata/fixedbugs/issue68903.go
vendored
Normal file
24
src/internal/types/testdata/fixedbugs/issue68903.go
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2024 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
|
||||||
|
|
||||||
|
type A = [4]int
|
||||||
|
type B = map[string]interface{}
|
||||||
|
|
||||||
|
func _[T ~A](x T) {
|
||||||
|
_ = len(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[U ~A](x U) {
|
||||||
|
_ = cap(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[V ~A]() {
|
||||||
|
_ = V{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[W ~B](a interface{}) {
|
||||||
|
_ = a.(W)["key"]
|
||||||
|
}
|
26
src/internal/types/testdata/fixedbugs/issue68935.go
vendored
Normal file
26
src/internal/types/testdata/fixedbugs/issue68935.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2024 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
|
||||||
|
|
||||||
|
type A = struct {
|
||||||
|
F string
|
||||||
|
G int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Make[T ~A]() T {
|
||||||
|
return T{
|
||||||
|
F: "blah",
|
||||||
|
G: 1234,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type N struct {
|
||||||
|
F string
|
||||||
|
G int
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
_ = Make[N]()
|
||||||
|
}
|
@ -5,9 +5,12 @@
|
|||||||
package weak_test
|
package weak_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"internal/weak"
|
"internal/weak"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type T struct {
|
type T struct {
|
||||||
@ -128,3 +131,82 @@ func TestPointerFinalizer(t *testing.T) {
|
|||||||
t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
|
t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for issue 69210.
|
||||||
|
//
|
||||||
|
// Weak-to-strong conversions must shade the new strong pointer, otherwise
|
||||||
|
// that might be creating the only strong pointer to a white object which
|
||||||
|
// is hidden in a blackened stack.
|
||||||
|
//
|
||||||
|
// Never fails if correct, fails with some high probability if incorrect.
|
||||||
|
func TestIssue69210(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("this is a stress test that takes seconds to run on its own")
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// What we're trying to do is manufacture the conditions under which this
|
||||||
|
// bug happens. Specifically, we want:
|
||||||
|
//
|
||||||
|
// 1. To create a whole bunch of objects that are only weakly-pointed-to,
|
||||||
|
// 2. To call Strong while the GC is in the mark phase,
|
||||||
|
// 3. The new strong pointer to be missed by the GC,
|
||||||
|
// 4. The following GC cycle to mark a free object.
|
||||||
|
//
|
||||||
|
// Unfortunately, (2) and (3) are hard to control, but we can increase
|
||||||
|
// the likelihood by having several goroutines do (1) at once while
|
||||||
|
// another goroutine constantly keeps us in the GC with runtime.GC.
|
||||||
|
// Like throwing darts at a dart board until they land just right.
|
||||||
|
// We can increase the likelihood of (4) by adding some delay after
|
||||||
|
// creating the strong pointer, but only if it's non-nil. If it's nil,
|
||||||
|
// that means it was already collected in which case there's no chance
|
||||||
|
// of triggering the bug, so we want to retry as fast as possible.
|
||||||
|
// Our heap here is tiny, so the GCs will go by fast.
|
||||||
|
//
|
||||||
|
// As of 2024-09-03, removing the line that shades pointers during
|
||||||
|
// the weak-to-strong conversion causes this test to fail about 50%
|
||||||
|
// of the time.
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for range max(runtime.GOMAXPROCS(-1)-1, 1) {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
for range 5 {
|
||||||
|
bt := new(T)
|
||||||
|
wt := weak.Make(bt)
|
||||||
|
bt = nil
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
bt = wt.Strong()
|
||||||
|
if bt != nil {
|
||||||
|
time.Sleep(4 * time.Millisecond)
|
||||||
|
bt.t = bt
|
||||||
|
bt.a = 12
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(bt)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
@ -614,7 +614,8 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
|
|||||||
|
|
||||||
// Redirect behavior:
|
// Redirect behavior:
|
||||||
redirectMethod string
|
redirectMethod string
|
||||||
includeBody bool
|
includeBody = true
|
||||||
|
stripSensitiveHeaders = false
|
||||||
)
|
)
|
||||||
uerr := func(err error) error {
|
uerr := func(err error) error {
|
||||||
// the body may have been closed already by c.send()
|
// the body may have been closed already by c.send()
|
||||||
@ -681,7 +682,12 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
|
|||||||
// in case the user set Referer on their first request.
|
// in case the user set Referer on their first request.
|
||||||
// If they really want to override, they can do it in
|
// If they really want to override, they can do it in
|
||||||
// their CheckRedirect func.
|
// their CheckRedirect func.
|
||||||
copyHeaders(req)
|
if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host {
|
||||||
|
if !shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) {
|
||||||
|
stripSensitiveHeaders = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copyHeaders(req, stripSensitiveHeaders)
|
||||||
|
|
||||||
// Add the Referer header from the most recent
|
// Add the Referer header from the most recent
|
||||||
// request URL to the new one, if it's not https->http:
|
// request URL to the new one, if it's not https->http:
|
||||||
@ -744,7 +750,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
|
|||||||
// makeHeadersCopier makes a function that copies headers from the
|
// makeHeadersCopier makes a function that copies headers from the
|
||||||
// initial Request, ireq. For every redirect, this function must be called
|
// initial Request, ireq. For every redirect, this function must be called
|
||||||
// so that it can copy headers into the upcoming Request.
|
// so that it can copy headers into the upcoming Request.
|
||||||
func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
|
func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) {
|
||||||
// The headers to copy are from the very initial request.
|
// The headers to copy are from the very initial request.
|
||||||
// We use a closured callback to keep a reference to these original headers.
|
// We use a closured callback to keep a reference to these original headers.
|
||||||
var (
|
var (
|
||||||
@ -758,8 +764,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preq := ireq // The previous request
|
return func(req *Request, stripSensitiveHeaders bool) {
|
||||||
return func(req *Request) {
|
|
||||||
// If Jar is present and there was some initial cookies provided
|
// If Jar is present and there was some initial cookies provided
|
||||||
// via the request header, then we may need to alter the initial
|
// via the request header, then we may need to alter the initial
|
||||||
// cookies as we follow redirects since each redirect may end up
|
// cookies as we follow redirects since each redirect may end up
|
||||||
@ -796,12 +801,15 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
|
|||||||
// Copy the initial request's Header values
|
// Copy the initial request's Header values
|
||||||
// (at least the safe ones).
|
// (at least the safe ones).
|
||||||
for k, vv := range ireqhdr {
|
for k, vv := range ireqhdr {
|
||||||
if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) {
|
sensitive := false
|
||||||
|
switch CanonicalHeaderKey(k) {
|
||||||
|
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
if !(sensitive && stripSensitiveHeaders) {
|
||||||
req.Header[k] = vv
|
req.Header[k] = vv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preq = req // Update previous Request with the current request
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,9 +985,7 @@ func (b *cancelTimerBody) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
|
func shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool {
|
||||||
switch CanonicalHeaderKey(headerKey) {
|
|
||||||
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
|
||||||
// Permit sending auth/cookie headers from "foo.com"
|
// Permit sending auth/cookie headers from "foo.com"
|
||||||
// to "sub.foo.com".
|
// to "sub.foo.com".
|
||||||
|
|
||||||
@ -997,9 +1003,6 @@ func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
|
|||||||
dhost := idnaASCIIFromURL(dest)
|
dhost := idnaASCIIFromURL(dest)
|
||||||
return isDomainOrSubdomain(dhost, ihost)
|
return isDomainOrSubdomain(dhost, ihost)
|
||||||
}
|
}
|
||||||
// All other headers are copied:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDomainOrSubdomain reports whether sub is a subdomain (or exact
|
// isDomainOrSubdomain reports whether sub is a subdomain (or exact
|
||||||
// match) of the parent domain.
|
// match) of the parent domain.
|
||||||
|
@ -1536,6 +1536,55 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue #70530: Once we strip a header on a redirect to a different host,
|
||||||
|
// the header should stay stripped across any further redirects.
|
||||||
|
func TestClientStripHeadersOnRepeatedRedirect(t *testing.T) {
|
||||||
|
run(t, testClientStripHeadersOnRepeatedRedirect)
|
||||||
|
}
|
||||||
|
func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
|
||||||
|
var proto string
|
||||||
|
ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||||
|
if r.Host+r.URL.Path != "a.example.com/" {
|
||||||
|
if h := r.Header.Get("Authorization"); h != "" {
|
||||||
|
t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Follow a chain of redirects from a to b and back to a.
|
||||||
|
// The Authorization header is stripped on the first redirect to b,
|
||||||
|
// and stays stripped even if we're sent back to a.
|
||||||
|
switch r.Host + r.URL.Path {
|
||||||
|
case "a.example.com/":
|
||||||
|
Redirect(w, r, proto+"://b.example.com/", StatusFound)
|
||||||
|
case "b.example.com/":
|
||||||
|
Redirect(w, r, proto+"://b.example.com/redirect", StatusFound)
|
||||||
|
case "b.example.com/redirect":
|
||||||
|
Redirect(w, r, proto+"://a.example.com/redirect", StatusFound)
|
||||||
|
case "a.example.com/redirect":
|
||||||
|
w.Header().Set("X-Done", "true")
|
||||||
|
default:
|
||||||
|
t.Errorf("unexpected request to %v", r.URL)
|
||||||
|
}
|
||||||
|
})).ts
|
||||||
|
proto, _, _ = strings.Cut(ts.URL, ":")
|
||||||
|
|
||||||
|
c := ts.Client()
|
||||||
|
c.Transport.(*Transport).Dial = func(_ string, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("tcp", ts.Listener.Addr().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := NewRequest("GET", proto+"://a.example.com/", nil)
|
||||||
|
req.Header.Add("Cookie", "foo=bar")
|
||||||
|
req.Header.Add("Authorization", "secretpassword")
|
||||||
|
res, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.Header.Get("X-Done") != "true" {
|
||||||
|
t.Fatalf("response missing expected header: X-Done=true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Issue 22233: copy host when Client follows a relative redirect.
|
// Issue 22233: copy host when Client follows a relative redirect.
|
||||||
func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) }
|
func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) }
|
||||||
func testClientCopyHostOnRedirect(t *testing.T, mode testMode) {
|
func testClientCopyHostOnRedirect(t *testing.T, mode testMode) {
|
||||||
@ -1702,43 +1751,39 @@ func testClientAltersCookiesOnRedirect(t *testing.T, mode testMode) {
|
|||||||
// Part of Issue 4800
|
// Part of Issue 4800
|
||||||
func TestShouldCopyHeaderOnRedirect(t *testing.T) {
|
func TestShouldCopyHeaderOnRedirect(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
header string
|
|
||||||
initialURL string
|
initialURL string
|
||||||
destURL string
|
destURL string
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"User-Agent", "http://foo.com/", "http://bar.com/", true},
|
|
||||||
{"X-Foo", "http://foo.com/", "http://bar.com/", true},
|
|
||||||
|
|
||||||
// Sensitive headers:
|
// Sensitive headers:
|
||||||
{"cookie", "http://foo.com/", "http://bar.com/", false},
|
{"http://foo.com/", "http://bar.com/", false},
|
||||||
{"cookie2", "http://foo.com/", "http://bar.com/", false},
|
{"http://foo.com/", "http://bar.com/", false},
|
||||||
{"authorization", "http://foo.com/", "http://bar.com/", false},
|
{"http://foo.com/", "http://bar.com/", false},
|
||||||
{"authorization", "http://foo.com/", "https://foo.com/", true},
|
{"http://foo.com/", "https://foo.com/", true},
|
||||||
{"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true},
|
{"http://foo.com:1234/", "http://foo.com:4321/", true},
|
||||||
{"www-authenticate", "http://foo.com/", "http://bar.com/", false},
|
{"http://foo.com/", "http://bar.com/", false},
|
||||||
{"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false},
|
{"http://foo.com/", "http://[::1%25.foo.com]/", false},
|
||||||
|
|
||||||
// But subdomains should work:
|
// But subdomains should work:
|
||||||
{"www-authenticate", "http://foo.com/", "http://foo.com/", true},
|
{"http://foo.com/", "http://foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true},
|
{"http://foo.com/", "http://sub.foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com/", "http://notfoo.com/", false},
|
{"http://foo.com/", "http://notfoo.com/", false},
|
||||||
{"www-authenticate", "http://foo.com/", "https://foo.com/", true},
|
{"http://foo.com/", "https://foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com:80/", "http://foo.com/", true},
|
{"http://foo.com:80/", "http://foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true},
|
{"http://foo.com:80/", "http://sub.foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com:443/", "https://foo.com/", true},
|
{"http://foo.com:443/", "https://foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true},
|
{"http://foo.com:443/", "https://sub.foo.com/", true},
|
||||||
{"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true},
|
{"http://foo.com:1234/", "http://foo.com/", true},
|
||||||
|
|
||||||
{"authorization", "http://foo.com/", "http://foo.com/", true},
|
{"http://foo.com/", "http://foo.com/", true},
|
||||||
{"authorization", "http://foo.com/", "http://sub.foo.com/", true},
|
{"http://foo.com/", "http://sub.foo.com/", true},
|
||||||
{"authorization", "http://foo.com/", "http://notfoo.com/", false},
|
{"http://foo.com/", "http://notfoo.com/", false},
|
||||||
{"authorization", "http://foo.com/", "https://foo.com/", true},
|
{"http://foo.com/", "https://foo.com/", true},
|
||||||
{"authorization", "http://foo.com:80/", "http://foo.com/", true},
|
{"http://foo.com:80/", "http://foo.com/", true},
|
||||||
{"authorization", "http://foo.com:80/", "http://sub.foo.com/", true},
|
{"http://foo.com:80/", "http://sub.foo.com/", true},
|
||||||
{"authorization", "http://foo.com:443/", "https://foo.com/", true},
|
{"http://foo.com:443/", "https://foo.com/", true},
|
||||||
{"authorization", "http://foo.com:443/", "https://sub.foo.com/", true},
|
{"http://foo.com:443/", "https://sub.foo.com/", true},
|
||||||
{"authorization", "http://foo.com:1234/", "http://foo.com/", true},
|
{"http://foo.com:1234/", "http://foo.com/", true},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
u0, err := url.Parse(tt.initialURL)
|
u0, err := url.Parse(tt.initialURL)
|
||||||
@ -1751,10 +1796,10 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) {
|
|||||||
t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err)
|
t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1)
|
got := Export_shouldCopyHeaderOnRedirect(u0, u1)
|
||||||
if got != tt.want {
|
if got != tt.want {
|
||||||
t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v",
|
t.Errorf("%d. shouldCopyHeaderOnRedirect(%q => %q) = %v; want %v",
|
||||||
i, tt.header, tt.initialURL, tt.destURL, got, tt.want)
|
i, tt.initialURL, tt.destURL, got, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,56 +10,56 @@ import "strings"
|
|||||||
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
||||||
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
||||||
// generated from src/crypto/tls:
|
// generated from src/crypto/tls:
|
||||||
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com,*.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||||
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
|
||||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
|
||||||
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
|
||||||
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
|
||||||
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
|
||||||
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
|
||||||
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||||||
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||||
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||||||
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||||||
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
|
||||||
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
|
||||||
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
|
||||||
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
|
||||||
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
|
||||||
WkBKOclmOV2xlTVuPw==
|
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
|
||||||
-----END CERTIFICATE-----`)
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
// LocalhostKey is the private key for LocalhostCert.
|
// LocalhostKey is the private key for LocalhostCert.
|
||||||
var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY-----
|
var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY-----
|
||||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFyXr1E4l3GM34
|
||||||
4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS
|
wlmdsWtjHJCigAMKwnpUOS4zI1AiLH8eTXk2T+4XIFfUx775oSkaZjdEhjh9S8Qu
|
||||||
gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW
|
pP+yu8AexNfBVVK20xxjylwAWZdKqjfHgy5RMb+MJfdV+2PSvcQzkzwiZjWvMD+O
|
||||||
URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX
|
pNMsmDTSsP4Oa4MAFypC+hfD9FXzDXJNGLkE+gcMUP8BZO39iAy+TWXZir/EjxVs
|
||||||
AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy
|
xQimMGgZfFaxJ69DmLazWaT3/JnO7RiynW1OXMOo49rjKwWMGK11eLB/GPG2/mde
|
||||||
VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
|
o4I/muF4o7SxYuTR960ynU5XklIkwAnDpzZkySVTZYyoASlGN0T+8d8i42D7IZpF
|
||||||
x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk
|
GojTs1lFAgMBAAECggEAIYthUi1lFBDd5gG4Rzlu+BlBIn5JhcqkCqLEBiJIFfOr
|
||||||
lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL
|
/4yuMRrvS3bNzqWt6xJ9MSAC4ZlN/VobRLnxL/QNymoiGYUKCT3Ww8nvPpPzR9OE
|
||||||
dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
|
sE68TUL9tJw/zZJcRMKwgvrGqSLimfq53MxxkE+kLdOc0v9C8YH8Re26mB5ZcWYa
|
||||||
EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq
|
7YFyZQpKsQYnsmu/05cMbpOQrQWhtmIqRoyn8mG/par2s3NzjtpSE9NINyz26uFc
|
||||||
XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
|
k/3ovFJQIHkUmTS7KHD3BgY5vuCqP98HramYnOysJ0WoYgvSDNCWw3037s5CCwJT
|
||||||
6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O
|
gCKuM+Ow6liFrj83RrdKBpm5QUGjfNpYP31o+QNP4QKBgQDSrUQ2XdgtAnibAV7u
|
||||||
3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
|
7kbxOxro0EhIKso0Y/6LbDQgcXgxLqltkmeqZgG8nC3Z793lhlSasz2snhzzooV5
|
||||||
uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
|
5fTy1y8ikXqjhG0nNkInFyOhsI0auE28CFoDowaQd+5cmCatpN4Grqo5PNRXxm1w
|
||||||
Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
|
HktfPEgoP11NNCFHvvN5fEKbbQKBgQDwVlOaV20IvW3IPq7cXZyiyabouFF9eTRo
|
||||||
w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo
|
VJka1Uv+JtyvL2P0NKkjYHOdN8gRblWqxQtJoTNk020rVA4UP1heiXALy50gvj/p
|
||||||
+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
|
hMcybPTLYSPOhAGx838KIcvGR5oskP1aUCmFbFQzGELxhJ9diVVjxUtbG2DuwPKd
|
||||||
OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
|
tD9TLxT2OQKBgQCcdlHSjp+dzdgERmBa0ludjGfPv9/uuNizUBAbO6D690psPFtY
|
||||||
brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv
|
JQMYaemgSd1DngEOFVWADt4e9M5Lose+YCoqr+UxpxmNlyv5kzJOFcFAs/4XeglB
|
||||||
m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y
|
PHKdgNW/NVKxMc6H54l9LPr+x05sYdGlEtqnP/3W5jhEvhJ5Vjc8YiyVgQKBgQCl
|
||||||
LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
|
zwjyrGo+42GACy7cPYE5FeIfIDqoVByB9guC5bD98JXEDu/opQQjsgFRcBCJZhOY
|
||||||
/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN
|
M0UsURiB8ROaFu13rpQq9KrmmF0ZH+g8FSzQbzcbsTLg4VXCDXmR5esOKowFPypr
|
||||||
s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
|
Sm667BfTAGP++D5ya7MLmCv6+RKQ5XD8uEQQAaV2kQKBgAD8qeJuWIXZT0VKkQrn
|
||||||
Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
|
nIhgtzGERF/6sZdQGW2LxTbUDWG74AfFkkEbeBfwEkCZXY/xmnYqYABhvlSex8jU
|
||||||
xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/
|
supU6Eea21esIxIub2zv/Np0ojUb6rlqTPS4Ox1E27D787EJ3VOXpriSD10vyNnZ
|
||||||
ZboOWVe3icTy64BT3OQhmg==
|
jel6uj2FOP9g54s+GzlSVg/T
|
||||||
-----END RSA TESTING KEY-----`))
|
-----END RSA TESTING KEY-----`))
|
||||||
|
|
||||||
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
||||||
|
@ -53,6 +53,9 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err, false
|
return 0, err, false
|
||||||
}
|
}
|
||||||
|
if fi.Mode()&(fs.ModeSymlink|fs.ModeDevice|fs.ModeCharDevice|fs.ModeIrregular) != 0 {
|
||||||
|
return 0, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
remain = fi.Size()
|
remain = fi.Size()
|
||||||
}
|
}
|
||||||
|
86
src/net/sendfile_unix_test.go
Normal file
86
src/net/sendfile_unix_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/testpty"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Issue 70763: test that we don't fail on sendfile from a tty.
|
||||||
|
func TestCopyFromTTY(t *testing.T) {
|
||||||
|
pty, ttyName, err := testpty.Open()
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("skipping test because pty open failed: %v", err)
|
||||||
|
}
|
||||||
|
defer pty.Close()
|
||||||
|
|
||||||
|
// Use syscall.Open so that the tty is blocking.
|
||||||
|
ttyFD, err := syscall.Open(ttyName, syscall.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("skipping test because tty open failed: %v", err)
|
||||||
|
}
|
||||||
|
defer syscall.Close(ttyFD)
|
||||||
|
|
||||||
|
tty := os.NewFile(uintptr(ttyFD), "tty")
|
||||||
|
defer tty.Close()
|
||||||
|
|
||||||
|
ln := newLocalListener(t, "tcp")
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
ch := make(chan bool)
|
||||||
|
|
||||||
|
const data = "data\n"
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, len(data))
|
||||||
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn, err := Dial("tcp", ln.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if _, err := pty.Write([]byte(data)); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
<-ch
|
||||||
|
if err := pty.Close(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
lr := io.LimitReader(tty, int64(len(data)))
|
||||||
|
if _, err := io.Copy(conn, lr); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
154
src/os/copy_test.go
Normal file
154
src/os/copy_test.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2024 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 os_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math/rand/v2"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/nettest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exercise sendfile/splice fast paths with a moderately large file.
|
||||||
|
//
|
||||||
|
// https://go.dev/issue/70000
|
||||||
|
|
||||||
|
func TestLargeCopyViaNetwork(t *testing.T) {
|
||||||
|
const size = 10 * 1024 * 1024
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
src, err := os.Create(dir + "/src")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
if _, err := io.CopyN(src, newRandReader(), size); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := src.Seek(0, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := os.Create(dir + "/dst")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
client, server := createSocketPair(t, "tcp")
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if n, err := io.Copy(dst, server); n != size || err != nil {
|
||||||
|
t.Errorf("copy to destination = %v, %v; want %v, nil", n, err, size)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
defer client.Close()
|
||||||
|
if n, err := io.Copy(client, src); n != size || err != nil {
|
||||||
|
t.Errorf("copy from source = %v, %v; want %v, nil", n, err, size)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if _, err := dst.Seek(0, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := compareReaders(dst, io.LimitReader(newRandReader(), size)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareReaders(a, b io.Reader) error {
|
||||||
|
bufa := make([]byte, 4096)
|
||||||
|
bufb := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
na, erra := io.ReadFull(a, bufa)
|
||||||
|
if erra != nil && erra != io.EOF {
|
||||||
|
return erra
|
||||||
|
}
|
||||||
|
nb, errb := io.ReadFull(b, bufb)
|
||||||
|
if errb != nil && errb != io.EOF {
|
||||||
|
return errb
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bufa[:na], bufb[:nb]) {
|
||||||
|
return errors.New("contents mismatch")
|
||||||
|
}
|
||||||
|
if erra == io.EOF && errb == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type randReader struct {
|
||||||
|
rand *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRandReader() *randReader {
|
||||||
|
return &randReader{rand.New(rand.NewPCG(0, 0))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *randReader) Read(p []byte) (int, error) {
|
||||||
|
var v uint64
|
||||||
|
var n int
|
||||||
|
for i := range p {
|
||||||
|
if n == 0 {
|
||||||
|
v = r.rand.Uint64()
|
||||||
|
n = 8
|
||||||
|
}
|
||||||
|
p[i] = byte(v & 0xff)
|
||||||
|
v >>= 8
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
|
||||||
|
t.Helper()
|
||||||
|
if !nettest.TestableNetwork(proto) {
|
||||||
|
t.Skipf("%s does not support %q", runtime.GOOS, proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
ln, err := nettest.NewLocalListener(proto)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewLocalListener error: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if ln != nil {
|
||||||
|
ln.Close()
|
||||||
|
}
|
||||||
|
if client != nil {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
if server != nil {
|
||||||
|
server.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
var err error
|
||||||
|
server, err = ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Accept new connection error: %v", err)
|
||||||
|
}
|
||||||
|
ch <- struct{}{}
|
||||||
|
}()
|
||||||
|
client, err = net.Dial(proto, ln.Addr().String())
|
||||||
|
<-ch
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Dial new connection error: %v", err)
|
||||||
|
}
|
||||||
|
return client, server
|
||||||
|
}
|
@ -132,15 +132,18 @@ func ReadDir(name string) ([]DirEntry, error) {
|
|||||||
// CopyFS copies the file system fsys into the directory dir,
|
// CopyFS copies the file system fsys into the directory dir,
|
||||||
// creating dir if necessary.
|
// creating dir if necessary.
|
||||||
//
|
//
|
||||||
// Newly created directories and files have their default modes
|
// Files are created with mode 0o666 plus any execute permissions
|
||||||
// where any bits from the file in fsys that are not part of the
|
// from the source, and directories are created with mode 0o777
|
||||||
// standard read, write, and execute permissions will be zeroed
|
// (before umask).
|
||||||
// out, and standard read and write permissions are set for owner,
|
|
||||||
// group, and others while retaining any existing execute bits from
|
|
||||||
// the file in fsys.
|
|
||||||
//
|
//
|
||||||
// Symbolic links in fsys are not supported, a *PathError with Err set
|
// CopyFS will not overwrite existing files. If a file name in fsys
|
||||||
// to ErrInvalid is returned on symlink.
|
// already exists in the destination, CopyFS will return an error
|
||||||
|
// such that errors.Is(err, fs.ErrExist) will be true.
|
||||||
|
//
|
||||||
|
// Symbolic links in fsys are not supported. A *PathError with Err set
|
||||||
|
// to ErrInvalid is returned when copying from a symbolic link.
|
||||||
|
//
|
||||||
|
// Symbolic links in dir are followed.
|
||||||
//
|
//
|
||||||
// Copying stops at and returns the first error encountered.
|
// Copying stops at and returns the first error encountered.
|
||||||
func CopyFS(dir string, fsys fs.FS) error {
|
func CopyFS(dir string, fsys fs.FS) error {
|
||||||
@ -174,7 +177,7 @@ func CopyFS(dir string, fsys fs.FS) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w, err := OpenFile(newPath, O_CREATE|O_TRUNC|O_WRONLY, 0666|info.Mode()&0777)
|
w, err := OpenFile(newPath, O_CREATE|O_EXCL|O_WRONLY, 0666|info.Mode()&0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func ExampleFileMode() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("permissions: %#o\n", fi.Mode().Perm()) // 0400, 0777, etc.
|
fmt.Printf("permissions: %#o\n", fi.Mode().Perm()) // 0o400, 0o777, etc.
|
||||||
switch mode := fi.Mode(); {
|
switch mode := fi.Mode(); {
|
||||||
case mode.IsRegular():
|
case mode.IsRegular():
|
||||||
fmt.Println("regular file")
|
fmt.Println("regular file")
|
||||||
|
@ -35,10 +35,11 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attrSys, shouldDupPidfd := ensurePidfd(attr.Sys)
|
||||||
sysattr := &syscall.ProcAttr{
|
sysattr := &syscall.ProcAttr{
|
||||||
Dir: attr.Dir,
|
Dir: attr.Dir,
|
||||||
Env: attr.Env,
|
Env: attr.Env,
|
||||||
Sys: ensurePidfd(attr.Sys),
|
Sys: attrSys,
|
||||||
}
|
}
|
||||||
if sysattr.Env == nil {
|
if sysattr.Env == nil {
|
||||||
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
||||||
@ -63,7 +64,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
|||||||
// For Windows, syscall.StartProcess above already returned a process handle.
|
// For Windows, syscall.StartProcess above already returned a process handle.
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
var ok bool
|
var ok bool
|
||||||
h, ok = getPidfd(sysattr.Sys)
|
h, ok = getPidfd(sysattr.Sys, shouldDupPidfd)
|
||||||
if !ok {
|
if !ok {
|
||||||
return newPIDProcess(pid), nil
|
return newPIDProcess(pid), nil
|
||||||
}
|
}
|
||||||
|
@ -366,7 +366,7 @@ func Open(name string) (*File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create creates or truncates the named file. If the file already exists,
|
// Create creates or truncates the named file. If the file already exists,
|
||||||
// it is truncated. If the file does not exist, it is created with mode 0666
|
// it is truncated. If the file does not exist, it is created with mode 0o666
|
||||||
// (before umask). If successful, methods on the returned File can
|
// (before umask). If successful, methods on the returned File can
|
||||||
// be used for I/O; the associated file descriptor has mode O_RDWR.
|
// be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||||
// If there is an error, it will be of type *PathError.
|
// If there is an error, it will be of type *PathError.
|
||||||
@ -602,11 +602,11 @@ func UserHomeDir() (string, error) {
|
|||||||
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
|
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
|
||||||
// ModeSticky are used.
|
// ModeSticky are used.
|
||||||
//
|
//
|
||||||
// On Windows, only the 0200 bit (owner writable) of mode is used; it
|
// On Windows, only the 0o200 bit (owner writable) of mode is used; it
|
||||||
// controls whether the file's read-only attribute is set or cleared.
|
// controls whether the file's read-only attribute is set or cleared.
|
||||||
// The other bits are currently unused. For compatibility with Go 1.12
|
// The other bits are currently unused. For compatibility with Go 1.12
|
||||||
// and earlier, use a non-zero mode. Use mode 0400 for a read-only
|
// and earlier, use a non-zero mode. Use mode 0o400 for a read-only
|
||||||
// file and 0600 for a readable+writable file.
|
// file and 0o600 for a readable+writable file.
|
||||||
//
|
//
|
||||||
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
|
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
|
||||||
// and ModeTemporary are used.
|
// and ModeTemporary are used.
|
||||||
|
@ -1376,8 +1376,7 @@ func TestChtimes(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
f := newFile(t)
|
f := newFile(t)
|
||||||
|
// This should be an empty file (see #68687, #68663).
|
||||||
f.Write([]byte("hello, world\n"))
|
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
testChtimes(t, f.Name())
|
testChtimes(t, f.Name())
|
||||||
@ -1395,12 +1394,9 @@ func TestChtimesOmit(t *testing.T) {
|
|||||||
func testChtimesOmit(t *testing.T, omitAt, omitMt bool) {
|
func testChtimesOmit(t *testing.T, omitAt, omitMt bool) {
|
||||||
t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt)
|
t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt)
|
||||||
file := newFile(t)
|
file := newFile(t)
|
||||||
_, err := file.Write([]byte("hello, world\n"))
|
// This should be an empty file (see #68687, #68663).
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
name := file.Name()
|
name := file.Name()
|
||||||
err = file.Close()
|
err := file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -3358,6 +3354,14 @@ func TestCopyFS(t *testing.T) {
|
|||||||
t.Fatal("comparing two directories:", err)
|
t.Fatal("comparing two directories:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test whether CopyFS disallows copying for disk filesystem when there is any
|
||||||
|
// existing file in the destination directory.
|
||||||
|
if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
|
||||||
|
t.Errorf("CopyFS should have failed and returned error when there is"+
|
||||||
|
"any existing file in the destination directory (in disk filesystem), "+
|
||||||
|
"got: %v, expected any error that indicates <file exists>", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Test with memory filesystem.
|
// Test with memory filesystem.
|
||||||
fsys = fstest.MapFS{
|
fsys = fstest.MapFS{
|
||||||
"william": {Data: []byte("Shakespeare\n")},
|
"william": {Data: []byte("Shakespeare\n")},
|
||||||
@ -3395,6 +3399,14 @@ func TestCopyFS(t *testing.T) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatal("comparing two directories:", err)
|
t.Fatal("comparing two directories:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test whether CopyFS disallows copying for memory filesystem when there is any
|
||||||
|
// existing file in the destination directory.
|
||||||
|
if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
|
||||||
|
t.Errorf("CopyFS should have failed and returned error when there is"+
|
||||||
|
"any existing file in the destination directory (in memory filesystem), "+
|
||||||
|
"got: %v, expected any error that indicates <file exists>", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyFSWithSymlinks(t *testing.T) {
|
func TestCopyFSWithSymlinks(t *testing.T) {
|
||||||
|
@ -8,20 +8,28 @@
|
|||||||
// v5.3: pidfd_open syscall, clone3 syscall;
|
// v5.3: pidfd_open syscall, clone3 syscall;
|
||||||
// v5.4: P_PIDFD idtype support for waitid syscall;
|
// v5.4: P_PIDFD idtype support for waitid syscall;
|
||||||
// v5.6: pidfd_getfd syscall.
|
// v5.6: pidfd_getfd syscall.
|
||||||
|
//
|
||||||
|
// N.B. Alternative Linux implementations may not follow this ordering. e.g.,
|
||||||
|
// QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until
|
||||||
|
// 8.0.
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"internal/syscall/unix"
|
"internal/syscall/unix"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
// ensurePidfd initializes the PidFD field in sysAttr if it is not already set.
|
||||||
|
// It returns the original or modified SysProcAttr struct and a flag indicating
|
||||||
|
// whether the PidFD should be duplicated before using.
|
||||||
|
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
|
||||||
if !pidfdWorks() {
|
if !pidfdWorks() {
|
||||||
return sysAttr
|
return sysAttr, false
|
||||||
}
|
}
|
||||||
|
|
||||||
var pidfd int
|
var pidfd int
|
||||||
@ -29,23 +37,33 @@ func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
|||||||
if sysAttr == nil {
|
if sysAttr == nil {
|
||||||
return &syscall.SysProcAttr{
|
return &syscall.SysProcAttr{
|
||||||
PidFD: &pidfd,
|
PidFD: &pidfd,
|
||||||
}
|
}, false
|
||||||
}
|
}
|
||||||
if sysAttr.PidFD == nil {
|
if sysAttr.PidFD == nil {
|
||||||
newSys := *sysAttr // copy
|
newSys := *sysAttr // copy
|
||||||
newSys.PidFD = &pidfd
|
newSys.PidFD = &pidfd
|
||||||
return &newSys
|
return &newSys, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return sysAttr
|
return sysAttr, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPidfd(sysAttr *syscall.SysProcAttr) (uintptr, bool) {
|
// getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is
|
||||||
|
// set) and a flag indicating whether the value can be used.
|
||||||
|
func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
|
||||||
if !pidfdWorks() {
|
if !pidfdWorks() {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return uintptr(*sysAttr.PidFD), true
|
h := *sysAttr.PidFD
|
||||||
|
if needDup {
|
||||||
|
dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
|
||||||
|
if e != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
h = dupH
|
||||||
|
}
|
||||||
|
return uintptr(h), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func pidfdFind(pid int) (uintptr, error) {
|
func pidfdFind(pid int) (uintptr, error) {
|
||||||
@ -126,14 +144,21 @@ func pidfdWorks() bool {
|
|||||||
|
|
||||||
var checkPidfdOnce = sync.OnceValue(checkPidfd)
|
var checkPidfdOnce = sync.OnceValue(checkPidfd)
|
||||||
|
|
||||||
// checkPidfd checks whether all required pidfd-related syscalls work.
|
// checkPidfd checks whether all required pidfd-related syscalls work. This
|
||||||
// This consists of pidfd_open and pidfd_send_signal syscalls, and waitid
|
// consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with
|
||||||
// syscall with idtype of P_PIDFD.
|
// idtype of P_PIDFD, and clone(CLONE_PIDFD).
|
||||||
//
|
//
|
||||||
// Reasons for non-working pidfd syscalls include an older kernel and an
|
// Reasons for non-working pidfd syscalls include an older kernel and an
|
||||||
// execution environment in which the above system calls are restricted by
|
// execution environment in which the above system calls are restricted by
|
||||||
// seccomp or a similar technology.
|
// seccomp or a similar technology.
|
||||||
func checkPidfd() error {
|
func checkPidfd() error {
|
||||||
|
// In Android version < 12, pidfd-related system calls are not allowed
|
||||||
|
// by seccomp and trigger the SIGSYS signal. See issue #69065.
|
||||||
|
if runtime.GOOS == "android" {
|
||||||
|
ignoreSIGSYS()
|
||||||
|
defer restoreSIGSYS()
|
||||||
|
}
|
||||||
|
|
||||||
// Get a pidfd of the current process (opening of "/proc/self" won't
|
// Get a pidfd of the current process (opening of "/proc/self" won't
|
||||||
// work for waitid).
|
// work for waitid).
|
||||||
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
|
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
|
||||||
@ -159,5 +184,27 @@ func checkPidfd() error {
|
|||||||
return NewSyscallError("pidfd_send_signal", err)
|
return NewSyscallError("pidfd_send_signal", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that clone(CLONE_PIDFD) works.
|
||||||
|
//
|
||||||
|
// This shouldn't be necessary since pidfd_open was added in Linux 5.3,
|
||||||
|
// after CLONE_PIDFD in Linux 5.2, but some alternative Linux
|
||||||
|
// implementations may not adhere to this ordering.
|
||||||
|
if err := checkClonePidfd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provided by syscall.
|
||||||
|
//
|
||||||
|
//go:linkname checkClonePidfd
|
||||||
|
func checkClonePidfd() error
|
||||||
|
|
||||||
|
// Provided by runtime.
|
||||||
|
//
|
||||||
|
//go:linkname ignoreSIGSYS
|
||||||
|
func ignoreSIGSYS()
|
||||||
|
|
||||||
|
//go:linkname restoreSIGSYS
|
||||||
|
func restoreSIGSYS()
|
||||||
|
@ -6,8 +6,10 @@ package os_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"internal/syscall/unix"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -57,3 +59,93 @@ func TestFindProcessViaPidfd(t *testing.T) {
|
|||||||
t.Fatalf("Release: got %v, want <nil>", err)
|
t.Fatalf("Release: got %v, want <nil>", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStartProcessWithPidfd(t *testing.T) {
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if err := os.CheckPidfdOnce(); err != nil {
|
||||||
|
// Non-pidfd code paths tested in exec_unix_test.go.
|
||||||
|
t.Skipf("skipping: pidfd not available: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pidfd int
|
||||||
|
p, err := os.StartProcess(testenv.GoToolPath(t), []string{"go"}, &os.ProcAttr{
|
||||||
|
Sys: &syscall.SysProcAttr{
|
||||||
|
PidFD: &pidfd,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("starting test process: %v", err)
|
||||||
|
}
|
||||||
|
defer syscall.Close(pidfd)
|
||||||
|
|
||||||
|
if _, err := p.Wait(); err != nil {
|
||||||
|
t.Fatalf("Wait: got %v, want <nil>", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the pidfd is still valid
|
||||||
|
err = unix.PidFDSendSignal(uintptr(pidfd), syscall.Signal(0))
|
||||||
|
if !errors.Is(err, syscall.ESRCH) {
|
||||||
|
t.Errorf("SendSignal: got %v, want %v", err, syscall.ESRCH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue #69284
|
||||||
|
func TestPidfdLeak(t *testing.T) {
|
||||||
|
testenv.MustHaveExec(t)
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next 10 descriptors.
|
||||||
|
// We need to get more than one descriptor in practice;
|
||||||
|
// the pidfd winds up not being the next descriptor.
|
||||||
|
const count = 10
|
||||||
|
want := make([]int, count)
|
||||||
|
for i := range count {
|
||||||
|
var err error
|
||||||
|
want[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the descriptors.
|
||||||
|
for _, d := range want {
|
||||||
|
syscall.Close(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a process 10 times.
|
||||||
|
for range 10 {
|
||||||
|
// For testing purposes this has to be an absolute path.
|
||||||
|
// Otherwise we will fail finding the executable
|
||||||
|
// and won't start a process at all.
|
||||||
|
cmd := exec.Command("/noSuchExecutable")
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the next 10 descriptors again.
|
||||||
|
got := make([]int, count)
|
||||||
|
for i := range count {
|
||||||
|
var err error
|
||||||
|
got[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the descriptors
|
||||||
|
for _, d := range got {
|
||||||
|
syscall.Close(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("got %v", got)
|
||||||
|
t.Logf("want %v", want)
|
||||||
|
|
||||||
|
// Allow some slack for runtime epoll descriptors and the like.
|
||||||
|
if got[count-1] > want[count-1]+5 {
|
||||||
|
t.Errorf("got descriptor %d, want %d", got[count-1], want[count-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,11 +8,11 @@ package os
|
|||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
|
||||||
return sysAttr
|
return sysAttr, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPidfd(_ *syscall.SysProcAttr) (uintptr, bool) {
|
func getPidfd(_ *syscall.SysProcAttr, _ bool) (uintptr, bool) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,15 +14,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
. "os"
|
. "os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/nettest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCopyFileRange(t *testing.T) {
|
func TestCopyFileRange(t *testing.T) {
|
||||||
@ -784,41 +781,3 @@ func testGetPollFDAndNetwork(t *testing.T, proto string) {
|
|||||||
t.Fatalf("server Control error: %v", err)
|
t.Fatalf("server Control error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
|
|
||||||
t.Helper()
|
|
||||||
if !nettest.TestableNetwork(proto) {
|
|
||||||
t.Skipf("%s does not support %q", runtime.GOOS, proto)
|
|
||||||
}
|
|
||||||
|
|
||||||
ln, err := nettest.NewLocalListener(proto)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewLocalListener error: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if ln != nil {
|
|
||||||
ln.Close()
|
|
||||||
}
|
|
||||||
if client != nil {
|
|
||||||
client.Close()
|
|
||||||
}
|
|
||||||
if server != nil {
|
|
||||||
server.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
ch := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
var err error
|
|
||||||
server, err = ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Accept new connection error: %v", err)
|
|
||||||
}
|
|
||||||
ch <- struct{}{}
|
|
||||||
}()
|
|
||||||
client, err = net.Dial(proto, ln.Addr().String())
|
|
||||||
<-ch
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Dial new connection error: %v", err)
|
|
||||||
}
|
|
||||||
return client, server
|
|
||||||
}
|
|
||||||
|
@ -31,10 +31,11 @@ x_cgo_getstackbound(uintptr bounds[2])
|
|||||||
pthread_attr_get_np(pthread_self(), &attr);
|
pthread_attr_get_np(pthread_self(), &attr);
|
||||||
pthread_attr_getstack(&attr, &addr, &size); // low address
|
pthread_attr_getstack(&attr, &addr, &size); // low address
|
||||||
#else
|
#else
|
||||||
// We don't know how to get the current stacks, so assume they are the
|
// We don't know how to get the current stacks, leave it as
|
||||||
// same as the default stack bounds.
|
// 0 and the caller will use an estimate based on the current
|
||||||
pthread_attr_getstacksize(&attr, &size);
|
// SP.
|
||||||
addr = __builtin_frame_address(0) + 4096 - size;
|
addr = 0;
|
||||||
|
size = 0;
|
||||||
#endif
|
#endif
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
|
|
||||||
|
@ -231,34 +231,6 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
|
|||||||
func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) {
|
func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) {
|
||||||
g0 := mp.g0
|
g0 := mp.g0
|
||||||
|
|
||||||
inBound := sp > g0.stack.lo && sp <= g0.stack.hi
|
|
||||||
if mp.ncgo > 0 && !inBound {
|
|
||||||
// ncgo > 0 indicates that this M was in Go further up the stack
|
|
||||||
// (it called C and is now receiving a callback).
|
|
||||||
//
|
|
||||||
// !inBound indicates that we were called with SP outside the
|
|
||||||
// expected system stack bounds (C changed the stack out from
|
|
||||||
// under us between the cgocall and cgocallback?).
|
|
||||||
//
|
|
||||||
// It is not safe for the C call to change the stack out from
|
|
||||||
// under us, so throw.
|
|
||||||
|
|
||||||
// Note that this case isn't possible for signal == true, as
|
|
||||||
// that is always passing a new M from needm.
|
|
||||||
|
|
||||||
// Stack is bogus, but reset the bounds anyway so we can print.
|
|
||||||
hi := g0.stack.hi
|
|
||||||
lo := g0.stack.lo
|
|
||||||
g0.stack.hi = sp + 1024
|
|
||||||
g0.stack.lo = sp - 32*1024
|
|
||||||
g0.stackguard0 = g0.stack.lo + stackGuard
|
|
||||||
g0.stackguard1 = g0.stackguard0
|
|
||||||
|
|
||||||
print("M ", mp.id, " procid ", mp.procid, " runtime: cgocallback with sp=", hex(sp), " out of bounds [", hex(lo), ", ", hex(hi), "]")
|
|
||||||
print("\n")
|
|
||||||
exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mp.isextra {
|
if !mp.isextra {
|
||||||
// We allocated the stack for standard Ms. Don't replace the
|
// We allocated the stack for standard Ms. Don't replace the
|
||||||
// stack bounds with estimated ones when we already initialized
|
// stack bounds with estimated ones when we already initialized
|
||||||
@ -266,26 +238,37 @@ func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// This M does not have Go further up the stack. However, it may have
|
inBound := sp > g0.stack.lo && sp <= g0.stack.hi
|
||||||
// previously called into Go, initializing the stack bounds. Between
|
if inBound && mp.g0StackAccurate {
|
||||||
// that call returning and now the stack may have changed (perhaps the
|
// This M has called into Go before and has the stack bounds
|
||||||
// C thread is running a coroutine library). We need to update the
|
// initialized. We have the accurate stack bounds, and the SP
|
||||||
// stack bounds for this case.
|
// is in bounds. We expect it continues to run within the same
|
||||||
|
// bounds.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have an accurate stack bounds (either it never calls
|
||||||
|
// into Go before, or we couldn't get the accurate bounds), or the
|
||||||
|
// current SP is not within the previous bounds (the stack may have
|
||||||
|
// changed between calls). We need to update the stack bounds.
|
||||||
//
|
//
|
||||||
// N.B. we need to update the stack bounds even if SP appears to
|
// N.B. we need to update the stack bounds even if SP appears to
|
||||||
// already be in bounds. Our "bounds" may actually be estimated dummy
|
// already be in bounds, if our bounds are estimated dummy bounds
|
||||||
// bounds (below). The actual stack bounds could have shifted but still
|
// (below). We may be in a different region within the same actual
|
||||||
// have partial overlap with our dummy bounds. If we failed to update
|
// stack bounds, but our estimates were not accurate. Or the actual
|
||||||
// in that case, we could find ourselves seemingly called near the
|
// stack bounds could have shifted but still have partial overlap with
|
||||||
// bottom of the stack bounds, where we quickly run out of space.
|
// our dummy bounds. If we failed to update in that case, we could find
|
||||||
|
// ourselves seemingly called near the bottom of the stack bounds, where
|
||||||
|
// we quickly run out of space.
|
||||||
|
|
||||||
// Set the stack bounds to match the current stack. If we don't
|
// Set the stack bounds to match the current stack. If we don't
|
||||||
// actually know how big the stack is, like we don't know how big any
|
// actually know how big the stack is, like we don't know how big any
|
||||||
// scheduling stack is, but we assume there's at least 32 kB. If we
|
// scheduling stack is, but we assume there's at least 32 kB. If we
|
||||||
// can get a more accurate stack bound from pthread, use that, provided
|
// can get a more accurate stack bound from pthread, use that, provided
|
||||||
// it actually contains SP..
|
// it actually contains SP.
|
||||||
g0.stack.hi = sp + 1024
|
g0.stack.hi = sp + 1024
|
||||||
g0.stack.lo = sp - 32*1024
|
g0.stack.lo = sp - 32*1024
|
||||||
|
mp.g0StackAccurate = false
|
||||||
if !signal && _cgo_getstackbound != nil {
|
if !signal && _cgo_getstackbound != nil {
|
||||||
// Don't adjust if called from the signal handler.
|
// Don't adjust if called from the signal handler.
|
||||||
// We are on the signal stack, not the pthread stack.
|
// We are on the signal stack, not the pthread stack.
|
||||||
@ -296,12 +279,16 @@ func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) {
|
|||||||
asmcgocall(_cgo_getstackbound, unsafe.Pointer(&bounds))
|
asmcgocall(_cgo_getstackbound, unsafe.Pointer(&bounds))
|
||||||
// getstackbound is an unsupported no-op on Windows.
|
// getstackbound is an unsupported no-op on Windows.
|
||||||
//
|
//
|
||||||
|
// On Unix systems, if the API to get accurate stack bounds is
|
||||||
|
// not available, it returns zeros.
|
||||||
|
//
|
||||||
// Don't use these bounds if they don't contain SP. Perhaps we
|
// Don't use these bounds if they don't contain SP. Perhaps we
|
||||||
// were called by something not using the standard thread
|
// were called by something not using the standard thread
|
||||||
// stack.
|
// stack.
|
||||||
if bounds[0] != 0 && sp > bounds[0] && sp <= bounds[1] {
|
if bounds[0] != 0 && sp > bounds[0] && sp <= bounds[1] {
|
||||||
g0.stack.lo = bounds[0]
|
g0.stack.lo = bounds[0]
|
||||||
g0.stack.hi = bounds[1]
|
g0.stack.hi = bounds[1]
|
||||||
|
mp.g0StackAccurate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g0.stackguard0 = g0.stack.lo + stackGuard
|
g0.stackguard0 = g0.stack.lo + stackGuard
|
||||||
@ -319,6 +306,8 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sp := gp.m.g0.sched.sp // system sp saved by cgocallback.
|
sp := gp.m.g0.sched.sp // system sp saved by cgocallback.
|
||||||
|
oldStack := gp.m.g0.stack
|
||||||
|
oldAccurate := gp.m.g0StackAccurate
|
||||||
callbackUpdateSystemStack(gp.m, sp, false)
|
callbackUpdateSystemStack(gp.m, sp, false)
|
||||||
|
|
||||||
// The call from C is on gp.m's g0 stack, so we must ensure
|
// The call from C is on gp.m's g0 stack, so we must ensure
|
||||||
@ -338,9 +327,14 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
|
|||||||
// stack. However, since we're returning to an earlier stack frame and
|
// stack. However, since we're returning to an earlier stack frame and
|
||||||
// need to pair with the entersyscall() call made by cgocall, we must
|
// need to pair with the entersyscall() call made by cgocall, we must
|
||||||
// save syscall* and let reentersyscall restore them.
|
// save syscall* and let reentersyscall restore them.
|
||||||
|
//
|
||||||
|
// Note: savedsp and savedbp MUST be held in locals as an unsafe.Pointer.
|
||||||
|
// When we call into Go, the stack is free to be moved. If these locals
|
||||||
|
// aren't visible in the stack maps, they won't get updated properly,
|
||||||
|
// and will end up being stale when restored by reentersyscall.
|
||||||
savedsp := unsafe.Pointer(gp.syscallsp)
|
savedsp := unsafe.Pointer(gp.syscallsp)
|
||||||
savedpc := gp.syscallpc
|
savedpc := gp.syscallpc
|
||||||
savedbp := gp.syscallbp
|
savedbp := unsafe.Pointer(gp.syscallbp)
|
||||||
exitsyscall() // coming out of cgo call
|
exitsyscall() // coming out of cgo call
|
||||||
gp.m.incgo = false
|
gp.m.incgo = false
|
||||||
if gp.m.isextra {
|
if gp.m.isextra {
|
||||||
@ -372,9 +366,15 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
|
|||||||
osPreemptExtEnter(gp.m)
|
osPreemptExtEnter(gp.m)
|
||||||
|
|
||||||
// going back to cgo call
|
// going back to cgo call
|
||||||
reentersyscall(savedpc, uintptr(savedsp), savedbp)
|
reentersyscall(savedpc, uintptr(savedsp), uintptr(savedbp))
|
||||||
|
|
||||||
gp.m.winsyscall = winsyscall
|
gp.m.winsyscall = winsyscall
|
||||||
|
|
||||||
|
// Restore the old g0 stack bounds
|
||||||
|
gp.m.g0.stack = oldStack
|
||||||
|
gp.m.g0.stackguard0 = oldStack.lo + stackGuard
|
||||||
|
gp.m.g0.stackguard1 = gp.m.g0.stackguard0
|
||||||
|
gp.m.g0StackAccurate = oldAccurate
|
||||||
}
|
}
|
||||||
|
|
||||||
func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {
|
func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {
|
||||||
|
@ -208,6 +208,18 @@ func coroswitch_m(gp *g) {
|
|||||||
// directly if possible.
|
// directly if possible.
|
||||||
setGNoWB(&mp.curg, gnext)
|
setGNoWB(&mp.curg, gnext)
|
||||||
setMNoWB(&gnext.m, mp)
|
setMNoWB(&gnext.m, mp)
|
||||||
|
|
||||||
|
// Synchronize with any out-standing goroutine profile. We're about to start
|
||||||
|
// executing, and an invariant of the profiler is that we tryRecordGoroutineProfile
|
||||||
|
// whenever a goroutine is about to start running.
|
||||||
|
//
|
||||||
|
// N.B. We must do this before transitioning to _Grunning but after installing gnext
|
||||||
|
// in curg, so that we have a valid curg for allocation (tryRecordGoroutineProfile
|
||||||
|
// may allocate).
|
||||||
|
if goroutineProfile.active {
|
||||||
|
tryRecordGoroutineProfile(gnext, nil, osyield)
|
||||||
|
}
|
||||||
|
|
||||||
if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) {
|
if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) {
|
||||||
// The CAS failed: use casgstatus, which will take care of
|
// The CAS failed: use casgstatus, which will take care of
|
||||||
// coordinating with the garbage collector about the state change.
|
// coordinating with the garbage collector about the state change.
|
||||||
|
@ -1886,3 +1886,30 @@ func (m *TraceMap) PutString(s string) (uint64, bool) {
|
|||||||
func (m *TraceMap) Reset() {
|
func (m *TraceMap) Reset() {
|
||||||
m.traceMap.reset()
|
m.traceMap.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetSpinInGCMarkDone(spin bool) {
|
||||||
|
gcDebugMarkDone.spinAfterRaggedBarrier.Store(spin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GCMarkDoneRestarted() bool {
|
||||||
|
// Only read this outside of the GC. If we're running during a GC, just report false.
|
||||||
|
mp := acquirem()
|
||||||
|
if gcphase != _GCoff {
|
||||||
|
releasem(mp)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
restarted := gcDebugMarkDone.restartedDueTo27993
|
||||||
|
releasem(mp)
|
||||||
|
return restarted
|
||||||
|
}
|
||||||
|
|
||||||
|
func GCMarkDoneResetRestartFlag() {
|
||||||
|
mp := acquirem()
|
||||||
|
for gcphase != _GCoff {
|
||||||
|
releasem(mp)
|
||||||
|
Gosched()
|
||||||
|
mp = acquirem()
|
||||||
|
}
|
||||||
|
gcDebugMarkDone.restartedDueTo27993 = false
|
||||||
|
releasem(mp)
|
||||||
|
}
|
||||||
|
@ -6,6 +6,8 @@ package runtime_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/testenv"
|
||||||
|
"internal/weak"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
@ -787,3 +789,78 @@ func TestMemoryLimitNoGCPercent(t *testing.T) {
|
|||||||
func TestMyGenericFunc(t *testing.T) {
|
func TestMyGenericFunc(t *testing.T) {
|
||||||
runtime.MyGenericFunc[int]()
|
runtime.MyGenericFunc[int]()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWeakToStrongMarkTermination(t *testing.T) {
|
||||||
|
testenv.MustHaveParallelism(t)
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
a *int
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
|
||||||
|
defer debug.SetGCPercent(debug.SetGCPercent(-1))
|
||||||
|
w := make([]weak.Pointer[T], 2048)
|
||||||
|
|
||||||
|
// Make sure there's no out-standing GC from a previous test.
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
// Create many objects with a weak pointers to them.
|
||||||
|
for i := range w {
|
||||||
|
x := new(T)
|
||||||
|
x.a = new(int)
|
||||||
|
w[i] = weak.Make(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the restart flag.
|
||||||
|
runtime.GCMarkDoneResetRestartFlag()
|
||||||
|
|
||||||
|
// Prevent mark termination from completing.
|
||||||
|
runtime.SetSpinInGCMarkDone(true)
|
||||||
|
|
||||||
|
// Start a GC, and wait a little bit to get something spinning in mark termination.
|
||||||
|
// Simultaneously, fire off another goroutine to disable spinning. If everything's
|
||||||
|
// working correctly, then weak.Strong will block, so we need to make sure something
|
||||||
|
// prevents the GC from continuing to spin.
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
runtime.GC()
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// Let mark termination continue.
|
||||||
|
runtime.SetSpinInGCMarkDone(false)
|
||||||
|
}()
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
|
// Perform many weak->strong conversions in the critical window.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, wp := range w {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
wp.Strong()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the GC completes.
|
||||||
|
<-done
|
||||||
|
|
||||||
|
// Make sure all the weak->strong conversions finish.
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// The bug is triggered if there's still mark work after gcMarkDone stops the world.
|
||||||
|
//
|
||||||
|
// This can manifest in one of two ways today:
|
||||||
|
// - An exceedingly rare crash in mark termination.
|
||||||
|
// - gcMarkDone restarts, as if issue #27993 is at play.
|
||||||
|
//
|
||||||
|
// Check for the latter. This is a fairly controlled environment, so #27993 is very
|
||||||
|
// unlikely to happen (it's already rare to begin with) but we'll always _appear_ to
|
||||||
|
// trigger the same bug if weak->strong conversions aren't properly coordinated with
|
||||||
|
// mark termination.
|
||||||
|
if runtime.GCMarkDoneRestarted() {
|
||||||
|
t.Errorf("gcMarkDone restarted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ const (
|
|||||||
lockRankDefer
|
lockRankDefer
|
||||||
lockRankSweepWaiters
|
lockRankSweepWaiters
|
||||||
lockRankAssistQueue
|
lockRankAssistQueue
|
||||||
|
lockRankStrongFromWeakQueue
|
||||||
lockRankSweep
|
lockRankSweep
|
||||||
lockRankTestR
|
lockRankTestR
|
||||||
lockRankTestW
|
lockRankTestW
|
||||||
@ -90,6 +91,7 @@ var lockNames = []string{
|
|||||||
lockRankDefer: "defer",
|
lockRankDefer: "defer",
|
||||||
lockRankSweepWaiters: "sweepWaiters",
|
lockRankSweepWaiters: "sweepWaiters",
|
||||||
lockRankAssistQueue: "assistQueue",
|
lockRankAssistQueue: "assistQueue",
|
||||||
|
lockRankStrongFromWeakQueue: "strongFromWeakQueue",
|
||||||
lockRankSweep: "sweep",
|
lockRankSweep: "sweep",
|
||||||
lockRankTestR: "testR",
|
lockRankTestR: "testR",
|
||||||
lockRankTestW: "testW",
|
lockRankTestW: "testW",
|
||||||
@ -169,6 +171,7 @@ var lockPartialOrder [][]lockRank = [][]lockRank{
|
|||||||
lockRankDefer: {},
|
lockRankDefer: {},
|
||||||
lockRankSweepWaiters: {},
|
lockRankSweepWaiters: {},
|
||||||
lockRankAssistQueue: {},
|
lockRankAssistQueue: {},
|
||||||
|
lockRankStrongFromWeakQueue: {},
|
||||||
lockRankSweep: {},
|
lockRankSweep: {},
|
||||||
lockRankTestR: {},
|
lockRankTestR: {},
|
||||||
lockRankTestW: {},
|
lockRankTestW: {},
|
||||||
@ -180,11 +183,11 @@ var lockPartialOrder [][]lockRank = [][]lockRank{
|
|||||||
lockRankPollDesc: {},
|
lockRankPollDesc: {},
|
||||||
lockRankWakeableSleep: {},
|
lockRankWakeableSleep: {},
|
||||||
lockRankHchan: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankWakeableSleep, lockRankHchan},
|
lockRankHchan: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankWakeableSleep, lockRankHchan},
|
||||||
lockRankAllocmR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan},
|
lockRankAllocmR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan},
|
||||||
lockRankExecR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan},
|
lockRankExecR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan},
|
||||||
lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR},
|
lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR},
|
||||||
lockRankAllg: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched},
|
lockRankAllg: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched},
|
||||||
lockRankAllp: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched},
|
lockRankAllp: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched},
|
||||||
lockRankNotifyList: {},
|
lockRankNotifyList: {},
|
||||||
lockRankSudog: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList},
|
lockRankSudog: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList},
|
||||||
lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankTimers},
|
lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankTimers},
|
||||||
@ -196,29 +199,29 @@ var lockPartialOrder [][]lockRank = [][]lockRank{
|
|||||||
lockRankUserArenaState: {},
|
lockRankUserArenaState: {},
|
||||||
lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
|
lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
|
||||||
lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf},
|
lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf},
|
||||||
lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankTraceTypeTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankTraceTypeTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankMspanSpecial},
|
lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankMspanSpecial},
|
||||||
lockRankProfInsert: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankProfInsert: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings},
|
||||||
lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive},
|
lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive},
|
||||||
lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture},
|
lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture},
|
||||||
lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
||||||
lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
||||||
lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf},
|
lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf},
|
||||||
lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan},
|
||||||
lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans},
|
lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans},
|
||||||
lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
|
lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
|
||||||
lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial},
|
lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial},
|
||||||
lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
|
lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap},
|
||||||
lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace},
|
lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollCache, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankNotifyList, lockRankSudog, lockRankTimers, lockRankTimer, lockRankNetpollInit, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace},
|
||||||
lockRankPanic: {},
|
lockRankPanic: {},
|
||||||
lockRankDeadlock: {lockRankPanic, lockRankDeadlock},
|
lockRankDeadlock: {lockRankPanic, lockRankDeadlock},
|
||||||
lockRankRaceFini: {lockRankPanic},
|
lockRankRaceFini: {lockRankPanic},
|
||||||
lockRankAllocmRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankAllocmW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR},
|
lockRankAllocmRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankAllocmW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankAllocmR},
|
||||||
lockRankExecRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankExecR},
|
lockRankExecRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankExecR},
|
||||||
lockRankTestRInternal: {lockRankTestR, lockRankTestW},
|
lockRankTestRInternal: {lockRankTestR, lockRankTestW},
|
||||||
}
|
}
|
||||||
|
@ -1209,6 +1209,11 @@ func (h *hmap) sameSizeGrow() bool {
|
|||||||
return h.flags&sameSizeGrow != 0
|
return h.flags&sameSizeGrow != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname sameSizeGrowForIssue69110Test
|
||||||
|
func sameSizeGrowForIssue69110Test(h *hmap) bool {
|
||||||
|
return h.sameSizeGrow()
|
||||||
|
}
|
||||||
|
|
||||||
// noldbuckets calculates the number of buckets prior to the current map growth.
|
// noldbuckets calculates the number of buckets prior to the current map growth.
|
||||||
func (h *hmap) noldbuckets() uintptr {
|
func (h *hmap) noldbuckets() uintptr {
|
||||||
oldB := h.B
|
oldB := h.B
|
||||||
@ -1668,7 +1673,16 @@ func moveToBmap(t *maptype, h *hmap, dst *bmap, pos int, src *bmap) (*bmap, int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mapclone2(t *maptype, src *hmap) *hmap {
|
func mapclone2(t *maptype, src *hmap) *hmap {
|
||||||
dst := makemap(t, src.count, nil)
|
hint := src.count
|
||||||
|
if overLoadFactor(hint, src.B) {
|
||||||
|
// Note: in rare cases (e.g. during a same-sized grow) the map
|
||||||
|
// can be overloaded. Make sure we don't allocate a destination
|
||||||
|
// bucket array larger than the source bucket array.
|
||||||
|
// This will cause the cloned map to be overloaded also,
|
||||||
|
// but that's better than crashing. See issue 69110.
|
||||||
|
hint = int(loadFactorNum * (bucketShift(src.B) / loadFactorDen))
|
||||||
|
}
|
||||||
|
dst := makemap(t, hint, nil)
|
||||||
dst.hash0 = src.hash0
|
dst.hash0 = src.hash0
|
||||||
dst.nevacuate = 0
|
dst.nevacuate = 0
|
||||||
// flags do not need to be copied here, just like a new map has no flags.
|
// flags do not need to be copied here, just like a new map has no flags.
|
||||||
|
@ -190,6 +190,7 @@ func gcinit() {
|
|||||||
work.markDoneSema = 1
|
work.markDoneSema = 1
|
||||||
lockInit(&work.sweepWaiters.lock, lockRankSweepWaiters)
|
lockInit(&work.sweepWaiters.lock, lockRankSweepWaiters)
|
||||||
lockInit(&work.assistQueue.lock, lockRankAssistQueue)
|
lockInit(&work.assistQueue.lock, lockRankAssistQueue)
|
||||||
|
lockInit(&work.strongFromWeak.lock, lockRankStrongFromWeakQueue)
|
||||||
lockInit(&work.wbufSpans.lock, lockRankWbufSpans)
|
lockInit(&work.wbufSpans.lock, lockRankWbufSpans)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,6 +419,26 @@ type workType struct {
|
|||||||
list gList
|
list gList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strongFromWeak controls how the GC interacts with weak->strong
|
||||||
|
// pointer conversions.
|
||||||
|
strongFromWeak struct {
|
||||||
|
// block is a flag set during mark termination that prevents
|
||||||
|
// new weak->strong conversions from executing by blocking the
|
||||||
|
// goroutine and enqueuing it onto q.
|
||||||
|
//
|
||||||
|
// Mutated only by one goroutine at a time in gcMarkDone,
|
||||||
|
// with globally-synchronizing events like forEachP and
|
||||||
|
// stopTheWorld.
|
||||||
|
block bool
|
||||||
|
|
||||||
|
// q is a queue of goroutines that attempted to perform a
|
||||||
|
// weak->strong conversion during mark termination.
|
||||||
|
//
|
||||||
|
// Protected by lock.
|
||||||
|
lock mutex
|
||||||
|
q gQueue
|
||||||
|
}
|
||||||
|
|
||||||
// cycles is the number of completed GC cycles, where a GC
|
// cycles is the number of completed GC cycles, where a GC
|
||||||
// cycle is sweep termination, mark, mark termination, and
|
// cycle is sweep termination, mark, mark termination, and
|
||||||
// sweep. This differs from memstats.numgc, which is
|
// sweep. This differs from memstats.numgc, which is
|
||||||
@ -800,6 +821,19 @@ func gcStart(trigger gcTrigger) {
|
|||||||
// This is protected by markDoneSema.
|
// This is protected by markDoneSema.
|
||||||
var gcMarkDoneFlushed uint32
|
var gcMarkDoneFlushed uint32
|
||||||
|
|
||||||
|
// gcDebugMarkDone contains fields used to debug/test mark termination.
|
||||||
|
var gcDebugMarkDone struct {
|
||||||
|
// spinAfterRaggedBarrier forces gcMarkDone to spin after it executes
|
||||||
|
// the ragged barrier.
|
||||||
|
spinAfterRaggedBarrier atomic.Bool
|
||||||
|
|
||||||
|
// restartedDueTo27993 indicates that we restarted mark termination
|
||||||
|
// due to the bug described in issue #27993.
|
||||||
|
//
|
||||||
|
// Protected by worldsema.
|
||||||
|
restartedDueTo27993 bool
|
||||||
|
}
|
||||||
|
|
||||||
// gcMarkDone transitions the GC from mark to mark termination if all
|
// gcMarkDone transitions the GC from mark to mark termination if all
|
||||||
// reachable objects have been marked (that is, there are no grey
|
// reachable objects have been marked (that is, there are no grey
|
||||||
// objects and can be no more in the future). Otherwise, it flushes
|
// objects and can be no more in the future). Otherwise, it flushes
|
||||||
@ -842,6 +876,10 @@ top:
|
|||||||
// stop the world later, so acquire worldsema now.
|
// stop the world later, so acquire worldsema now.
|
||||||
semacquire(&worldsema)
|
semacquire(&worldsema)
|
||||||
|
|
||||||
|
// Prevent weak->strong conversions from generating additional
|
||||||
|
// GC work. forEachP will guarantee that it is observed globally.
|
||||||
|
work.strongFromWeak.block = true
|
||||||
|
|
||||||
// Flush all local buffers and collect flushedWork flags.
|
// Flush all local buffers and collect flushedWork flags.
|
||||||
gcMarkDoneFlushed = 0
|
gcMarkDoneFlushed = 0
|
||||||
forEachP(waitReasonGCMarkTermination, func(pp *p) {
|
forEachP(waitReasonGCMarkTermination, func(pp *p) {
|
||||||
@ -872,6 +910,10 @@ top:
|
|||||||
goto top
|
goto top
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For debugging/testing.
|
||||||
|
for gcDebugMarkDone.spinAfterRaggedBarrier.Load() {
|
||||||
|
}
|
||||||
|
|
||||||
// There was no global work, no local work, and no Ps
|
// There was no global work, no local work, and no Ps
|
||||||
// communicated work since we took markDoneSema. Therefore
|
// communicated work since we took markDoneSema. Therefore
|
||||||
// there are no grey objects and no more objects can be
|
// there are no grey objects and no more objects can be
|
||||||
@ -910,6 +952,8 @@ top:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if restart {
|
if restart {
|
||||||
|
gcDebugMarkDone.restartedDueTo27993 = true
|
||||||
|
|
||||||
getg().m.preemptoff = ""
|
getg().m.preemptoff = ""
|
||||||
systemstack(func() {
|
systemstack(func() {
|
||||||
// Accumulate the time we were stopped before we had to start again.
|
// Accumulate the time we were stopped before we had to start again.
|
||||||
@ -936,6 +980,11 @@ top:
|
|||||||
// start the world again.
|
// start the world again.
|
||||||
gcWakeAllAssists()
|
gcWakeAllAssists()
|
||||||
|
|
||||||
|
// Wake all blocked weak->strong conversions. These will run
|
||||||
|
// when we start the world again.
|
||||||
|
work.strongFromWeak.block = false
|
||||||
|
gcWakeAllStrongFromWeak()
|
||||||
|
|
||||||
// Likewise, release the transition lock. Blocked
|
// Likewise, release the transition lock. Blocked
|
||||||
// workers and assists will run when we start the
|
// workers and assists will run when we start the
|
||||||
// world again.
|
// world again.
|
||||||
|
@ -2049,8 +2049,19 @@ func internal_weak_runtime_registerWeakPointer(p unsafe.Pointer) unsafe.Pointer
|
|||||||
func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
||||||
handle := (*atomic.Uintptr)(u)
|
handle := (*atomic.Uintptr)(u)
|
||||||
|
|
||||||
// Prevent preemption. We want to make sure that another GC cycle can't start.
|
// Prevent preemption. We want to make sure that another GC cycle can't start
|
||||||
|
// and that work.strongFromWeak.block can't change out from under us.
|
||||||
mp := acquirem()
|
mp := acquirem()
|
||||||
|
|
||||||
|
// Yield to the GC if necessary.
|
||||||
|
if work.strongFromWeak.block {
|
||||||
|
releasem(mp)
|
||||||
|
|
||||||
|
// Try to park and wait for mark termination.
|
||||||
|
// N.B. gcParkStrongFromWeak calls acquirem before returning.
|
||||||
|
mp = gcParkStrongFromWeak()
|
||||||
|
}
|
||||||
|
|
||||||
p := handle.Load()
|
p := handle.Load()
|
||||||
if p == 0 {
|
if p == 0 {
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
@ -2073,14 +2084,67 @@ func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
|||||||
// Even if we just swept some random span that doesn't contain this object, because
|
// Even if we just swept some random span that doesn't contain this object, because
|
||||||
// this object is long dead and its memory has since been reused, we'll just observe nil.
|
// this object is long dead and its memory has since been reused, we'll just observe nil.
|
||||||
ptr := unsafe.Pointer(handle.Load())
|
ptr := unsafe.Pointer(handle.Load())
|
||||||
|
|
||||||
|
// This is responsible for maintaining the same GC-related
|
||||||
|
// invariants as the Yuasa part of the write barrier. During
|
||||||
|
// the mark phase, it's possible that we just created the only
|
||||||
|
// valid pointer to the object pointed to by ptr. If it's only
|
||||||
|
// ever referenced from our stack, and our stack is blackened
|
||||||
|
// already, we could fail to mark it. So, mark it now.
|
||||||
|
if gcphase != _GCoff {
|
||||||
|
shade(uintptr(ptr))
|
||||||
|
}
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
|
|
||||||
|
// Explicitly keep ptr alive. This seems unnecessary since we return ptr,
|
||||||
|
// but let's be explicit since it's important we keep ptr alive across the
|
||||||
|
// call to shade.
|
||||||
|
KeepAlive(ptr)
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gcParkStrongFromWeak puts the current goroutine on the weak->strong queue and parks.
|
||||||
|
func gcParkStrongFromWeak() *m {
|
||||||
|
// Prevent preemption as we check strongFromWeak, so it can't change out from under us.
|
||||||
|
mp := acquirem()
|
||||||
|
|
||||||
|
for work.strongFromWeak.block {
|
||||||
|
lock(&work.strongFromWeak.lock)
|
||||||
|
releasem(mp) // N.B. Holding the lock prevents preemption.
|
||||||
|
|
||||||
|
// Queue ourselves up.
|
||||||
|
work.strongFromWeak.q.pushBack(getg())
|
||||||
|
|
||||||
|
// Park.
|
||||||
|
goparkunlock(&work.strongFromWeak.lock, waitReasonGCWeakToStrongWait, traceBlockGCWeakToStrongWait, 2)
|
||||||
|
|
||||||
|
// Re-acquire the current M since we're going to check the condition again.
|
||||||
|
mp = acquirem()
|
||||||
|
|
||||||
|
// Re-check condition. We may have awoken in the next GC's mark termination phase.
|
||||||
|
}
|
||||||
|
return mp
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcWakeAllStrongFromWeak wakes all currently blocked weak->strong
|
||||||
|
// conversions. This is used at the end of a GC cycle.
|
||||||
|
//
|
||||||
|
// work.strongFromWeak.block must be false to prevent woken goroutines
|
||||||
|
// from immediately going back to sleep.
|
||||||
|
func gcWakeAllStrongFromWeak() {
|
||||||
|
lock(&work.strongFromWeak.lock)
|
||||||
|
list := work.strongFromWeak.q.popList()
|
||||||
|
injectglist(&list)
|
||||||
|
unlock(&work.strongFromWeak.lock)
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieves or creates a weak pointer handle for the object p.
|
// Retrieves or creates a weak pointer handle for the object p.
|
||||||
func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||||
// First try to retrieve without allocating.
|
// First try to retrieve without allocating.
|
||||||
if handle := getWeakHandle(p); handle != nil {
|
if handle := getWeakHandle(p); handle != nil {
|
||||||
|
// Keep p alive for the duration of the function to ensure
|
||||||
|
// that it cannot die while we're trying to do this.
|
||||||
|
KeepAlive(p)
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2105,7 +2169,17 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||||||
scanblock(uintptr(unsafe.Pointer(&s.handle)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
|
scanblock(uintptr(unsafe.Pointer(&s.handle)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
}
|
}
|
||||||
return s.handle
|
|
||||||
|
// Keep p alive for the duration of the function to ensure
|
||||||
|
// that it cannot die while we're trying to do this.
|
||||||
|
//
|
||||||
|
// Same for handle, which is only stored in the special.
|
||||||
|
// There's a window where it might die if we don't keep it
|
||||||
|
// alive explicitly. Returning it here is probably good enough,
|
||||||
|
// but let's be defensive and explicit. See #70455.
|
||||||
|
KeepAlive(p)
|
||||||
|
KeepAlive(handle)
|
||||||
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// There was an existing handle. Free the special
|
// There was an existing handle. Free the special
|
||||||
@ -2124,8 +2198,11 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep p alive for the duration of the function to ensure
|
// Keep p alive for the duration of the function to ensure
|
||||||
// that it cannot die while we're trying to this.
|
// that it cannot die while we're trying to do this.
|
||||||
|
//
|
||||||
|
// Same for handle, just to be defensive.
|
||||||
KeepAlive(p)
|
KeepAlive(p)
|
||||||
|
KeepAlive(handle)
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2154,6 +2231,9 @@ func getWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||||||
unlock(&span.speciallock)
|
unlock(&span.speciallock)
|
||||||
releasem(mp)
|
releasem(mp)
|
||||||
|
|
||||||
|
// Keep p alive for the duration of the function to ensure
|
||||||
|
// that it cannot die while we're trying to do this.
|
||||||
|
KeepAlive(p)
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ NONE < defer;
|
|||||||
NONE <
|
NONE <
|
||||||
sweepWaiters,
|
sweepWaiters,
|
||||||
assistQueue,
|
assistQueue,
|
||||||
|
strongFromWeakQueue,
|
||||||
sweep;
|
sweep;
|
||||||
|
|
||||||
# Test only
|
# Test only
|
||||||
@ -66,6 +67,7 @@ assistQueue,
|
|||||||
hchan,
|
hchan,
|
||||||
pollDesc, # pollDesc can interact with timers, which can lock sched.
|
pollDesc, # pollDesc can interact with timers, which can lock sched.
|
||||||
scavenge,
|
scavenge,
|
||||||
|
strongFromWeakQueue,
|
||||||
sweep,
|
sweep,
|
||||||
sweepWaiters,
|
sweepWaiters,
|
||||||
testR,
|
testR,
|
||||||
|
@ -1136,11 +1136,12 @@ func expandFrames(p []BlockProfileRecord) {
|
|||||||
for i := range p {
|
for i := range p {
|
||||||
cf := CallersFrames(p[i].Stack())
|
cf := CallersFrames(p[i].Stack())
|
||||||
j := 0
|
j := 0
|
||||||
for ; j < len(expandedStack); j++ {
|
for j < len(expandedStack) {
|
||||||
f, more := cf.Next()
|
f, more := cf.Next()
|
||||||
// f.PC is a "call PC", but later consumers will expect
|
// f.PC is a "call PC", but later consumers will expect
|
||||||
// "return PCs"
|
// "return PCs"
|
||||||
expandedStack[j] = f.PC + 1
|
expandedStack[j] = f.PC + 1
|
||||||
|
j++
|
||||||
if !more {
|
if !more {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1270,7 +1271,8 @@ func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok
|
|||||||
// of calling ThreadCreateProfile directly.
|
// of calling ThreadCreateProfile directly.
|
||||||
func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
|
func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
|
||||||
return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
|
return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
|
||||||
copy(p[0].Stack0[:], r.Stack)
|
i := copy(p[0].Stack0[:], r.Stack)
|
||||||
|
clear(p[0].Stack0[i:])
|
||||||
p = p[1:]
|
p = p[1:]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1649,7 +1651,8 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i, mr := range records[0:n] {
|
for i, mr := range records[0:n] {
|
||||||
copy(p[i].Stack0[:], mr.Stack)
|
l := copy(p[i].Stack0[:], mr.Stack)
|
||||||
|
clear(p[i].Stack0[l:])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -881,6 +881,7 @@ func runPerThreadSyscall() {
|
|||||||
const (
|
const (
|
||||||
_SI_USER = 0
|
_SI_USER = 0
|
||||||
_SI_TKILL = -6
|
_SI_TKILL = -6
|
||||||
|
_SYS_SECCOMP = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// sigFromUser reports whether the signal was sent because of a call
|
// sigFromUser reports whether the signal was sent because of a call
|
||||||
@ -892,6 +893,14 @@ func (c *sigctxt) sigFromUser() bool {
|
|||||||
return code == _SI_USER || code == _SI_TKILL
|
return code == _SI_USER || code == _SI_TKILL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sigFromSeccomp reports whether the signal was sent from seccomp.
|
||||||
|
//
|
||||||
|
//go:nosplit
|
||||||
|
func (c *sigctxt) sigFromSeccomp() bool {
|
||||||
|
code := int32(c.sigcode())
|
||||||
|
return code == _SYS_SECCOMP
|
||||||
|
}
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int32) {
|
func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int32) {
|
||||||
r, _, err := syscall.Syscall6(syscall.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)
|
r, _, err := syscall.Syscall6(syscall.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)
|
||||||
|
@ -13,3 +13,10 @@ package runtime
|
|||||||
func (c *sigctxt) sigFromUser() bool {
|
func (c *sigctxt) sigFromUser() bool {
|
||||||
return c.sigcode() == _SI_USER
|
return c.sigcode() == _SI_USER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sigFromSeccomp reports whether the signal was sent from seccomp.
|
||||||
|
//
|
||||||
|
//go:nosplit
|
||||||
|
func (c *sigctxt) sigFromSeccomp() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user