mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Compare commits
91 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a2cfb70b0 | ||
|
|
c72a2bad68 | ||
|
|
2c0a0fc6b9 | ||
|
|
c855149768 | ||
|
|
ec6e84df74 | ||
|
|
15e01a2e43 | ||
|
|
45aade7f1e | ||
|
|
c01c4d41d6 | ||
|
|
25177ecde0 | ||
|
|
e4772831d3 | ||
|
|
9facf1f2c2 | ||
|
|
0fa31cb69f | ||
|
|
d89fda21d5 | ||
|
|
acde84cf1b | ||
|
|
c57e2bd22c | ||
|
|
2aaa388971 | ||
|
|
22fdd35c24 | ||
|
|
a991f9c34d | ||
|
|
6644ed63b1 | ||
|
|
ab44565bcd | ||
|
|
9cbbf5e0f4 | ||
|
|
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 |
163 changed files with 3860 additions and 844 deletions
2
VERSION
Normal file
2
VERSION
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
go1.23.8
|
||||||
|
time 2025-03-26T19:09:39Z
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -28,11 +29,9 @@ func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input
|
||||||
pr := pkgReader{
|
pr := pkgReader{
|
||||||
PkgDecoder: input,
|
PkgDecoder: 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,12 @@ type Func struct {
|
||||||
ABISelf *abi.ABIConfig // ABI for function being compiled
|
ABISelf *abi.ABIConfig // ABI for function being compiled
|
||||||
ABIDefault *abi.ABIConfig // ABI for rtcall and other no-parsed-signature/pragma functions.
|
ABIDefault *abi.ABIConfig // ABI for rtcall and other no-parsed-signature/pragma functions.
|
||||||
|
|
||||||
scheduled bool // Values in Blocks are in final order
|
scheduled bool // Values in Blocks are in final order
|
||||||
laidout bool // Blocks are ordered
|
laidout bool // Blocks are ordered
|
||||||
NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
|
NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
|
||||||
dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
|
dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
|
||||||
IsPgoHot bool
|
IsPgoHot bool
|
||||||
|
HasDeferRangeFunc bool // if true, needs a deferreturn so deferrangefunc can use it for recover() return PC
|
||||||
|
|
||||||
// when register allocation is done, maps value ids to locations
|
// when register allocation is done, maps value ids to locations
|
||||||
RegAlloc []Location
|
RegAlloc []Location
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ func writebarrier(f *Func) {
|
||||||
var start, end int
|
var start, end int
|
||||||
var nonPtrStores int
|
var nonPtrStores int
|
||||||
values := b.Values
|
values := b.Values
|
||||||
|
hasMove := false
|
||||||
FindSeq:
|
FindSeq:
|
||||||
for i := len(values) - 1; i >= 0; i-- {
|
for i := len(values) - 1; i >= 0; i-- {
|
||||||
w := values[i]
|
w := values[i]
|
||||||
|
|
@ -263,6 +264,9 @@ func writebarrier(f *Func) {
|
||||||
end = i + 1
|
end = i + 1
|
||||||
}
|
}
|
||||||
nonPtrStores = 0
|
nonPtrStores = 0
|
||||||
|
if w.Op == OpMoveWB {
|
||||||
|
hasMove = true
|
||||||
|
}
|
||||||
case OpVarDef, OpVarLive:
|
case OpVarDef, OpVarLive:
|
||||||
continue
|
continue
|
||||||
case OpStore:
|
case OpStore:
|
||||||
|
|
@ -273,6 +277,17 @@ func writebarrier(f *Func) {
|
||||||
if nonPtrStores > 2 {
|
if nonPtrStores > 2 {
|
||||||
break FindSeq
|
break FindSeq
|
||||||
}
|
}
|
||||||
|
if hasMove {
|
||||||
|
// We need to ensure that this store happens
|
||||||
|
// before we issue a wbMove, as the wbMove might
|
||||||
|
// use the result of this store as its source.
|
||||||
|
// Even though this store is not write-barrier
|
||||||
|
// eligible, it might nevertheless be the store
|
||||||
|
// of a pointer to the stack, which is then the
|
||||||
|
// source of the move.
|
||||||
|
// See issue 71228.
|
||||||
|
break FindSeq
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if last == nil {
|
if last == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -5390,6 +5390,9 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
|
||||||
callABI = s.f.ABI1
|
callABI = s.f.ABI1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fn := n.Fun.Sym().Name; n.Fun.Sym().Pkg == ir.Pkgs.Runtime && fn == "deferrangefunc" {
|
||||||
|
s.f.HasDeferRangeFunc = true
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
closure = s.expr(fn)
|
closure = s.expr(fn)
|
||||||
|
|
@ -7513,10 +7516,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
||||||
// nop (which will never execute) after the call.
|
// nop (which will never execute) after the call.
|
||||||
Arch.Ginsnop(s.pp)
|
Arch.Ginsnop(s.pp)
|
||||||
}
|
}
|
||||||
if openDeferInfo != nil {
|
if openDeferInfo != nil || f.HasDeferRangeFunc {
|
||||||
// When doing open-coded defers, generate a disconnected call to
|
// When doing open-coded defers, generate a disconnected call to
|
||||||
// deferreturn and a return. This will be used to during panic
|
// deferreturn and a return. This will be used to during panic
|
||||||
// recovery to unwind the stack and return back to the runtime.
|
// recovery to unwind the stack and return back to the runtime.
|
||||||
|
//
|
||||||
|
// deferrangefunc needs to be sure that at least one of these exists;
|
||||||
|
// if all returns are dead-code eliminated, there might not be.
|
||||||
s.pp.NextLive = s.livenessMap.DeferReturn
|
s.pp.NextLive = s.livenessMap.DeferReturn
|
||||||
p := s.pp.Prog(obj.ACALL)
|
p := s.pp.Prog(obj.ACALL)
|
||||||
p.To.Type = obj.TYPE_MEM
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -2898,22 +2898,48 @@ func TestFileVersions(t *testing.T) {
|
||||||
fileVersion string
|
fileVersion string
|
||||||
wantVersion string
|
wantVersion string
|
||||||
}{
|
}{
|
||||||
{"", "", ""}, // 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
|
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
// downgrade to a version before go1.22, where the for loop semantics
|
||||||
// We can do that backwards compatibly.
|
// change was made, while being backwards compatible with versions of
|
||||||
//
|
// go before the new //go:build semantics were introduced.
|
||||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
v = string(versionMax(fileVersion, go1_21))
|
||||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
|
||||||
// That can't be done compatibly in general, since before the
|
|
||||||
// build lines were ignored and code got the module's Go version.
|
|
||||||
// To work around this, downgrades are only allowed when the
|
|
||||||
// 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=
|
||||||
|
|
|
||||||
|
|
@ -662,7 +662,21 @@ func (r *gitRepo) statLocal(ctx context.Context, version, rev string) (*RevInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(info.Tags)
|
|
||||||
|
// Git 2.47.1 does not send the tags during shallow clone anymore
|
||||||
|
// (perhaps the exact version that changed behavior is an earlier one),
|
||||||
|
// so we have to also add tags from the refs list we fetched with ls-remote.
|
||||||
|
if refs, err := r.loadRefs(ctx); err == nil {
|
||||||
|
for ref, h := range refs {
|
||||||
|
if h == hash {
|
||||||
|
if tag, found := strings.CutPrefix(ref, "refs/tags/"); found {
|
||||||
|
info.Tags = append(info.Tags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.Sort(info.Tags)
|
||||||
|
info.Tags = slices.Compact(info.Tags)
|
||||||
|
|
||||||
// Used hash as info.Version above.
|
// Used hash as info.Version above.
|
||||||
// Use caller's suggested version if it appears in the tag list
|
// Use caller's suggested version if it appears in the tag list
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ type PkgSpecial struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var runtimePkgs = []string{
|
var runtimePkgs = []string{
|
||||||
|
// TODO(panjf2000): consider syncing the list inside the
|
||||||
|
// isAsyncSafePoint in preempt.go based on this list?
|
||||||
|
|
||||||
"runtime",
|
"runtime",
|
||||||
|
|
||||||
"internal/runtime/atomic",
|
"internal/runtime/atomic",
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
21
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
21
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
|
|
@ -112,9 +112,24 @@ 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 (
|
||||||
if err != nil {
|
config *telemetry.UploadConfig
|
||||||
return nil, err
|
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 {
|
||||||
|
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.
|
||||||
|
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -126,14 +126,23 @@ GLOBL p256mul<>(SB), 8, $160
|
||||||
#define PH V31
|
#define PH V31
|
||||||
|
|
||||||
#define CAR1 V6
|
#define CAR1 V6
|
||||||
|
|
||||||
|
#define SEL V8
|
||||||
|
#define ZER V9
|
||||||
|
|
||||||
// func p256NegCond(val *p256Point, cond int)
|
// func p256NegCond(val *p256Point, cond int)
|
||||||
TEXT ·p256NegCond(SB), NOSPLIT, $0-16
|
TEXT ·p256NegCond(SB), NOSPLIT, $0-16
|
||||||
MOVD val+0(FP), P1ptr
|
MOVD val+0(FP), P1ptr
|
||||||
MOVD $16, R16
|
MOVD $16, R16
|
||||||
|
|
||||||
MOVD cond+8(FP), R6
|
// Copy cond into SEL (cond is R1 + 8 (cond offset) + 32)
|
||||||
CMP $0, R6
|
MOVD $40, R17
|
||||||
BC 12, 2, LR // just return if cond == 0
|
LXVDSX (R1)(R17), SEL
|
||||||
|
// Zeroize ZER
|
||||||
|
VSPLTISB $0, ZER
|
||||||
|
// SEL controls whether to return the original value (Y1H/Y1L)
|
||||||
|
// or the negated value (T1H/T1L).
|
||||||
|
VCMPEQUD SEL, ZER, SEL
|
||||||
|
|
||||||
MOVD $p256mul<>+0x00(SB), CPOOL
|
MOVD $p256mul<>+0x00(SB), CPOOL
|
||||||
|
|
||||||
|
|
@ -150,6 +159,9 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16
|
||||||
VSUBUQM PL, Y1L, T1L // subtract part2 giving result
|
VSUBUQM PL, Y1L, T1L // subtract part2 giving result
|
||||||
VSUBEUQM PH, Y1H, CAR1, T1H // subtract part1 using carry from part2
|
VSUBEUQM PH, Y1H, CAR1, T1H // subtract part1 using carry from part2
|
||||||
|
|
||||||
|
VSEL T1H, Y1H, SEL, T1H
|
||||||
|
VSEL T1L, Y1L, SEL, T1L
|
||||||
|
|
||||||
XXPERMDI T1H, T1H, $2, T1H
|
XXPERMDI T1H, T1H, $2, T1H
|
||||||
XXPERMDI T1L, T1L, $2, T1L
|
XXPERMDI T1L, T1L, $2, T1L
|
||||||
|
|
||||||
|
|
@ -166,6 +178,8 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16
|
||||||
#undef PL
|
#undef PL
|
||||||
#undef PH
|
#undef PH
|
||||||
#undef CAR1
|
#undef CAR1
|
||||||
|
#undef SEL
|
||||||
|
#undef ZER
|
||||||
|
|
||||||
#define P3ptr R3
|
#define P3ptr R3
|
||||||
#define P1ptr R4
|
#define P1ptr R4
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a
|
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a
|
||||||
golang.org/x/net v0.25.1-0.20240603202750-6249541f2a6c
|
golang.org/x/net v0.25.1-0.20250304182835-b70a9e3eaa27
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a h1:37MIv+iGfwMYzWJECGyrPCtd5nuqcciRUeJfkNCkCf0=
|
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a h1:37MIv+iGfwMYzWJECGyrPCtd5nuqcciRUeJfkNCkCf0=
|
||||||
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/net v0.25.1-0.20240603202750-6249541f2a6c h1:CR/7/SLUhIJw6g675eeoDiwggElO2MV9rGkNYjqi8GM=
|
golang.org/x/net v0.25.1-0.20250304182835-b70a9e3eaa27 h1:BLroQt2NWk69+mgdbJFxbd1Y6nc8r9UCc/iPQ0FgpNs=
|
||||||
golang.org/x/net v0.25.1-0.20240603202750-6249541f2a6c/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.1-0.20250304182835-b70a9e3eaa27/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -2904,22 +2904,48 @@ func TestFileVersions(t *testing.T) {
|
||||||
fileVersion string
|
fileVersion string
|
||||||
wantVersion string
|
wantVersion string
|
||||||
}{
|
}{
|
||||||
{"", "", ""}, // 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
|
// Go 1.21 introduced the feature of setting the go.mod
|
||||||
// (file versions are either the empty string or of the form go1.dd)
|
// go line to an early version of Go and allowing //go:build lines
|
||||||
if pkgVersionOk {
|
// to set the Go version in a given file. Versions Go 1.21 and later
|
||||||
cmp := fileVersion.cmp(check.version)
|
// can be set backwards compatibly as that was the first version
|
||||||
// Go 1.21 introduced the feature of setting the go.mod
|
// files with go1.21 or later build tags could be built with.
|
||||||
// go line to an early version of Go and allowing //go:build lines
|
//
|
||||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||||
// We can do that backwards compatibly.
|
// downgrade to a version before go1.22, where the for loop semantics
|
||||||
//
|
// change was made, while being backwards compatible with versions of
|
||||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
// go before the new //go:build semantics were introduced.
|
||||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
v = string(versionMax(fileVersion, go1_21))
|
||||||
// That can't be done compatibly in general, since before the
|
|
||||||
// build lines were ignored and code got the module's Go version.
|
|
||||||
// To work around this, downgrades are only allowed when the
|
|
||||||
// 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 {
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ var All = []Info{
|
||||||
{Name: "tlsmaxrsasize", Package: "crypto/tls"},
|
{Name: "tlsmaxrsasize", Package: "crypto/tls"},
|
||||||
{Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
{Name: "winreadlinkvolume", Package: "os", Changed: 22, Old: "0"},
|
{Name: "winreadlinkvolume", Package: "os", Changed: 23, Old: "0"},
|
||||||
{Name: "winsymlink", Package: "os", Changed: 22, Old: "0"},
|
{Name: "winsymlink", Package: "os", Changed: 23, Old: "0"},
|
||||||
{Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"},
|
{Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"},
|
||||||
{Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"},
|
{Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"},
|
||||||
{Name: "x509sha1", Package: "crypto/x509"},
|
{Name: "x509sha1", Package: "crypto/x509"},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
// 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
|
||||||
|
} 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
|
||||||
|
// support) and syscall.EINVAL (fd types which
|
||||||
|
// don't implement sendfile), and other errors.
|
||||||
|
// We should end the loop when there is no error
|
||||||
|
// returned from sendfile(2) or it is not a retryable error.
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if err == syscall.EINTR {
|
if err == syscall.EINTR {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// This includes syscall.ENOSYS (no kernel
|
|
||||||
// support) and syscall.EINVAL (fd types which
|
|
||||||
// don't implement sendfile), and other errors.
|
|
||||||
// We should end the loop when there is no error
|
|
||||||
// returned from sendfile(2) or it is not a retryable error.
|
|
||||||
if err != syscall.EAGAIN {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -522,3 +522,26 @@ func ParallelOn64Bit(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CPUProfilingBroken returns true if CPU profiling has known issues on this
|
||||||
|
// platform.
|
||||||
|
func CPUProfilingBroken() bool {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9":
|
||||||
|
// Profiling unimplemented.
|
||||||
|
return true
|
||||||
|
case "aix":
|
||||||
|
// See https://golang.org/issue/45170.
|
||||||
|
return true
|
||||||
|
case "ios", "dragonfly", "netbsd", "illumos", "solaris":
|
||||||
|
// See https://golang.org/issue/13841.
|
||||||
|
return true
|
||||||
|
case "openbsd":
|
||||||
|
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
|
||||||
|
// See https://golang.org/issue/13841.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -613,8 +613,9 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
|
||||||
reqBodyClosed = false // have we closed the current req.Body?
|
reqBodyClosed = false // have we closed the current req.Body?
|
||||||
|
|
||||||
// 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,28 +985,23 @@ 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) {
|
// Permit sending auth/cookie headers from "foo.com"
|
||||||
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
// to "sub.foo.com".
|
||||||
// Permit sending auth/cookie headers from "foo.com"
|
|
||||||
// to "sub.foo.com".
|
|
||||||
|
|
||||||
// Note that we don't send all cookies to subdomains
|
// Note that we don't send all cookies to subdomains
|
||||||
// automatically. This function is only used for
|
// automatically. This function is only used for
|
||||||
// Cookies set explicitly on the initial outgoing
|
// Cookies set explicitly on the initial outgoing
|
||||||
// client request. Cookies automatically added via the
|
// client request. Cookies automatically added via the
|
||||||
// CookieJar mechanism continue to follow each
|
// CookieJar mechanism continue to follow each
|
||||||
// cookie's scope as set by Set-Cookie. But for
|
// cookie's scope as set by Set-Cookie. But for
|
||||||
// outgoing requests with the Cookie header set
|
// outgoing requests with the Cookie header set
|
||||||
// directly, we don't know their scope, so we assume
|
// directly, we don't know their scope, so we assume
|
||||||
// it's for *.domain.com.
|
// it's for *.domain.com.
|
||||||
|
|
||||||
ihost := idnaASCIIFromURL(initial)
|
ihost := idnaASCIIFromURL(initial)
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,19 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC 9112 permits parsers to accept a bare \n as a line ending in headers,
|
||||||
|
// but not in chunked encoding lines. See https://www.rfc-editor.org/errata/eid7633,
|
||||||
|
// which explicitly rejects a clarification permitting \n as a chunk terminator.
|
||||||
|
//
|
||||||
|
// Verify that the line ends in a CRLF, and that no CRs appear before the end.
|
||||||
|
if idx := bytes.IndexByte(p, '\r'); idx == -1 {
|
||||||
|
return nil, errors.New("chunked line ends with bare LF")
|
||||||
|
} else if idx != len(p)-2 {
|
||||||
|
return nil, errors.New("invalid CR in chunked line")
|
||||||
|
}
|
||||||
|
p = p[:len(p)-2] // trim CRLF
|
||||||
|
|
||||||
if len(p) >= maxLineLength {
|
if len(p) >= maxLineLength {
|
||||||
return nil, ErrLineTooLong
|
return nil, ErrLineTooLong
|
||||||
}
|
}
|
||||||
|
|
@ -171,14 +184,14 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimTrailingWhitespace(b []byte) []byte {
|
func trimTrailingWhitespace(b []byte) []byte {
|
||||||
for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
|
for len(b) > 0 && isOWS(b[len(b)-1]) {
|
||||||
b = b[:len(b)-1]
|
b = b[:len(b)-1]
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func isASCIISpace(b byte) bool {
|
func isOWS(b byte) bool {
|
||||||
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
|
return b == ' ' || b == '\t'
|
||||||
}
|
}
|
||||||
|
|
||||||
var semi = []byte(";")
|
var semi = []byte(";")
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,33 @@ func TestChunkReaderByteAtATime(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChunkInvalidInputs(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
b string
|
||||||
|
}{{
|
||||||
|
name: "bare LF in chunk size",
|
||||||
|
b: "1\na\r\n0\r\n",
|
||||||
|
}, {
|
||||||
|
name: "extra LF in chunk size",
|
||||||
|
b: "1\r\r\na\r\n0\r\n",
|
||||||
|
}, {
|
||||||
|
name: "bare LF in chunk data",
|
||||||
|
b: "1\r\na\n0\r\n",
|
||||||
|
}, {
|
||||||
|
name: "bare LF in chunk extension",
|
||||||
|
b: "1;\na\r\n0\r\n",
|
||||||
|
}} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
r := NewChunkedReader(strings.NewReader(test.b))
|
||||||
|
got, err := io.ReadAll(r)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("unexpectedly parsed invalid chunked data:\n%q", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type funcReader struct {
|
type funcReader struct {
|
||||||
f func(iteration int) ([]byte, error)
|
f func(iteration int) ([]byte, error)
|
||||||
i int
|
i int
|
||||||
|
|
|
||||||
|
|
@ -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") }
|
||||||
|
|
|
||||||
|
|
@ -7284,3 +7284,52 @@ func testServerReadAfterHandlerAbort100Continue(t *testing.T, mode testMode) {
|
||||||
readyc <- struct{}{} // server starts reading from the request body
|
readyc <- struct{}{} // server starts reading from the request body
|
||||||
readyc <- struct{}{} // server finishes reading from the request body
|
readyc <- struct{}{} // server finishes reading from the request body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidChunkedBodies(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
b string
|
||||||
|
}{{
|
||||||
|
name: "bare LF in chunk size",
|
||||||
|
b: "1\na\r\n0\r\n\r\n",
|
||||||
|
}, {
|
||||||
|
name: "bare LF at body end",
|
||||||
|
b: "1\r\na\r\n0\r\n\n",
|
||||||
|
}} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
reqc := make(chan error)
|
||||||
|
ts := newClientServerTest(t, http1Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||||
|
got, err := io.ReadAll(r.Body)
|
||||||
|
if err == nil {
|
||||||
|
t.Logf("read body: %q", got)
|
||||||
|
}
|
||||||
|
reqc <- err
|
||||||
|
})).ts
|
||||||
|
|
||||||
|
serverURL, err := url.Parse(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.Dial("tcp", serverURL.Host)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := conn.Write([]byte(
|
||||||
|
"POST / HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"Transfer-Encoding: chunked\r\n" +
|
||||||
|
"Connection: close\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
test.b)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
conn.(*net.TCPConn).CloseWrite()
|
||||||
|
|
||||||
|
if err := <-reqc; err == nil {
|
||||||
|
t.Errorf("server handler: io.ReadAll(r.Body) succeeded, want error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,15 @@ import (
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -24,6 +27,7 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerHelperCommand("pwd", cmdPwd)
|
registerHelperCommand("pwd", cmdPwd)
|
||||||
|
registerHelperCommand("signaltest", cmdSignalTest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdPwd(...string) {
|
func cmdPwd(...string) {
|
||||||
|
|
@ -274,3 +278,55 @@ func TestExplicitPWD(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 71828.
|
||||||
|
func TestSIGCHLD(t *testing.T) {
|
||||||
|
cmd := helperCommand(t, "signaltest")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
t.Logf("%s", out)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmdSignaltest is for TestSIGCHLD.
|
||||||
|
// This runs in a separate process because the bug only happened
|
||||||
|
// the first time that a child process was started.
|
||||||
|
func cmdSignalTest(...string) {
|
||||||
|
chSig := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(chSig, syscall.SIGCHLD)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
c := 0
|
||||||
|
for range chSig {
|
||||||
|
c++
|
||||||
|
fmt.Printf("SIGCHLD %d\n", c)
|
||||||
|
if c > 1 {
|
||||||
|
fmt.Println("too many SIGCHLD signals")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer func() {
|
||||||
|
signal.Reset(syscall.SIGCHLD)
|
||||||
|
close(chSig)
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("os.Executable failed: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(exe, "hang", "200ms")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Printf("failed to run child process: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue