mirror of
https://github.com/golang/go.git
synced 2026-02-07 03:15:05 +03:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc04f3ba1f | ||
|
|
618120c165 | ||
|
|
d803401771 | ||
|
|
9f4433322b | ||
|
|
14074cec72 | ||
|
|
861ba027ea | ||
|
|
c6539796c9 | ||
|
|
0dfdf3262d | ||
|
|
ec6510887c | ||
|
|
9e0b6c1111 | ||
|
|
a2335d052b | ||
|
|
39ac1fbd13 | ||
|
|
23fd10b378 | ||
|
|
6109c07ec4 | ||
|
|
5d5ed57b13 | ||
|
|
2a7adf4ccd | ||
|
|
0618956b31 | ||
|
|
e73130cf45 | ||
|
|
0cc20ecc28 | ||
|
|
8d10cc0261 | ||
|
|
895664482c | ||
|
|
645abfe529 | ||
|
|
4fa773cdef | ||
|
|
f6d844510d | ||
|
|
2614985ef7 | ||
|
|
3747bb2482 | ||
|
|
11728b38dc | ||
|
|
064f34f048 | ||
|
|
a366ed5982 | ||
|
|
4b0e03da0e | ||
|
|
225bcec98b | ||
|
|
19d792c1eb | ||
|
|
00ece11be1 | ||
|
|
4a4127bccc | ||
|
|
9cfe4e258b | ||
|
|
86e9e0ea87 | ||
|
|
0bba4d2fe6 | ||
|
|
823e5d8afa | ||
|
|
11033eac90 | ||
|
|
4580d6dc6d | ||
|
|
62aa93010d | ||
|
|
15b234b830 | ||
|
|
4da9d6aa10 | ||
|
|
4b1c16cc45 | ||
|
|
2553a09e31 | ||
|
|
2833550891 | ||
|
|
d2bcb22ce0 | ||
|
|
d39fd4f2b7 | ||
|
|
f1b38e8b41 | ||
|
|
2b59f4dd19 | ||
|
|
2375ef8df6 | ||
|
|
16c2b3615a | ||
|
|
3c200d6c81 | ||
|
|
43456202a1 | ||
|
|
56ec1163e3 | ||
|
|
ec7bf2007e | ||
|
|
46ab46c7c5 | ||
|
|
ad672e7ce1 | ||
|
|
392548f147 | ||
|
|
bac4eb53d6 | ||
|
|
18033a0b56 |
@@ -1 +1,2 @@
|
||||
branch: master
|
||||
branch: release-branch.go1.19
|
||||
parent-branch: master
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -519,38 +520,13 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||
|
||||
func TestSignalForwarding(t *testing.T) {
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%v\n%s", cmd.Args, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
@@ -561,7 +537,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,32 +548,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
|
||||
}
|
||||
checkSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
// We want to send the process a signal and see if it dies.
|
||||
// Normally the signal goes to the C thread, the Go signal
|
||||
@@ -610,42 +561,27 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
// fail.
|
||||
const tries = 20
|
||||
for i := 0; i < tries; i++ {
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "2")...)
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that the process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatalf("Did not receive OK signal")
|
||||
}
|
||||
|
||||
// Give the program a chance to enter the sleep function.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
err := runSignalForwardingTest(t, "2")
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if expectSignal(t, err, syscall.SIGSEGV) {
|
||||
// If the signal is delivered to a C thread, as expected,
|
||||
// the Go signal handler will disable itself and re-raise
|
||||
// the signal, causing the program to die with SIGSEGV.
|
||||
//
|
||||
// It is also possible that the signal will be
|
||||
// delivered to a Go thread, such as a GC thread.
|
||||
// Currently when the Go runtime sees that a SIGSEGV was
|
||||
// sent from a different program, it first tries to send
|
||||
// the signal to the os/signal API. If nothing is looking
|
||||
// for (or explicitly ignoring) SIGSEGV, then it crashes.
|
||||
// Because the Go runtime is invoked via a c-archive,
|
||||
// it treats this as GOTRACEBACK=crash, meaning that it
|
||||
// dumps a stack trace for all goroutines, which it does
|
||||
// by raising SIGQUIT. The effect is that we will see the
|
||||
// program die with SIGQUIT in that case, not SIGSEGV.
|
||||
if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -653,6 +589,23 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Errorf("program succeeded unexpectedly %d times", tries)
|
||||
}
|
||||
|
||||
func TestSignalForwardingGo(t *testing.T) {
|
||||
// This test fails on darwin-amd64 because of the special
|
||||
// handling of user-generated SIGSEGV signals in fixsigcode in
|
||||
// runtime/signal_darwin_amd64.go.
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
|
||||
t.Skip("not supported on darwin-amd64")
|
||||
}
|
||||
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
err := runSignalForwardingTest(t, "4")
|
||||
|
||||
// Occasionally the signal will be delivered to a C thread,
|
||||
// and the program will crash with SIGSEGV.
|
||||
expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
|
||||
}
|
||||
|
||||
// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
|
||||
// doesn't work on this platform.
|
||||
func checkSignalForwardingTest(t *testing.T) {
|
||||
@@ -667,18 +620,121 @@ func checkSignalForwardingTest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildSignalForwardingTest builds the executable used by the various
|
||||
// signal forwarding tests.
|
||||
func buildSignalForwardingTest(t *testing.T) {
|
||||
if !testWork {
|
||||
t.Cleanup(func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
t.Log(ccArgs)
|
||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runSignalForwardingTest(t *testing.T, arg string) error {
|
||||
t.Logf("%v %s", bin, arg)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], arg)...)
|
||||
|
||||
var out strings.Builder
|
||||
cmd.Stdout = &out
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatal("Did not receive OK signal")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var errsb strings.Builder
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(&errsb, r)
|
||||
}()
|
||||
|
||||
// Give the program a chance to enter the function.
|
||||
// If the program doesn't get there the test will still
|
||||
// pass, although it doesn't quite test what we intended.
|
||||
// This is fine as long as the program normally makes it.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
s := out.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
wg.Wait()
|
||||
s = errsb.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// expectSignal checks that err, the exit status of a test program,
|
||||
// shows a failure due to a specific signal. Returns whether we found
|
||||
// the expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
|
||||
// shows a failure due to a specific signal or two. Returns whether we
|
||||
// found an expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Error("test program succeeded unexpectedly")
|
||||
} else if ee, ok := err.(*exec.ExitError); !ok {
|
||||
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
|
||||
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
|
||||
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
|
||||
} else if !ws.Signaled() || ws.Signal() != sig {
|
||||
t.Errorf("got %v; expected signal %v", ee, sig)
|
||||
} else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
|
||||
if sig2 == 0 {
|
||||
t.Errorf("got %q; expected signal %q", ee, sig1)
|
||||
} else {
|
||||
t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
@@ -1015,14 +1071,14 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||
binArgs := append(cmdToRun(exe), "1")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
binArgs := append(cmdToRun(exe), "3")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ func RunGoroutines() {
|
||||
}
|
||||
}
|
||||
|
||||
// Block blocks the current thread while running Go code.
|
||||
//export Block
|
||||
func Block() {
|
||||
select {}
|
||||
}
|
||||
|
||||
var P *byte
|
||||
|
||||
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||
|
||||
13
misc/cgo/testcarchive/testdata/main5.c
vendored
13
misc/cgo/testcarchive/testdata/main5.c
vendored
@@ -29,10 +29,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
verbose = (argc > 2);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling RunGoroutines\n");
|
||||
}
|
||||
|
||||
Noop();
|
||||
|
||||
switch (test) {
|
||||
@@ -90,6 +86,15 @@ int main(int argc, char** argv) {
|
||||
printf("did not receive SIGPIPE\n");
|
||||
return 0;
|
||||
}
|
||||
case 4: {
|
||||
fprintf(stderr, "OK\n");
|
||||
fflush(stderr);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling Block\n");
|
||||
}
|
||||
Block();
|
||||
}
|
||||
default:
|
||||
printf("Unknown test: %d\n", test);
|
||||
return 0;
|
||||
|
||||
@@ -143,6 +143,10 @@ const (
|
||||
blockSize = 512 // Size of each block in a tar stream
|
||||
nameSize = 100 // Max length of the name field in USTAR format
|
||||
prefixSize = 155 // Max length of the prefix field in USTAR format
|
||||
|
||||
// Max length of a special file (PAX header, GNU long name or link).
|
||||
// This matches the limit used by libarchive.
|
||||
maxSpecialFileSize = 1 << 20
|
||||
)
|
||||
|
||||
// blockPadding computes the number of bytes needed to pad offset up to the
|
||||
|
||||
@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) {
|
||||
continue // This is a meta header affecting the next header
|
||||
case TypeGNULongName, TypeGNULongLink:
|
||||
format.mayOnlyBe(FormatGNU)
|
||||
realname, err := io.ReadAll(tr)
|
||||
realname, err := readSpecialFile(tr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
|
||||
// parsePAX parses PAX headers.
|
||||
// If an extended header (type 'x') is invalid, ErrHeader is returned
|
||||
func parsePAX(r io.Reader) (map[string]string, error) {
|
||||
buf, err := io.ReadAll(r)
|
||||
buf, err := readSpecialFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -828,6 +828,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// readSpecialFile is like io.ReadAll except it returns
|
||||
// ErrFieldTooLong if more than maxSpecialFileSize is read.
|
||||
func readSpecialFile(r io.Reader) ([]byte, error) {
|
||||
buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
|
||||
if len(buf) > maxSpecialFileSize {
|
||||
return nil, ErrFieldTooLong
|
||||
}
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// discard skips n bytes in r, reporting an error if unable to do so.
|
||||
func discard(r io.Reader, n int64) error {
|
||||
// If possible, Seek to the last byte before the end of the data section.
|
||||
|
||||
@@ -6,6 +6,7 @@ package tar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -243,6 +244,9 @@ func TestReader(t *testing.T) {
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-file.tar",
|
||||
err: ErrHeader,
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-large.tar.bz2",
|
||||
err: ErrFieldTooLong,
|
||||
}, {
|
||||
file: "testdata/pax-bad-mtime-file.tar",
|
||||
err: ErrHeader,
|
||||
@@ -625,9 +629,14 @@ func TestReader(t *testing.T) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var fr io.Reader = f
|
||||
if strings.HasSuffix(v.file, ".bz2") {
|
||||
fr = bzip2.NewReader(fr)
|
||||
}
|
||||
|
||||
// Capture all headers and checksums.
|
||||
var (
|
||||
tr = NewReader(f)
|
||||
tr = NewReader(fr)
|
||||
hdrs []*Header
|
||||
chksums []string
|
||||
rdbuf = make([]byte, 8)
|
||||
|
||||
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
Binary file not shown.
@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
|
||||
flag = TypeXHeader
|
||||
}
|
||||
data := buf.String()
|
||||
if len(data) > maxSpecialFileSize {
|
||||
return ErrFieldTooLong
|
||||
}
|
||||
if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
|
||||
return err // Global headers return here
|
||||
}
|
||||
|
||||
@@ -1004,6 +1004,33 @@ func TestIssue12594(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLongHeader(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
h *Header
|
||||
}{{
|
||||
name: "name too long",
|
||||
h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "linkname too long",
|
||||
h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "uname too long",
|
||||
h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "gname too long",
|
||||
h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "PAX header too long",
|
||||
h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
|
||||
}} {
|
||||
w := NewWriter(io.Discard)
|
||||
if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
|
||||
t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testNonEmptyWriter wraps an io.Writer and ensures that
|
||||
// Write is never called with an empty buffer.
|
||||
type testNonEmptyWriter struct{ io.Writer }
|
||||
|
||||
@@ -798,18 +798,18 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
|
||||
// CalleeEffects appends any side effects from evaluating callee to init.
|
||||
func CalleeEffects(init *ir.Nodes, callee ir.Node) {
|
||||
for {
|
||||
init.Append(ir.TakeInit(callee)...)
|
||||
|
||||
switch callee.Op() {
|
||||
case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
|
||||
return // done
|
||||
|
||||
case ir.OCONVNOP:
|
||||
conv := callee.(*ir.ConvExpr)
|
||||
init.Append(ir.TakeInit(conv)...)
|
||||
callee = conv.X
|
||||
|
||||
case ir.OINLCALL:
|
||||
ic := callee.(*ir.InlinedCallExpr)
|
||||
init.Append(ir.TakeInit(ic)...)
|
||||
init.Append(ic.Body.Take()...)
|
||||
callee = ic.SingleResult()
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ var Syms struct {
|
||||
GCWriteBarrier *obj.LSym
|
||||
Goschedguarded *obj.LSym
|
||||
Growslice *obj.LSym
|
||||
Memmove *obj.LSym
|
||||
Msanread *obj.LSym
|
||||
Msanwrite *obj.LSym
|
||||
Msanmove *obj.LSym
|
||||
|
||||
@@ -434,7 +434,7 @@ func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, loc
|
||||
if node.FrameOffset() < 0 {
|
||||
lv.f.Fatalf("Node %v has frameoffset %d\n", node.Sym().Name, node.FrameOffset())
|
||||
}
|
||||
typebits.Set(node.Type(), node.FrameOffset(), args)
|
||||
typebits.SetNoCheck(node.Type(), node.FrameOffset(), args)
|
||||
break
|
||||
}
|
||||
fallthrough // PPARAMOUT in registers acts memory-allocates like an AUTO
|
||||
@@ -1507,7 +1507,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
|
||||
bv := bitvec.New(int32(nptr) * 2)
|
||||
|
||||
for _, p := range abiInfo.InParams() {
|
||||
typebits.Set(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
typebits.SetNoCheck(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
}
|
||||
|
||||
nbitmap := 1
|
||||
@@ -1522,7 +1522,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
|
||||
if fn.Type().NumResults() > 0 {
|
||||
for _, p := range abiInfo.OutParams() {
|
||||
if len(p.Registers) == 0 {
|
||||
typebits.Set(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
typebits.SetNoCheck(p.Type, p.FrameOffset(abiInfo), bv)
|
||||
}
|
||||
}
|
||||
off = objw.BitVec(lsym, off, bv)
|
||||
|
||||
@@ -132,7 +132,9 @@ func (p *noder) processPragmas() {
|
||||
}
|
||||
n := ir.AsNode(typecheck.Lookup(l.local).Def)
|
||||
if n == nil || n.Op() != ir.ONAME {
|
||||
p.errorAt(l.pos, "//go:linkname must refer to declared function or variable")
|
||||
if types.AllowsGoVersion(1, 18) {
|
||||
p.errorAt(l.pos, "//go:linkname must refer to declared function or variable")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if n.Sym().Linkname != "" {
|
||||
|
||||
@@ -25,6 +25,17 @@ func (s *gcSizes) Alignof(T types2.Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.Elem())
|
||||
case *types2.Struct:
|
||||
if t.NumFields() == 0 && types2.IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
// 64-bit-aligned.
|
||||
//
|
||||
// This logic is equivalent to the logic in
|
||||
// cmd/compile/internal/types/size.go:calcStructOffset
|
||||
return 8
|
||||
}
|
||||
|
||||
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||
// field f of x, but at least 1."
|
||||
|
||||
@@ -334,10 +334,6 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
} else { // ir.OMETHEXPR or ir.METHVALUE
|
||||
// Method expression T.M where T is a generic type.
|
||||
se := x.(*ir.SelectorExpr)
|
||||
targs := deref(se.X.Type()).RParams()
|
||||
if len(targs) == 0 {
|
||||
panic("bad")
|
||||
}
|
||||
if x.Op() == ir.OMETHVALUE {
|
||||
rcvrValue = se.X
|
||||
}
|
||||
@@ -348,7 +344,8 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
// of se.Selection, since that will be the type that actually has
|
||||
// the method.
|
||||
recv := deref(se.Selection.Type.Recv().Type)
|
||||
if len(recv.RParams()) == 0 {
|
||||
targs := recv.RParams()
|
||||
if len(targs) == 0 {
|
||||
// The embedded type that actually has the method is not
|
||||
// actually generic, so no need to build a closure.
|
||||
return x
|
||||
@@ -1357,6 +1354,9 @@ func (g *genInst) dictPass(info *instInfo) {
|
||||
}
|
||||
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||
dt := m.(*ir.TypeAssertExpr)
|
||||
if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) {
|
||||
break
|
||||
}
|
||||
if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) {
|
||||
break
|
||||
}
|
||||
@@ -1787,6 +1787,7 @@ func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsub
|
||||
// instantiations have been created.
|
||||
// Also handles writing method expression closures into the dictionaries.
|
||||
func (g *genInst) finalizeSyms() {
|
||||
Outer:
|
||||
for _, d := range g.dictSymsToFinalize {
|
||||
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
|
||||
|
||||
@@ -1856,7 +1857,30 @@ func (g *genInst) finalizeSyms() {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
base.Fatalf("method %s on %v not found", bf.name, rcvr)
|
||||
// We failed to find a method expression needed for this
|
||||
// dictionary. This may happen because we tried to create a
|
||||
// dictionary for an invalid instantiation.
|
||||
//
|
||||
// For example, in test/typeparam/issue54225.go, we attempt to
|
||||
// construct a dictionary for "Node[struct{}].contentLen",
|
||||
// even though "struct{}" does not implement "Value", so it
|
||||
// cannot actually be used as a type argument to "Node".
|
||||
//
|
||||
// The real issue here is we shouldn't be attempting to create
|
||||
// those dictionaries in the first place (e.g., CL 428356),
|
||||
// but that fix is scarier for backporting to Go 1.19. Too
|
||||
// many backport CLs to this code have fixed one issue while
|
||||
// introducing another.
|
||||
//
|
||||
// So as a hack, instead of calling Fatalf, we simply skip
|
||||
// calling objw.Global below, which prevents us from emitting
|
||||
// the broken dictionary. The linker's dead code elimination
|
||||
// should then naturally prune this invalid, unneeded
|
||||
// dictionary. Worst case, if the dictionary somehow *is*
|
||||
// needed by the final executable, we've just turned an ICE
|
||||
// into a link-time missing symbol failure.
|
||||
infoPrint(" ! abandoning dictionary %v; missing method expression %v.%s\n", d.sym.Name, rcvr, bf.name)
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1890,14 +1890,14 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
// the TOC to the appropriate value for that module. But if it returns
|
||||
// directly to the wrapper's caller, nothing will reset it to the correct
|
||||
// value for that function.
|
||||
var call *ir.CallExpr
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(fn.Type())
|
||||
call.IsDDD = fn.Type().IsVariadic()
|
||||
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
|
||||
} else {
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
var call *ir.CallExpr
|
||||
|
||||
if generic && dot.X != nthis {
|
||||
// If there is embedding involved, then we should do the
|
||||
@@ -1982,7 +1982,22 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
typecheck.Stmts(fn.Body)
|
||||
|
||||
if AfterGlobalEscapeAnalysis {
|
||||
inline.InlineCalls(fn)
|
||||
// Inlining the method may reveal closures, which require walking all function bodies
|
||||
// to decide whether to capture free variables by value or by ref. So we only do inline
|
||||
// if the method do not contain any closures, otherwise, the escape analysis may make
|
||||
// dead variables resurrected, and causing liveness analysis confused, see issue #53702.
|
||||
var canInline bool
|
||||
switch x := call.X.(type) {
|
||||
case *ir.Name:
|
||||
canInline = len(x.Func.Closures) == 0
|
||||
case *ir.SelectorExpr:
|
||||
if x.Op() == ir.OMETHEXPR {
|
||||
canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0
|
||||
}
|
||||
}
|
||||
if canInline {
|
||||
inline.InlineCalls(fn)
|
||||
}
|
||||
escape.Batch([]*ir.Func{fn}, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -2092,7 +2092,13 @@
|
||||
|
||||
// Inline small or disjoint runtime.memmove calls with constant length.
|
||||
// See the comment in op Move in genericOps.go for discussion of the type.
|
||||
|
||||
//
|
||||
// Note that we've lost any knowledge of the type and alignment requirements
|
||||
// of the source and destination. We only know the size, and that the type
|
||||
// contains no pointers.
|
||||
// The type of the move is not necessarily v.Args[0].Type().Elem()!
|
||||
// See issue 55122 for details.
|
||||
//
|
||||
// Because expand calls runs after prove, constants useful to this pattern may not appear.
|
||||
// Both versions need to exist; the memory and register variants.
|
||||
//
|
||||
@@ -2100,31 +2106,28 @@
|
||||
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
&& sz >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
=> (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match post-expansion calls, register version.
|
||||
(SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match pre-expansion calls.
|
||||
(SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// De-virtualize late-expanded interface calls into late-expanded static calls.
|
||||
// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
|
||||
|
||||
@@ -355,7 +355,9 @@ var genericOps = []opData{
|
||||
{name: "Load", argLength: 2}, // Load from arg0. arg1=memory
|
||||
{name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value".
|
||||
{name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory.
|
||||
// The source and destination of Move may overlap in some cases. See e.g.
|
||||
// Normally we require that the source and destination of Move do not overlap.
|
||||
// There is an exception when we know all the loads will happen before all
|
||||
// the stores. In that case, overlap is ok. See
|
||||
// memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go)
|
||||
// returns true, we must do all loads before all stores, when lowering Move.
|
||||
// The type of Move is used for the write barrier pass to insert write barriers
|
||||
|
||||
@@ -1362,7 +1362,8 @@ func zeroUpper56Bits(x *Value, depth int) bool {
|
||||
|
||||
// isInlinableMemmove reports whether the given arch performs a Move of the given size
|
||||
// faster than memmove. It will only return true if replacing the memmove with a Move is
|
||||
// safe, either because Move is small or because the arguments are disjoint.
|
||||
// safe, either because Move will do all of its loads before any of its stores, or
|
||||
// because the arguments are known to be disjoint.
|
||||
// This is used as a check for replacing memmove with Move ops.
|
||||
func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
// It is always safe to convert memmove into Move when its arguments are disjoint.
|
||||
@@ -1381,6 +1382,9 @@ func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func IsInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
return isInlinableMemmove(dst, src, sz, c)
|
||||
}
|
||||
|
||||
// logLargeCopy logs the occurrence of a large copy.
|
||||
// The best place to do this is in the rewrite rules where the size of the move is easy to find.
|
||||
@@ -1394,6 +1398,14 @@ func logLargeCopy(v *Value, s int64) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func LogLargeCopy(funcName string, pos src.XPos, s int64) {
|
||||
if s < 128 {
|
||||
return
|
||||
}
|
||||
if logopt.Enabled() {
|
||||
logopt.LogOpt(pos, "copy", "lower", funcName, fmt.Sprintf("%d bytes", s))
|
||||
}
|
||||
}
|
||||
|
||||
// hasSmallRotate reports whether the architecture has rotate instructions
|
||||
// for sizes < 32-bit. This is used to decide whether to promote some rotations.
|
||||
|
||||
@@ -21317,8 +21317,8 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21348,21 +21348,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21392,21 +21391,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21424,18 +21422,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21453,18 +21451,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21482,18 +21480,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21511,12 +21509,12 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func needAlloc(n *ir.Name) bool {
|
||||
func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
s.stksize = 0
|
||||
s.stkptrsize = 0
|
||||
s.stkalign = int64(types.RegSize)
|
||||
fn := s.curfn
|
||||
|
||||
// Mark the PAUTO's unused.
|
||||
@@ -160,6 +161,9 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
}
|
||||
s.stksize += w
|
||||
s.stksize = types.Rnd(s.stksize, n.Type().Alignment())
|
||||
if n.Type().Alignment() > int64(types.RegSize) {
|
||||
s.stkalign = n.Type().Alignment()
|
||||
}
|
||||
if n.Type().HasPointers() {
|
||||
s.stkptrsize = s.stksize
|
||||
lastHasPtr = true
|
||||
@@ -169,8 +173,8 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
n.SetFrameOffset(-s.stksize)
|
||||
}
|
||||
|
||||
s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
|
||||
s.stksize = types.Rnd(s.stksize, s.stkalign)
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, s.stkalign)
|
||||
}
|
||||
|
||||
const maxStackSize = 1 << 30
|
||||
|
||||
@@ -105,6 +105,7 @@ func InitConfig() {
|
||||
ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier")
|
||||
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
|
||||
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
|
||||
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove")
|
||||
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
|
||||
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
|
||||
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
|
||||
@@ -1359,7 +1360,47 @@ func (s *state) zero(t *types.Type, dst *ssa.Value) {
|
||||
}
|
||||
|
||||
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
|
||||
s.moveWhichMayOverlap(t, dst, src, false)
|
||||
}
|
||||
func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverlap bool) {
|
||||
s.instrumentMove(t, dst, src)
|
||||
if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmove(dst, src, t.Size(), s.f.Config) {
|
||||
// Normally, when moving Go values of type T from one location to another,
|
||||
// we don't need to worry about partial overlaps. The two Ts must either be
|
||||
// in disjoint (nonoverlapping) memory or in exactly the same location.
|
||||
// There are 2 cases where this isn't true:
|
||||
// 1) Using unsafe you can arrange partial overlaps.
|
||||
// 2) Since Go 1.17, you can use a cast from a slice to a ptr-to-array.
|
||||
// https://go.dev/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
// This feature can be used to construct partial overlaps of array types.
|
||||
// var a [3]int
|
||||
// p := (*[2]int)(a[:])
|
||||
// q := (*[2]int)(a[1:])
|
||||
// *p = *q
|
||||
// We don't care about solving 1. Or at least, we haven't historically
|
||||
// and no one has complained.
|
||||
// For 2, we need to ensure that if there might be partial overlap,
|
||||
// then we can't use OpMove; we must use memmove instead.
|
||||
// (memmove handles partial overlap by copying in the correct
|
||||
// direction. OpMove does not.)
|
||||
//
|
||||
// Note that we have to be careful here not to introduce a call when
|
||||
// we're marshaling arguments to a call or unmarshaling results from a call.
|
||||
// Cases where this is happening must pass mayOverlap to false.
|
||||
// (Currently this only happens when unmarshaling results of a call.)
|
||||
if t.HasPointers() {
|
||||
s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t), dst, src)
|
||||
// We would have otherwise implemented this move with straightline code,
|
||||
// including a write barrier. Pretend we issue a write barrier here,
|
||||
// so that the write barrier tests work. (Otherwise they'd need to know
|
||||
// the details of IsInlineableMemmove.)
|
||||
s.curfn.SetWBPos(s.peekPos())
|
||||
} else {
|
||||
s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt(types.Types[types.TUINTPTR], t.Size()))
|
||||
}
|
||||
ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size())
|
||||
return
|
||||
}
|
||||
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
|
||||
store.Aux = t
|
||||
s.vars[memVar] = store
|
||||
@@ -1541,6 +1582,36 @@ func (s *state) stmt(n ir.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
// mayOverlap keeps track of whether the LHS and RHS might
|
||||
// refer to overlapping memory.
|
||||
mayOverlap := true
|
||||
if n.Y == nil {
|
||||
// Not a move at all, mayOverlap is not relevant.
|
||||
} else if n.Def {
|
||||
// A variable being defined cannot overlap anything else.
|
||||
mayOverlap = false
|
||||
} else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME {
|
||||
// Two named things never overlap.
|
||||
// (Or they are identical, which we treat as nonoverlapping.)
|
||||
mayOverlap = false
|
||||
} else if n.Y.Op() == ir.ODEREF {
|
||||
p := n.Y.(*ir.StarExpr).X
|
||||
for p.Op() == ir.OCONVNOP {
|
||||
p = p.(*ir.ConvExpr).X
|
||||
}
|
||||
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() {
|
||||
// Pointer fields of strings point to unmodifiable memory.
|
||||
// That memory can't overlap with the memory being written.
|
||||
mayOverlap = false
|
||||
}
|
||||
} else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n.Y.Op() == ir.OCALLINTER {
|
||||
// When copying values out of the return area of a call, we know
|
||||
// the source and destination don't overlap. Importantly, we must
|
||||
// set mayOverlap so we don't introduce a call to memmove while
|
||||
// we still have live data in the argument area.
|
||||
mayOverlap = false
|
||||
}
|
||||
|
||||
// Evaluate RHS.
|
||||
rhs := n.Y
|
||||
if rhs != nil {
|
||||
@@ -1641,7 +1712,7 @@ func (s *state) stmt(n ir.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
s.assign(n.X, r, deref, skip)
|
||||
s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap)
|
||||
|
||||
case ir.OIF:
|
||||
n := n.(*ir.IfStmt)
|
||||
@@ -3564,7 +3635,11 @@ const (
|
||||
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
|
||||
// If deref is true and right == nil, just do left = 0.
|
||||
// skip indicates assignments (at the top level) that can be avoided.
|
||||
// mayOverlap indicates whether left&right might partially overlap in memory. Default is false.
|
||||
func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) {
|
||||
s.assignWhichMayOverlap(left, right, deref, skip, false)
|
||||
}
|
||||
func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool, skip skipMask, mayOverlap bool) {
|
||||
if left.Op() == ir.ONAME && ir.IsBlank(left) {
|
||||
return
|
||||
}
|
||||
@@ -3665,7 +3740,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask
|
||||
if right == nil {
|
||||
s.zero(t, addr)
|
||||
} else {
|
||||
s.move(t, addr, right)
|
||||
s.moveWhichMayOverlap(t, addr, right, mayOverlap)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -7262,7 +7337,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
||||
func defframe(s *State, e *ssafn, f *ssa.Func) {
|
||||
pp := s.pp
|
||||
|
||||
frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize))
|
||||
s.maxarg = types.Rnd(s.maxarg, e.stkalign)
|
||||
frame := s.maxarg + e.stksize
|
||||
if Arch.PadFrame != nil {
|
||||
frame = Arch.PadFrame(frame)
|
||||
}
|
||||
@@ -7700,7 +7776,14 @@ type ssafn struct {
|
||||
strings map[string]*obj.LSym // map from constant string to data symbols
|
||||
stksize int64 // stack size for current frame
|
||||
stkptrsize int64 // prefix of stack containing pointers
|
||||
log bool // print ssa debug to the stdout
|
||||
|
||||
// alignment for current frame.
|
||||
// NOTE: when stkalign > PtrSize, currently this only ensures the offsets of
|
||||
// objects in the stack frame are aligned. The stack pointer is still aligned
|
||||
// only PtrSize.
|
||||
stkalign int64
|
||||
|
||||
log bool // print ssa debug to the stdout
|
||||
}
|
||||
|
||||
// StringData returns a symbol which
|
||||
|
||||
65
src/cmd/compile/internal/test/race.go
Normal file
65
src/cmd/compile/internal/test/race.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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.
|
||||
|
||||
//go:build !compiler_bootstrap
|
||||
// +build !compiler_bootstrap
|
||||
|
||||
package test
|
||||
|
||||
// The racecompile builder only builds packages, but does not build
|
||||
// or run tests. This is a non-test file to hold cases that (used
|
||||
// to) trigger compiler data races, so they will be exercised on
|
||||
// the racecompile builder.
|
||||
//
|
||||
// This package is not imported so functions here are not included
|
||||
// in the actual compiler.
|
||||
|
||||
// Issue 55357: data race when building multiple instantiations of
|
||||
// generic closures with _ parameters.
|
||||
func Issue55357() {
|
||||
type U struct {
|
||||
A int
|
||||
B string
|
||||
C string
|
||||
}
|
||||
var q T55357[U]
|
||||
q.Count()
|
||||
q.List()
|
||||
|
||||
type M struct {
|
||||
A int64
|
||||
B uint32
|
||||
C uint32
|
||||
}
|
||||
var q2 T55357[M]
|
||||
q2.Count()
|
||||
q2.List()
|
||||
}
|
||||
|
||||
type T55357[T any] struct{}
|
||||
|
||||
//go:noinline
|
||||
func (q *T55357[T]) do(w, v bool, fn func(bk []byte, v T) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *T55357[T]) Count() (n int, rerr error) {
|
||||
err := q.do(false, false, func(kb []byte, _ T) error {
|
||||
n++
|
||||
return nil
|
||||
})
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (q *T55357[T]) List() (list []T, rerr error) {
|
||||
var l []T
|
||||
err := q.do(false, true, func(_ []byte, v T) error {
|
||||
l = append(l, v)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
@@ -14,7 +14,16 @@ import (
|
||||
// the first run and then simply copied into bv at the correct offset
|
||||
// on future calls with the same type t.
|
||||
func Set(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
if uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 {
|
||||
set(t, off, bv, false)
|
||||
}
|
||||
|
||||
// SetNoCheck is like Set, but do not check for alignment.
|
||||
func SetNoCheck(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
set(t, off, bv, true)
|
||||
}
|
||||
|
||||
func set(t *types.Type, off int64, bv bitvec.BitVec, skip bool) {
|
||||
if !skip && uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 {
|
||||
base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, uint8(t.Alignment()), off)
|
||||
}
|
||||
if !t.HasPointers() {
|
||||
@@ -72,13 +81,13 @@ func Set(t *types.Type, off int64, bv bitvec.BitVec) {
|
||||
break
|
||||
}
|
||||
for i := int64(0); i < t.NumElem(); i++ {
|
||||
Set(elt, off, bv)
|
||||
set(elt, off, bv, skip)
|
||||
off += elt.Size()
|
||||
}
|
||||
|
||||
case types.TSTRUCT:
|
||||
for _, f := range t.Fields().Slice() {
|
||||
Set(f.Type, off+f.Offset, bv)
|
||||
set(f.Type, off+f.Offset, bv, skip)
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -376,10 +376,10 @@ func runtimeTypes() []*types.Type {
|
||||
typs[142] = newSig(params(typs[7], typs[1], typs[5]), nil)
|
||||
typs[143] = types.NewSlice(typs[7])
|
||||
typs[144] = newSig(params(typs[7], typs[143]), nil)
|
||||
typs[145] = newSig(params(typs[66], typs[66], typs[15]), nil)
|
||||
typs[146] = newSig(params(typs[60], typs[60], typs[15]), nil)
|
||||
typs[147] = newSig(params(typs[62], typs[62], typs[15]), nil)
|
||||
typs[148] = newSig(params(typs[24], typs[24], typs[15]), nil)
|
||||
typs[149] = newSig(params(typs[28], typs[28], typs[15]), nil)
|
||||
typs[145] = newSig(params(typs[66], typs[66], typs[17]), nil)
|
||||
typs[146] = newSig(params(typs[60], typs[60], typs[17]), nil)
|
||||
typs[147] = newSig(params(typs[62], typs[62], typs[17]), nil)
|
||||
typs[148] = newSig(params(typs[24], typs[24], typs[17]), nil)
|
||||
typs[149] = newSig(params(typs[28], typs[28], typs[17]), nil)
|
||||
return typs[:]
|
||||
}
|
||||
|
||||
@@ -259,16 +259,16 @@ func asanwrite(addr, size uintptr)
|
||||
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
|
||||
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
|
||||
|
||||
func libfuzzerTraceCmp1(uint8, uint8, int)
|
||||
func libfuzzerTraceCmp2(uint16, uint16, int)
|
||||
func libfuzzerTraceCmp4(uint32, uint32, int)
|
||||
func libfuzzerTraceCmp8(uint64, uint64, int)
|
||||
func libfuzzerTraceConstCmp1(uint8, uint8, int)
|
||||
func libfuzzerTraceConstCmp2(uint16, uint16, int)
|
||||
func libfuzzerTraceConstCmp4(uint32, uint32, int)
|
||||
func libfuzzerTraceConstCmp8(uint64, uint64, int)
|
||||
func libfuzzerHookStrCmp(string, string, int)
|
||||
func libfuzzerHookEqualFold(string, string, int)
|
||||
func libfuzzerTraceCmp1(uint8, uint8, uint)
|
||||
func libfuzzerTraceCmp2(uint16, uint16, uint)
|
||||
func libfuzzerTraceCmp4(uint32, uint32, uint)
|
||||
func libfuzzerTraceCmp8(uint64, uint64, uint)
|
||||
func libfuzzerTraceConstCmp1(uint8, uint8, uint)
|
||||
func libfuzzerTraceConstCmp2(uint16, uint16, uint)
|
||||
func libfuzzerTraceConstCmp4(uint32, uint32, uint)
|
||||
func libfuzzerTraceConstCmp8(uint64, uint64, uint)
|
||||
func libfuzzerHookStrCmp(string, string, uint)
|
||||
func libfuzzerHookEqualFold(string, string, uint)
|
||||
|
||||
// architecture variants
|
||||
var x86HasPOPCNT bool
|
||||
|
||||
@@ -1928,7 +1928,9 @@ func (w *exportWriter) expr(n ir.Node) {
|
||||
w.op(n.Op())
|
||||
w.pos(n.Pos())
|
||||
w.expr(n.X)
|
||||
w.expr(n.RType)
|
||||
if w.bool(n.RType != nil) {
|
||||
w.expr(n.RType)
|
||||
}
|
||||
if w.bool(n.ITab != nil) {
|
||||
w.expr(n.ITab)
|
||||
}
|
||||
|
||||
@@ -1465,7 +1465,10 @@ func (r *importReader) node() ir.Node {
|
||||
return n
|
||||
|
||||
case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr())
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), nil)
|
||||
if r.bool() {
|
||||
n.RType = r.expr()
|
||||
}
|
||||
if r.bool() {
|
||||
n.ITab = r.expr()
|
||||
}
|
||||
|
||||
@@ -1350,7 +1350,8 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
|
||||
newfields[i].SetNointerface(true)
|
||||
}
|
||||
if f.Nname != nil && ts.Vars != nil {
|
||||
v := ts.Vars[f.Nname.(*ir.Name)]
|
||||
n := f.Nname.(*ir.Name)
|
||||
v := ts.Vars[n]
|
||||
if v != nil {
|
||||
// This is the case where we are
|
||||
// translating the type of the function we
|
||||
@@ -1358,6 +1359,13 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
|
||||
// the subst.ts.vars table, and we want to
|
||||
// change to reference the new dcl.
|
||||
newfields[i].Nname = v
|
||||
} else if ir.IsBlank(n) {
|
||||
// Blank variable is not dcl list. Make a
|
||||
// new one to not share.
|
||||
m := ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
|
||||
m.SetType(n.Type())
|
||||
m.SetTypecheck(1)
|
||||
newfields[i].Nname = m
|
||||
} else {
|
||||
// This is the case where we are
|
||||
// translating the type of a function
|
||||
|
||||
@@ -637,3 +637,40 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue55030(t *testing.T) {
|
||||
// makeSig makes the signature func(typ...)
|
||||
makeSig := func(typ Type) {
|
||||
par := NewVar(nopos, nil, "", typ)
|
||||
params := NewTuple(par)
|
||||
NewSignatureType(nil, nil, nil, params, nil, true)
|
||||
}
|
||||
|
||||
// makeSig must not panic for the following (example) types:
|
||||
// []int
|
||||
makeSig(NewSlice(Typ[Int]))
|
||||
|
||||
// string
|
||||
makeSig(Typ[String])
|
||||
|
||||
// P where P's core type is string
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P string]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
|
||||
}
|
||||
|
||||
// P where P's core type is an (unnamed) slice
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P []int]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
|
||||
}
|
||||
|
||||
// P where P's core type is bytestring (i.e., string or []byte)
|
||||
{
|
||||
t1 := NewTerm(true, Typ[String]) // ~string
|
||||
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
|
||||
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
package types2
|
||||
|
||||
import "cmd/compile/internal/syntax"
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// API
|
||||
@@ -28,16 +31,18 @@ type Signature struct {
|
||||
// NewSignatureType creates a new function type for the given receiver,
|
||||
// receiver type parameters, type parameters, parameters, and results. If
|
||||
// variadic is set, params must hold at least one parameter and the last
|
||||
// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
|
||||
// be empty. If recvTypeParams is non-empty, recv must be non-nil.
|
||||
// parameter's core type must be of unnamed slice or bytestring type.
|
||||
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
|
||||
// non-empty, recv must be non-nil.
|
||||
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
|
||||
if variadic {
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
panic("variadic function must have at least one parameter")
|
||||
}
|
||||
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||
panic("variadic parameter must be of unnamed slice type")
|
||||
core := coreString(params.At(n - 1).typ)
|
||||
if _, ok := core.(*Slice); !ok && !isString(core) {
|
||||
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as core type", core.String()))
|
||||
}
|
||||
}
|
||||
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.elem)
|
||||
case *Struct:
|
||||
if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
|
||||
if len(t.fields) == 0 && IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
@@ -104,7 +104,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
return a
|
||||
}
|
||||
|
||||
func isSyncAtomicAlign64(T Type) bool {
|
||||
func IsSyncAtomicAlign64(T Type) bool {
|
||||
named, ok := T.(*Named)
|
||||
if !ok {
|
||||
return false
|
||||
|
||||
@@ -243,6 +243,7 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node,
|
||||
// confuses about variables lifetime. So making sure those expressions
|
||||
// are ordered correctly here. See issue #52673.
|
||||
orderBlock(&sinit, map[string][]*ir.Name{})
|
||||
walkStmtList(sinit)
|
||||
}
|
||||
init.Append(sinit...)
|
||||
continue
|
||||
|
||||
3
src/cmd/dist/build.go
vendored
3
src/cmd/dist/build.go
vendored
@@ -938,7 +938,8 @@ func packagefile(pkg string) string {
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is the same list as in go/build/syslist.go.
|
||||
// This is the same list as in go/build/syslist.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -3,7 +3,7 @@ module cmd
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0 h1:XgEFTOJTsN3Li0Txfhn2UzsysGJfXIDe7wE07uY7ZfI=
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2 h1:rcanfLhLDA8nozr/K289V1zcntHr3V+SHlXwzz1ZI2g=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU=
|
||||
|
||||
@@ -174,8 +174,8 @@ func ExtraEnvVars() []cfg.EnvVar {
|
||||
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
|
||||
// but are costly to evaluate.
|
||||
func ExtraEnvVarsCostly() []cfg.EnvVar {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
|
||||
if err != nil {
|
||||
// Should not happen - b.CFlags was given an empty package.
|
||||
|
||||
@@ -20,6 +20,7 @@ package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/cfg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build/constraint"
|
||||
@@ -201,17 +202,22 @@ func matchTag(name string, tags map[string]bool, prefer bool) bool {
|
||||
return prefer
|
||||
}
|
||||
|
||||
have := tags[name]
|
||||
if name == "linux" {
|
||||
have = have || tags["android"]
|
||||
if tags[name] {
|
||||
return true
|
||||
}
|
||||
if name == "solaris" {
|
||||
have = have || tags["illumos"]
|
||||
|
||||
switch name {
|
||||
case "linux":
|
||||
return tags["android"]
|
||||
case "solaris":
|
||||
return tags["illumos"]
|
||||
case "darwin":
|
||||
return tags["ios"]
|
||||
case "unix":
|
||||
return unixOS[cfg.BuildContext.GOOS]
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if name == "darwin" {
|
||||
have = have || tags["ios"]
|
||||
}
|
||||
return have
|
||||
}
|
||||
|
||||
// eval is like
|
||||
@@ -322,6 +328,24 @@ var KnownOS = map[string]bool{
|
||||
"zos": true,
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This is the same list as in go/build/syslist.go and cmd/dist/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
"darwin": true,
|
||||
"dragonfly": true,
|
||||
"freebsd": true,
|
||||
"hurd": true,
|
||||
"illumos": true,
|
||||
"ios": true,
|
||||
"linux": true,
|
||||
"netbsd": true,
|
||||
"openbsd": true,
|
||||
"solaris": true,
|
||||
}
|
||||
|
||||
var KnownArch = map[string]bool{
|
||||
"386": true,
|
||||
"amd64": true,
|
||||
|
||||
@@ -36,6 +36,9 @@ func loadTags() map[string]bool {
|
||||
for _, tag := range cfg.BuildContext.BuildTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ToolTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ReleaseTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
|
||||
@@ -689,8 +689,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
// Do we need to run a build to gather information?
|
||||
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
|
||||
if needStale || *listExport || *listCompiled {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.IsCmdList = true
|
||||
b.NeedExport = *listExport
|
||||
b.NeedCompiledGoFiles = *listCompiled
|
||||
|
||||
@@ -37,7 +37,9 @@ const (
|
||||
// A Repo represents a code hosting source.
|
||||
// Typical implementations include local version control repositories,
|
||||
// remote version control servers, and code hosting sites.
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines.
|
||||
//
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines,
|
||||
// and callers must not modify returned values, which may be cached and shared.
|
||||
type Repo interface {
|
||||
// CheckReuse checks whether the old origin information
|
||||
// remains up to date. If so, whatever cached object it was
|
||||
|
||||
@@ -348,12 +348,21 @@ func (r *gitRepo) Latest() (*RevInfo, error) {
|
||||
if refs["HEAD"] == "" {
|
||||
return nil, ErrNoCommits
|
||||
}
|
||||
info, err := r.Stat(refs["HEAD"])
|
||||
statInfo, err := r.Stat(refs["HEAD"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stat may return cached info, so make a copy to modify here.
|
||||
info := new(RevInfo)
|
||||
*info = *statInfo
|
||||
info.Origin = new(Origin)
|
||||
if statInfo.Origin != nil {
|
||||
*info.Origin = *statInfo.Origin
|
||||
}
|
||||
info.Origin.Ref = "HEAD"
|
||||
info.Origin.Hash = refs["HEAD"]
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -560,7 +569,7 @@ func (r *gitRepo) fetchRefsLocked() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||
// statLocal returns a new RevInfo describing rev in the local git repository.
|
||||
// It uses version as info.Version.
|
||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "--no-decorate", "-n1", "--format=format:%H %ct %D", rev, "--")
|
||||
|
||||
@@ -182,23 +182,27 @@ func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin {
|
||||
if !m2.Checkable() {
|
||||
return m2
|
||||
}
|
||||
|
||||
merged := new(codehost.Origin)
|
||||
*merged = *m1 // Clone to avoid overwriting fields in cached results.
|
||||
|
||||
if m2.TagSum != "" {
|
||||
if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.TagSum = m2.TagSum
|
||||
m1.TagPrefix = m2.TagPrefix
|
||||
merged.TagSum = m2.TagSum
|
||||
merged.TagPrefix = m2.TagPrefix
|
||||
}
|
||||
if m2.Hash != "" {
|
||||
if m1.Hash != "" && (m1.Hash != m2.Hash || m1.Ref != m2.Ref) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.Hash = m2.Hash
|
||||
m1.Ref = m2.Ref
|
||||
merged.Hash = m2.Hash
|
||||
merged.Ref = m2.Ref
|
||||
}
|
||||
return m1
|
||||
return merged
|
||||
}
|
||||
|
||||
// addVersions fills in m.Versions with the list of known versions.
|
||||
|
||||
@@ -27,7 +27,7 @@ var importTests = []struct {
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/net",
|
||||
err: `module golang.org/x/net@.* found \(v0.0.0-.*\), but does not contain package golang.org/x/net`,
|
||||
err: `module golang.org/x/net@.* found \(v[01]\.\d+\.\d+\), but does not contain package golang.org/x/net`,
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/text",
|
||||
|
||||
@@ -220,6 +220,17 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
return revErr, err
|
||||
}
|
||||
|
||||
mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo {
|
||||
merged := mergeOrigin(rev.Origin, origin)
|
||||
if merged == rev.Origin {
|
||||
return rev
|
||||
}
|
||||
clone := new(modfetch.RevInfo)
|
||||
*clone = *rev
|
||||
clone.Origin = merged
|
||||
return clone
|
||||
}
|
||||
|
||||
lookup := func(v string) (*modfetch.RevInfo, error) {
|
||||
rev, err := repo.Stat(v)
|
||||
// Stat can return a non-nil rev and a non-nil err,
|
||||
@@ -227,7 +238,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
rev.Origin = mergeOrigin(rev.Origin, versions.Origin)
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
if err != nil {
|
||||
return rev, err
|
||||
}
|
||||
@@ -256,12 +267,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
|
||||
return revErr, err
|
||||
}
|
||||
info, err := repo.Stat(current)
|
||||
if info == nil && err != nil {
|
||||
rev, err = repo.Stat(current)
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
info.Origin = mergeOrigin(info.Origin, versions.Origin)
|
||||
return info, err
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
return rev, err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.Print = printStderr
|
||||
|
||||
i := 0
|
||||
|
||||
@@ -744,8 +744,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
if cfg.BuildI {
|
||||
fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
|
||||
@@ -800,7 +799,16 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if !testC || a.Failed {
|
||||
return
|
||||
}
|
||||
b.Init()
|
||||
|
||||
// TODO(bcmills): I have no idea why the Builder must be reset here, but
|
||||
// without this reset dance, TestGoTestDashIDashOWritesBinary fails with
|
||||
// lots of "vet config not found" errors. This was added in CL 5699088,
|
||||
// which had almost no public discussion, a very short commit description,
|
||||
// and left no comment in the code to explain what is going on here. 🤯
|
||||
//
|
||||
// Maybe this has the effect of removing actions that were registered by the
|
||||
// call to CompileAction above?
|
||||
b = work.NewBuilder("")
|
||||
}
|
||||
|
||||
var builds, runs, prints []*work.Action
|
||||
@@ -916,7 +924,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
ensureImport(p, "sync/atomic")
|
||||
}
|
||||
|
||||
buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p])
|
||||
buildTest, runTest, printTest, err := builderTest(b, ctx, pkgOpts, p, allImports[p])
|
||||
if err != nil {
|
||||
str := err.Error()
|
||||
str = strings.TrimPrefix(str, "\n")
|
||||
|
||||
@@ -94,8 +94,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) {
|
||||
base.Fatalf("no packages to vet")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
root := &work.Action{Mode: "go vet"}
|
||||
for _, p := range pkgs {
|
||||
|
||||
@@ -240,7 +240,13 @@ const (
|
||||
ModeVetOnly = 1 << 8
|
||||
)
|
||||
|
||||
func (b *Builder) Init() {
|
||||
// NewBuilder returns a new Builder ready for use.
|
||||
//
|
||||
// If workDir is the empty string, NewBuilder creates a WorkDir if needed
|
||||
// and arranges for it to be removed in case of an unclean exit.
|
||||
func NewBuilder(workDir string) *Builder {
|
||||
b := new(Builder)
|
||||
|
||||
b.Print = func(a ...any) (int, error) {
|
||||
return fmt.Fprint(os.Stderr, a...)
|
||||
}
|
||||
@@ -249,7 +255,9 @@ func (b *Builder) Init() {
|
||||
b.toolIDCache = make(map[string]string)
|
||||
b.buildIDCache = make(map[string]string)
|
||||
|
||||
if cfg.BuildN {
|
||||
if workDir != "" {
|
||||
b.WorkDir = workDir
|
||||
} else if cfg.BuildN {
|
||||
b.WorkDir = "$WORK"
|
||||
} else {
|
||||
tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
|
||||
@@ -306,6 +314,8 @@ func (b *Builder) Init() {
|
||||
base.Exit()
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func CheckGOOSARCHPair(goos, goarch string) error {
|
||||
|
||||
@@ -403,8 +403,7 @@ var RuntimeVersion = runtime.Version()
|
||||
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
BuildInit()
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
|
||||
load.CheckPackageErrors(pkgs)
|
||||
@@ -728,8 +727,8 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
depMode := ModeBuild
|
||||
if cfg.BuildI {
|
||||
depMode = ModeInstall
|
||||
|
||||
@@ -556,10 +556,8 @@ func (ts *testScript) cmdCc(want simpleStatus, args []string) {
|
||||
ts.fatalf("usage: cc args... [&]")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder(ts.workdir)
|
||||
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
|
||||
robustio.RemoveAll(b.WorkDir)
|
||||
}
|
||||
|
||||
// cd changes to a different directory.
|
||||
|
||||
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Regression test for https://go.dev/issue/54712: the "unix" build constraint
|
||||
# was not applied consistently during package loading.
|
||||
|
||||
go list -x -f '{{if .Module}}{{.ImportPath}}{{end}}' -deps .
|
||||
stdout 'example.com/version'
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.19
|
||||
|
||||
require example.com/version v1.1.0
|
||||
-- go.sum --
|
||||
example.com/version v1.1.0 h1:VdPnGmIF1NJrntStkxGrF3L/OfhaL567VzCjncGUgtM=
|
||||
example.com/version v1.1.0/go.mod h1:S7K9BnT4o5wT4PCczXPfWVzpjD4ud4e7AJMQJEgiu2Q=
|
||||
-- main_notunix.go --
|
||||
//go:build !(aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris)
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
|
||||
-- main_unix.go --
|
||||
//go:build unix
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Tests Issue #54468
|
||||
|
||||
[short] skip 'links a test binary'
|
||||
[!race] skip
|
||||
|
||||
go mod tidy
|
||||
go test -c -o=$devnull -race .
|
||||
|
||||
! stderr 'cannot find package'
|
||||
|
||||
-- go.mod --
|
||||
module testrace
|
||||
|
||||
go 1.18
|
||||
|
||||
require rsc.io/sampler v1.0.0
|
||||
-- race_test.go --
|
||||
//go:build race
|
||||
|
||||
package testrace
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "rsc.io/sampler"
|
||||
)
|
||||
|
||||
func TestRaceTag(t *testing.T) {
|
||||
}
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
// See issues 36852, 41409, and 43687.
|
||||
// (Also see golang.org/issue/27348.)
|
||||
func TestAllDependencies(t *testing.T) {
|
||||
t.Skip("TODO(#57009): 1.19.4 contains unreleased changes from vendored modules")
|
||||
|
||||
goBin := testenv.GoToolPath(t)
|
||||
|
||||
// Ensure that all packages imported within GOROOT
|
||||
|
||||
@@ -1842,3 +1842,82 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestIssue54320(t *testing.T) {
|
||||
// Check that when trampolines are used, the DWARF LPT is correctly
|
||||
// emitted in the final binary
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const prog = `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello world\n");
|
||||
}
|
||||
`
|
||||
|
||||
dir := t.TempDir()
|
||||
f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
|
||||
defer f.Close()
|
||||
|
||||
d, err := f.DWARF()
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
|
||||
rdr := d.Reader()
|
||||
found := false
|
||||
var entry *dwarf.Entry
|
||||
for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
if entry.Tag != dwarf.TagCompileUnit {
|
||||
continue
|
||||
}
|
||||
name, _ := entry.Val(dwarf.AttrName).(string)
|
||||
if name == "main" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
rdr.SkipChildren()
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("could not find main compile unit")
|
||||
}
|
||||
lr, err := d.LineReader(entry)
|
||||
if err != nil {
|
||||
t.Fatalf("error obtaining linereader: %v", err)
|
||||
}
|
||||
|
||||
var le dwarf.LineEntry
|
||||
found = false
|
||||
for {
|
||||
if err := lr.Next(&le); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatalf("error reading linentry: %v", err)
|
||||
}
|
||||
// check LE contains an entry to test.go
|
||||
if le.File == nil {
|
||||
continue
|
||||
}
|
||||
file := filepath.Base(le.File.Name)
|
||||
if file == "test.go" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("no LPT entries for test.go")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1423,7 +1423,6 @@ func (ctxt *Link) hostlink() {
|
||||
if ctxt.HeadType == objabi.Hdarwin {
|
||||
if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
|
||||
argv = append(argv, "-Wl,-no_pie")
|
||||
argv = append(argv, "-Wl,-pagezero_size,4000000")
|
||||
}
|
||||
}
|
||||
if *flagRace && ctxt.HeadType == objabi.Hwindows {
|
||||
@@ -1775,6 +1774,13 @@ func (ctxt *Link) hostlink() {
|
||||
if len(out) > 0 {
|
||||
// always print external output even if the command is successful, so that we don't
|
||||
// swallow linker warnings (see https://golang.org/issue/17935).
|
||||
if ctxt.IsDarwin() && ctxt.IsAMD64() {
|
||||
const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n"
|
||||
if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 {
|
||||
// swallow -no_pie deprecation warning, issue 54482
|
||||
out = append(out[:i], out[i+len(noPieWarning):]...)
|
||||
}
|
||||
}
|
||||
ctxt.Logf("%s", out)
|
||||
}
|
||||
|
||||
|
||||
@@ -1610,13 +1610,8 @@ func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, a
|
||||
if l.SymType(fnSymIdx) != sym.STEXT {
|
||||
log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
|
||||
}
|
||||
if l.IsExternal(fnSymIdx) {
|
||||
// Current expectation is that any external function will
|
||||
// not have auxsyms.
|
||||
return
|
||||
}
|
||||
r, li := l.toLocal(fnSymIdx)
|
||||
auxs := r.Auxs(li)
|
||||
r, auxs := l.auxs(fnSymIdx)
|
||||
|
||||
for i := range auxs {
|
||||
a := &auxs[i]
|
||||
switch a.Type() {
|
||||
|
||||
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
@@ -79,7 +79,7 @@ type configMenuEntry struct {
|
||||
}
|
||||
|
||||
// configMenu returns a list of items to add to a menu in the web UI.
|
||||
func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
func configMenu(fname string, u url.URL) []configMenuEntry {
|
||||
// Start with system configs.
|
||||
configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
|
||||
if settings, err := readSettings(fname); err == nil {
|
||||
@@ -91,13 +91,15 @@ func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
result := make([]configMenuEntry, len(configs))
|
||||
lastMatch := -1
|
||||
for i, cfg := range configs {
|
||||
dst, changed := cfg.config.makeURL(url)
|
||||
dst, changed := cfg.config.makeURL(u)
|
||||
if !changed {
|
||||
lastMatch = i
|
||||
}
|
||||
// Use a relative URL to work in presence of stripping/redirects in webui.go.
|
||||
rel := &url.URL{RawQuery: dst.RawQuery, ForceQuery: true}
|
||||
result[i] = configMenuEntry{
|
||||
Name: cfg.Name,
|
||||
URL: dst.String(),
|
||||
URL: rel.String(),
|
||||
UserConfig: (i != 0),
|
||||
}
|
||||
}
|
||||
|
||||
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
@@ -385,6 +385,9 @@ func multilinePrintableName(info *NodeInfo) string {
|
||||
infoCopy := *info
|
||||
infoCopy.Name = escapeForDot(ShortenFunctionName(infoCopy.Name))
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "::", `\n`, -1)
|
||||
// Go type parameters are reported as "[...]" by Go pprof profiles.
|
||||
// Keep this ellipsis rather than replacing with newlines below.
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "[...]", "[…]", -1)
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, ".", `\n`, -1)
|
||||
if infoCopy.File != "" {
|
||||
infoCopy.File = filepath.Base(infoCopy.File)
|
||||
|
||||
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
@@ -205,49 +205,64 @@ func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
|
||||
}
|
||||
}
|
||||
|
||||
var options []demangle.Option
|
||||
options := demanglerModeToOptions(demanglerMode)
|
||||
for _, fn := range prof.Function {
|
||||
demangleSingleFunction(fn, options)
|
||||
}
|
||||
}
|
||||
|
||||
func demanglerModeToOptions(demanglerMode string) []demangle.Option {
|
||||
switch demanglerMode {
|
||||
case "": // demangled, simplified: no parameters, no templates, no return type
|
||||
options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
return []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
case "templates": // demangled, simplified: no parameters, no return type
|
||||
options = []demangle.Option{demangle.NoParams}
|
||||
return []demangle.Option{demangle.NoParams}
|
||||
case "full":
|
||||
options = []demangle.Option{demangle.NoClones}
|
||||
return []demangle.Option{demangle.NoClones}
|
||||
case "none": // no demangling
|
||||
return
|
||||
return []demangle.Option{}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown demanglerMode %s", demanglerMode))
|
||||
}
|
||||
|
||||
func demangleSingleFunction(fn *profile.Function, options []demangle.Option) {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
return // Already demangled.
|
||||
}
|
||||
// Copy the options because they may be updated by the call.
|
||||
o := make([]demangle.Option, len(options))
|
||||
for _, fn := range prof.Function {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
continue // Already demangled.
|
||||
}
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
continue
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
if demanglerMode == "" || demanglerMode == "templates" {
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
return
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
for _, o := range options {
|
||||
switch o {
|
||||
case demangle.NoParams:
|
||||
name = removeMatching(name, '(', ')')
|
||||
}
|
||||
if demanglerMode == "" {
|
||||
case demangle.NoTemplateParams:
|
||||
name = removeMatching(name, '<', '>')
|
||||
}
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
|
||||
// looksLikeDemangledCPlusPlus is a heuristic to decide if a name is
|
||||
// the result of demangling C++. If so, further heuristics will be
|
||||
// applied to simplify the name.
|
||||
func looksLikeDemangledCPlusPlus(demangled string) bool {
|
||||
if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>"
|
||||
// Skip java names of the form "class.<init>".
|
||||
if strings.Contains(demangled, ".<") {
|
||||
return false
|
||||
}
|
||||
// Skip Go names of the form "foo.(*Bar[...]).Method".
|
||||
if strings.Contains(demangled, "]).") {
|
||||
return false
|
||||
}
|
||||
return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -1,4 +1,4 @@
|
||||
# github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
# github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
## explicit; go 1.17
|
||||
github.com/google/pprof/driver
|
||||
github.com/google/pprof/internal/binutils
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"internal/obscuretestdata"
|
||||
"math/big"
|
||||
"net"
|
||||
"runtime"
|
||||
@@ -311,7 +312,7 @@ func TestBoringCertAlgs(t *testing.T) {
|
||||
// Set up some roots, intermediate CAs, and leaf certs with various algorithms.
|
||||
// X_Y is X signed by Y.
|
||||
R1 := boringCert(t, "R1", boringRSAKey(t, 2048), nil, boringCertCA|boringCertFIPSOK)
|
||||
R2 := boringCert(t, "R2", boringRSAKey(t, 4096), nil, boringCertCA)
|
||||
R2 := boringCert(t, "R2", boringRSAKey(t, 512), nil, boringCertCA)
|
||||
|
||||
M1_R1 := boringCert(t, "M1_R1", boringECDSAKey(t, elliptic.P256()), R1, boringCertCA|boringCertFIPSOK)
|
||||
M2_R1 := boringCert(t, "M2_R1", boringECDSAKey(t, elliptic.P224()), R1, boringCertCA)
|
||||
@@ -558,56 +559,56 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
block, _ := pem.Decode([]byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC/zCCAeegAwIBAgIRALHHX/kh4+4zMU9DarzBEcQwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xMTAxMDExNTA0MDVaFw0yMDEyMjkxNTA0
|
||||
MDVaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQCf8fk0N6ieCBX4IOVIfKitt4kGcOQLeimCfsjqqHcysMIVGEtFSM6E
|
||||
4Ay141f/7IqdW0UtIqNb4PXhROID7yDxR284xL6XbCuv/t5hP3UcehYc3hmLiyVd
|
||||
MkZQiZWtfUUJf/1qOtM+ohNg59LRWp4d+6iX0la1JL3EwCIckkNjJ9hQbF7Pb2CS
|
||||
+ES9Yo55KAap8KOblpcR8MBSN38bqnwjfQdCXvOEOjam2HUxKzEFX5MA+fA0me4C
|
||||
ioCcCRLWKl+GoN9F8fABfoZ+T+2eal4DLuO95rXR8SrOIVBh3XFOr/RVhjtXcNVF
|
||||
ZKcvDt6d68V6jAKAYKm5nlj9GPpd4v+rAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBkGA1UdEQQSMBCC
|
||||
DmV4YW1wbGUuZ29sYW5nMA0GCSqGSIb3DQEBCwUAA4IBAQCOoYsVcFCBhboqe3WH
|
||||
dC6V7XXXECmnjh01r8h80yv0NR379nSD3cw2M+HKvaXysWqrl5hjGVKw0vtwD81r
|
||||
V4JzDu7IfIog5m8+QNC+7LqDZsz88vDKOrsoySVOmUCgmCKFXew+LA+eO/iQEJTr
|
||||
7ensddOeXJEp27Ed5vW+kmWW3Qmglc2Gwy8wFrMDIqnrnOzBA4oCnDEgtXJt0zog
|
||||
nRwbfEMAWi1aQRy5dT9KA3SP9mo5SeTFSzGGHiE4s4gHUe7jvsAFF2qgtD6+wH6s
|
||||
z9b6shxnC7g5IlBKhI7SVB/Uqt2ydJ+kH1YbjMcIq6NAM5eNMKgZuJr3+zwsSgwh
|
||||
GNaE
|
||||
-----END CERTIFICATE-----`))
|
||||
block, _ := pem.Decode(obscuretestdata.Rot13([]byte(`
|
||||
-----ORTVA PREGVSVPNGR-----
|
||||
ZVVP/mPPNrrtNjVONtVENYUUK/xu4+4mZH9QnemORpDjQDLWXbMVuipANDRYODNj
|
||||
RwRDZN4TN1HRPuZUDJAgMFOQomNrSj0kZGNkZQRkAGN0ZQInSj0lZQRlZwxkAGN0
|
||||
ZQInZOVkRQNBOtAIONbGO0SwoJHtD28jttRvZN0TPFdTFVo3QDRONDHNN4VOQjNj
|
||||
ttRXNbVONDPs8sx0A6vrPOK4VBIVsXvgg4xTpBDYrvzPsfwddUplfZVITRgSFZ6R
|
||||
4Nl141s/7VdqJ0HgVdAo4CKuEBVQ7lQkE284kY6KoPhi/g5uC3HpruLp3uzYvlIq
|
||||
ZxMDvMJgsHHWs/1dBgZ+buAt59YEJc4q+6vK0yn1WY3RjPVpxxAwW9uDoS7Co2PF
|
||||
+RF9Lb55XNnc8XBoycpE8ZOFA38odajwsDqPKiBRBwnz2UHkXmRSK5ZN+sN0zr4P
|
||||
vbPpPEYJXy+TbA9S8sNOsbM+G+2rny4QYhB95eKE8FeBVIOu3KSBe/EIuwgKpAIS
|
||||
MXpiQg6q68I6wNXNLXz5ayw9TCcq4i+eNtZONNTwHQOBZN4TN1HqQjRO/jDRNjVS
|
||||
bQNGOtAIUFHRQQNXOtteOtRSODpQNGNZOtAIUEZONs8RNwNNZOxTN1HqRDDFZOPP
|
||||
QzI4LJ1joTHhM29fLJ5aZN0TPFdTFVo3QDROPjHNN4VONDPBbLfIpSPOuobdr3JU
|
||||
qP6I7KKKRPzawu01e8u80li0AE379aFQ3pj2Z+UXinKlfJdey5uwTIXj0igjQ81e
|
||||
I4WmQh7VsVbt5z8+DAP+7YdQMfm88iQXBefblFIBzHPtzPXSKrj+YN+rB/vDRWGe
|
||||
7rafqqBrKWRc27Rq5iJ+xzJJ3Dztyp2Tjl8jSeZQVdaeaBmON4bPaQRtgKWg0mbt
|
||||
aEjosRZNJv1nDEl5qG9XN3FC9zb5FrGSFmTTUvR4f4tUHr7wifNSS2dtgQ6+jU6f
|
||||
m9o6fukaP7t5VyOXuV7FIO/Hdg2lqW+xU1LowZpVd6ANZ5rAZXtMhWe3+mjfFtju
|
||||
TAnR
|
||||
-----RAQ PREGVSVPNGR-----`)))
|
||||
testRSA2048Certificate = block.Bytes
|
||||
|
||||
block, _ = pem.Decode([]byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAn/H5NDeonggV+CDlSHyorbeJBnDkC3opgn7I6qh3MrDCFRhL
|
||||
RUjOhOAMteNX/+yKnVtFLSKjW+D14UTiA+8g8UdvOMS+l2wrr/7eYT91HHoWHN4Z
|
||||
i4slXTJGUImVrX1FCX/9ajrTPqITYOfS0VqeHfuol9JWtSS9xMAiHJJDYyfYUGxe
|
||||
z29gkvhEvWKOeSgGqfCjm5aXEfDAUjd/G6p8I30HQl7zhDo2pth1MSsxBV+TAPnw
|
||||
NJnuAoqAnAkS1ipfhqDfRfHwAX6Gfk/tnmpeAy7jvea10fEqziFQYd1xTq/0VYY7
|
||||
V3DVRWSnLw7enevFeowCgGCpuZ5Y/Rj6XeL/qwIDAQABAoIBAQCNpMZifd/vg42h
|
||||
HdCvLuZaYS0R7SunFlpoXEsltGdLFsnp0IfoJZ/ugFQBSAIIfLwMumU6oXA1z7Uv
|
||||
98aIYV61DePrTCDVDFBsHbNmP8JAo8WtbusEbwd5zyoB7LYG2+clkJklWE73KqUq
|
||||
rmI+UJeyScl2Gin7ZTxBXz1WPBk9VwcnwkeaXpgASIBW23fhECM9gnYEEwaBez5T
|
||||
6Me8d1tHtYQv7vsKe7ro9w9/HKrRXejqYKK1LxkhfFriyV+m8LZJZn2nXOa6G3gF
|
||||
Nb8Qk1Uk5PUBENBmyMFJhT4M/uuSq4YtMrrO2gi8Q+fPhuGzc5SshYKRBp0W4P5r
|
||||
mtVCtEFRAoGBAMENBIFLrV2+HsGj0xYFasKov/QPe6HSTR1Hh2IZONp+oK4oszWE
|
||||
jBT4VcnITmpl6tC1Wy4GcrxjNgKIFZAj+1x1LUULdorXkuG8yr0tAhG9zNyfWsSy
|
||||
PrSovC0UVbzr8Jxxla+kQVxEQQqWQxPlEVuL8kXaIDA6Lyt1Hpua2LvPAoGBANQZ
|
||||
c6Lq2T7+BxLxNdi2m8kZzej5kgzBp/XdVsbFWRlebIX2KrFHsrHzT9PUk3DE1vZK
|
||||
M6pzTt94nQhWSkDgCaw1SohElJ3HFIFwcusF1SJAc3pQepd8ug6IYdlpDMLtBj/P
|
||||
/5P6BVUtgo05E4+I/T3iYatmglQxTtlZ0RkSV2llAoGBALOXkKFX7ahPvf0WksDh
|
||||
uTfuFOTPoowgQG0EpgW0wRdCxeg/JLic3lSD0gsttQV2WsRecryWcxaelRg10RmO
|
||||
38BbogmhaF4xvgsSvujOfiZTE8oK1T43M+6NKsIlML3YILbpU/9aJxPWy0s2DqDr
|
||||
cQJhZrlk+pzjBA7Bnf/URdwxAoGAKR/CNw14D+mrL3YLbbiCXiydqxVwxv5pdZdz
|
||||
8thi3TNcsWC4iGURdcVqbfUinVPdJiXe/Kac3WGCeRJaFVgbKAOxLti1RB5MkIhg
|
||||
D8eyupBqk4W1L1gkrxqsdj4TFlxkwMywjl2E2S4YyQ8PBt6V04DoVRZsIKzqz+PF
|
||||
UionPq0CgYBCYXvqioJhPewkOq/Y5wrDBeZW1FQK5QD9W5M8/5zxd4rdvJtjhbJp
|
||||
oOrtvMdrl6upy9Hz4BJD3FXwVFiPFE7jqeNqi0F21viLxBPMMD3UODF6LL5EyLiR
|
||||
9V4xVMS8KXxvg7rxsuqzMPscViaWUL6WNVBhsD2+92dHxSXzz5EJKQ==
|
||||
-----END RSA PRIVATE KEY-----`))
|
||||
block, _ = pem.Decode(obscuretestdata.Rot13([]byte(`
|
||||
-----ORTVA EFN CEVINGR XRL-----
|
||||
ZVVRcNVONNXPNDRNa/U5AQrbattI+PQyFUlbeorWOaQxP3bcta7V6du3ZeQPSEuY
|
||||
EHwBuBNZgrAK/+lXaIgSYFXwJ+Q14HGvN+8t8HqiBZF+y2jee/7rLG91UUbJUA4M
|
||||
v4fyKGWTHVzIeK1SPK/9nweGCdVGLBsF0IdrUshby9WJgFF9kZNvUWWQLlsLHTkr
|
||||
m29txiuRiJXBrFtTdsPwz5nKRsQNHwq/T6c8V30UDy7muQb2cgu1ZFfkOI+GNCaj
|
||||
AWahNbdNaNxF1vcsudQsEsUjNK6Tsx/gazcrNl7wirn10sRdmvSDLq1kGd/0ILL7
|
||||
I3QIEJFaYj7rariSrbjPtTPchM5L/Ew6KrY/djVQNDNONbVONDPAcZMvsq/it42u
|
||||
UqPiYhMnLF0E7FhaSycbKRfygTqYSfac0VsbWM/htSDOFNVVsYjZhzH6bKN1m7Hi
|
||||
98nVLI61QrCeGPQIQSOfUoAzC8WNb8JgohfRojq5mlbO7YLT2+pyxWxyJR73XdHd
|
||||
ezV+HWrlFpy2Tva7MGkOKm1JCOx9IjpajxrnKctNFVOJ23suRPZ9taLRRjnOrm5G
|
||||
6Zr8q1gUgLDi7ifXr7eb9j9/UXeEKrwdLXX1YkxusSevlI+z8YMWMa2aKBn6T3tS
|
||||
Ao8Dx1Hx5CHORAOzlZSWuG4Z/hhFd4LgZeeB2tv8D+sCuhTmp5FfuLXEOc0J4C5e
|
||||
zgIPgRSENbTONZRAOVSYeI2+UfTw0kLSnfXbi/DCr6UFGE1Uu2VMBAc+bX4bfmJR
|
||||
wOG4IpaVGzcy6gP1Jl4TpekwAtXVSMNw+1k1YHHYqbeKxhT8le0gNuT9mAlsJfFl
|
||||
CeFbiP0HIome8Wkkyn+xDIkRDDdJDkCyRIhY8xKnVQN6Ylg1Uchn2YiCNbTONADM
|
||||
p6Yd2G7+OkYkAqv2z8xMmrw5xtmOc/KqIfoSJEyroVK2XeSUfeUmG9CHx3QR1iMX
|
||||
Z6cmGg94aDuJFxQtPnj1FbuRyW3USVSjphfS1FWNp3cDrcq8ht6VLqycQZYgOw/C
|
||||
/5C6OIHgtb05R4+V/G3vLngztyDkGgyM0ExFI2yyNbTONYBKxXSK7nuCis0JxfQu
|
||||
hGshSBGCbbjtDT0RctJ0jEqPkrt/WYvp3yFQ0tfggDI2JfErpelJpknryEt10EzB
|
||||
38OobtzunS4kitfFihwBsvMGR8bX1G43Z+6AXfVyZY3LVYocH/9nWkCJl0f2QdQe
|
||||
pDWuMeyx+cmwON7Oas/HEqjkNbTNXE/PAj14Q+zeY3LYoovPKvlqdkIjki5cqMqm
|
||||
8guv3GApfJP4vTHEqpIdosHvaICqWvKr/Xnp3JTPrEWnSItoXNBkYgv1EO5ZxVut
|
||||
Q8rlhcOdx4J1Y1txekdfqw4GSykxjZljwy2R2F4LlD8COg6I04QbIEMfVXmdm+CS
|
||||
HvbaCd0PtLOPLKidvbWuCrjxBd/L5jeQOrMJ1SDX5DQ9J5Z8/5mkq4eqiWgwuoWc
|
||||
bBegiZqey6hcl9Um4OWQ3SKjISvCSR7wdrAdv0S21ivYkOCZZQ3HBQS6YY5RlYvE
|
||||
9I4kIZF8XKkit7ekfhdmZCfpIvnJHY6JAIOufQ2+92qUkFKmm5RWXD==
|
||||
-----RAQ EFN CEVINGR XRL-----`)))
|
||||
var err error
|
||||
testRSA2048PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
|
||||
@@ -240,7 +240,7 @@ func (hs *serverHandshakeState) processClientHello() error {
|
||||
|
||||
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
||||
|
||||
if hs.ecdheOk {
|
||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
||||
// Although omitting the ec_point_formats extension is permitted, some
|
||||
// old OpenSSL version will refuse to handshake if not present.
|
||||
//
|
||||
@@ -321,6 +321,13 @@ func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8
|
||||
break
|
||||
}
|
||||
}
|
||||
// Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
|
||||
// missing, uncompressed points are supported. If supportedPoints is empty,
|
||||
// the extension must be missing, as an empty extension body is rejected by
|
||||
// the parser. See https://go.dev/issue/49126.
|
||||
if len(supportedPoints) == 0 {
|
||||
supportsPointFormat = true
|
||||
}
|
||||
|
||||
return supportsCurve && supportsPointFormat
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
|
||||
|
||||
func TestTLSPointFormats(t *testing.T) {
|
||||
// Test that a Server returns the ec_point_format extension when ECC is
|
||||
// negotiated, and not returned on RSA handshake.
|
||||
// negotiated, and not on a RSA handshake or if ec_point_format is missing.
|
||||
tests := []struct {
|
||||
name string
|
||||
cipherSuites []uint16
|
||||
@@ -289,8 +289,11 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
supportedPoints []uint8
|
||||
wantSupportedPoints bool
|
||||
}{
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{compressionNone}, true},
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{pointFormatUncompressed}, true},
|
||||
{"ECC without ec_point_format", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, nil, false},
|
||||
{"ECC with extra values", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{13, 37, pointFormatUncompressed, 42}, true},
|
||||
{"RSA", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, nil, false},
|
||||
{"RSA with ec_point_format", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, []uint8{pointFormatUncompressed}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -330,18 +333,8 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply)
|
||||
}
|
||||
if tt.wantSupportedPoints {
|
||||
if len(serverHello.supportedPoints) < 1 {
|
||||
t.Fatal("missing ec_point_format extension from server")
|
||||
}
|
||||
found := false
|
||||
for _, p := range serverHello.supportedPoints {
|
||||
if p == pointFormatUncompressed {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("missing uncompressed format in ec_point_format extension from server")
|
||||
if !bytes.Equal(serverHello.supportedPoints, []uint8{pointFormatUncompressed}) {
|
||||
t.Fatal("incorrect ec_point_format extension from server")
|
||||
}
|
||||
} else {
|
||||
if len(serverHello.supportedPoints) != 0 {
|
||||
|
||||
@@ -21,12 +21,13 @@ func boringAllowCert(c *Certificate) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// The key must be RSA 2048, RSA 3072, or ECDSA P-256, P-384, or P-521.
|
||||
// The key must be RSA 2048, RSA 3072, RSA 4096,
|
||||
// or ECDSA P-256, P-384, P-521.
|
||||
switch k := c.PublicKey.(type) {
|
||||
default:
|
||||
return false
|
||||
case *rsa.PublicKey:
|
||||
if size := k.N.BitLen(); size != 2048 && size != 3072 {
|
||||
if size := k.N.BitLen(); size != 2048 && size != 3072 && size != 4096 {
|
||||
return false
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
|
||||
@@ -54,7 +54,8 @@ type boringCertificate struct {
|
||||
|
||||
func TestBoringAllowCert(t *testing.T) {
|
||||
R1 := testBoringCert(t, "R1", boringRSAKey(t, 2048), nil, boringCertCA|boringCertFIPSOK)
|
||||
R2 := testBoringCert(t, "R2", boringRSAKey(t, 4096), nil, boringCertCA)
|
||||
R2 := testBoringCert(t, "R2", boringRSAKey(t, 8192), nil, boringCertCA)
|
||||
R3 := testBoringCert(t, "R3", boringRSAKey(t, 4096), nil, boringCertCA|boringCertFIPSOK)
|
||||
|
||||
M1_R1 := testBoringCert(t, "M1_R1", boringECDSAKey(t, elliptic.P256()), R1, boringCertCA|boringCertFIPSOK)
|
||||
M2_R1 := testBoringCert(t, "M2_R1", boringECDSAKey(t, elliptic.P224()), R1, boringCertCA)
|
||||
@@ -64,6 +65,9 @@ func TestBoringAllowCert(t *testing.T) {
|
||||
testBoringCert(t, "I_M1", I_R1.key, M1_R1, boringCertCA|boringCertFIPSOK)
|
||||
testBoringCert(t, "I_M2", I_R1.key, M2_R1, boringCertCA|boringCertFIPSOK)
|
||||
|
||||
I_R3 := testBoringCert(t, "I_R3", boringRSAKey(t, 3072), R3, boringCertCA|boringCertFIPSOK)
|
||||
testBoringCert(t, "I_R3", I_R3.key, R3, boringCertCA|boringCertFIPSOK)
|
||||
|
||||
testBoringCert(t, "L1_I", boringECDSAKey(t, elliptic.P384()), I_R1, boringCertLeaf|boringCertFIPSOK)
|
||||
testBoringCert(t, "L2_I", boringRSAKey(t, 1024), I_R1, boringCertLeaf)
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and
|
||||
// sets the curve ID to the given OID, or omits it if OID is nil.
|
||||
func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) {
|
||||
if !key.Curve.IsOnCurve(key.X, key.Y) {
|
||||
return nil, errors.New("invalid elliptic key public key")
|
||||
}
|
||||
privateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
|
||||
return asn1.Marshal(ecPrivateKey{
|
||||
Version: 1,
|
||||
|
||||
@@ -543,8 +543,8 @@ func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) {
|
||||
func TestGoVerify(t *testing.T) {
|
||||
// Temporarily enable SHA-1 verification since a number of test chains
|
||||
// require it. TODO(filippo): regenerate test chains.
|
||||
defer func(old bool) { debugAllowSHA1 = old }(debugAllowSHA1)
|
||||
debugAllowSHA1 = true
|
||||
t.Setenv("GODEBUG", "x509sha1=1")
|
||||
|
||||
for _, test := range verifyTests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testVerify(t, test, false)
|
||||
|
||||
@@ -84,11 +84,14 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
|
||||
// RFC 3279, Section 2.3.1.
|
||||
publicKeyAlgorithm.Parameters = asn1.NullRawValue
|
||||
case *ecdsa.PublicKey:
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
oid, ok := oidFromNamedCurve(pub.Curve)
|
||||
if !ok {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
|
||||
}
|
||||
if !pub.Curve.IsOnCurve(pub.X, pub.Y) {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: invalid elliptic curve public key")
|
||||
}
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
|
||||
var paramBytes []byte
|
||||
paramBytes, err = asn1.Marshal(oid)
|
||||
@@ -725,9 +728,6 @@ type Certificate struct {
|
||||
// involves algorithms that are not currently implemented.
|
||||
var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented")
|
||||
|
||||
// debugAllowSHA1 allows SHA-1 signatures. See issue 41682.
|
||||
var debugAllowSHA1 = godebug.Get("x509sha1") == "1"
|
||||
|
||||
// An InsecureAlgorithmError indicates that the SignatureAlgorithm used to
|
||||
// generate the signature is not secure, and the signature has been rejected.
|
||||
//
|
||||
@@ -787,7 +787,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) error {
|
||||
|
||||
// TODO(agl): don't ignore the path length constraint.
|
||||
|
||||
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, debugAllowSHA1)
|
||||
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, false)
|
||||
}
|
||||
|
||||
// CheckSignature verifies that signature is a valid signature over signed from
|
||||
@@ -834,7 +834,8 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
|
||||
case crypto.MD5:
|
||||
return InsecureAlgorithmError(algo)
|
||||
case crypto.SHA1:
|
||||
if !allowSHA1 {
|
||||
// SHA-1 signatures are mostly disabled. See go.dev/issue/41682.
|
||||
if !allowSHA1 && godebug.Get("x509sha1") != "1" {
|
||||
return InsecureAlgorithmError(algo)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
@@ -68,6 +68,20 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalInvalidPublicKey(t *testing.T) {
|
||||
_, err := MarshalPKIXPublicKey(&ecdsa.PublicKey{})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
_, err = MarshalPKIXPublicKey(&ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: big.NewInt(1), Y: big.NewInt(2),
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
}
|
||||
|
||||
func testParsePKIXPublicKey(t *testing.T, pemBytes string) (pub any) {
|
||||
block, _ := pem.Decode([]byte(pemBytes))
|
||||
pub, err := ParsePKIXPublicKey(block.Bytes)
|
||||
@@ -1862,9 +1876,7 @@ func TestSHA1(t *testing.T) {
|
||||
t.Fatalf("certificate verification returned %v (%T), wanted InsecureAlgorithmError", err, err)
|
||||
}
|
||||
|
||||
defer func(old bool) { debugAllowSHA1 = old }(debugAllowSHA1)
|
||||
debugAllowSHA1 = true
|
||||
|
||||
t.Setenv("GODEBUG", "x509sha1=1")
|
||||
if err = cert.CheckSignatureFrom(cert); err != nil {
|
||||
t.Fatalf("SHA-1 certificate did not verify with GODEBUG=x509sha1=1: %v", err)
|
||||
}
|
||||
@@ -3456,8 +3468,7 @@ func TestParseUniqueID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDisableSHA1ForCertOnly(t *testing.T) {
|
||||
defer func(old bool) { debugAllowSHA1 = old }(debugAllowSHA1)
|
||||
debugAllowSHA1 = false
|
||||
t.Setenv("GODEBUG", "")
|
||||
|
||||
tmpl := &Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 h1:y+mHpWoQJNAHt26Nhh6JP7hvM71IRZureyvZhoVALIs=
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 h1:cCR+9mKLOGyX4Zx+uBZDXEDAQsvKQ/XbW4vreG5v1jU=
|
||||
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766 h1:D02YdIT3M6OQkZXTQiO761u/SmR3DDDiDXLN2oZIUac=
|
||||
golang.org/x/net v0.0.0-20220907013720-d52c520e3766/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.8-0.20220509174342-b4bca84b0361 h1:h+pU/hCb7sEApigI6eII3/Emx5ZHaFWS+nulUp0Az/k=
|
||||
|
||||
@@ -167,6 +167,7 @@ var depsRules = `
|
||||
io/fs
|
||||
< internal/testlog
|
||||
< internal/poll
|
||||
< internal/safefilepath
|
||||
< os
|
||||
< os/signal;
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ var knownOS = map[string]bool{
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This list also appears in cmd/dist/build.go.
|
||||
// This list also appears in cmd/dist/build.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -664,3 +664,40 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue55030(t *testing.T) {
|
||||
// makeSig makes the signature func(typ...)
|
||||
makeSig := func(typ Type) {
|
||||
par := NewVar(token.NoPos, nil, "", typ)
|
||||
params := NewTuple(par)
|
||||
NewSignatureType(nil, nil, nil, params, nil, true)
|
||||
}
|
||||
|
||||
// makeSig must not panic for the following (example) types:
|
||||
// []int
|
||||
makeSig(NewSlice(Typ[Int]))
|
||||
|
||||
// string
|
||||
makeSig(Typ[String])
|
||||
|
||||
// P where P's core type is string
|
||||
{
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P string]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
|
||||
}
|
||||
|
||||
// P where P's core type is an (unnamed) slice
|
||||
{
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P []int]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
|
||||
}
|
||||
|
||||
// P where P's core type is bytestring (i.e., string or []byte)
|
||||
{
|
||||
t1 := NewTerm(true, Typ[String]) // ~string
|
||||
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
|
||||
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
|
||||
P := NewTypeName(token.NoPos, nil, "P", nil) // [P ~string | []byte]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
@@ -41,16 +42,18 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
|
||||
// NewSignatureType creates a new function type for the given receiver,
|
||||
// receiver type parameters, type parameters, parameters, and results. If
|
||||
// variadic is set, params must hold at least one parameter and the last
|
||||
// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
|
||||
// be empty. If recvTypeParams is non-empty, recv must be non-nil.
|
||||
// parameter's core type must be of unnamed slice or bytestring type.
|
||||
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
|
||||
// non-empty, recv must be non-nil.
|
||||
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
|
||||
if variadic {
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
panic("variadic function must have at least one parameter")
|
||||
}
|
||||
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||
panic("variadic parameter must be of unnamed slice type")
|
||||
core := coreString(params.At(n - 1).typ)
|
||||
if _, ok := core.(*Slice); !ok && !isString(core) {
|
||||
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as core type", core.String()))
|
||||
}
|
||||
}
|
||||
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
|
||||
|
||||
@@ -21,15 +21,15 @@ import _ "unsafe" // for go:linkname
|
||||
//go:linkname libfuzzerHookStrCmp runtime.libfuzzerHookStrCmp
|
||||
//go:linkname libfuzzerHookEqualFold runtime.libfuzzerHookEqualFold
|
||||
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) {}
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) {}
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) {}
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) {}
|
||||
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC uint) {}
|
||||
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC uint) {}
|
||||
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC uint) {}
|
||||
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC uint) {}
|
||||
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) {}
|
||||
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC uint) {}
|
||||
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC uint) {}
|
||||
|
||||
func libfuzzerHookStrCmp(arg0, arg1 string, fakePC int) {}
|
||||
func libfuzzerHookEqualFold(arg0, arg1 string, fakePC int) {}
|
||||
func libfuzzerHookStrCmp(arg0, arg1 string, fakePC uint) {}
|
||||
func libfuzzerHookEqualFold(arg0, arg1 string, fakePC uint) {}
|
||||
|
||||
@@ -13,6 +13,21 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Rot13 returns the rot13 encoding or decoding of its input.
|
||||
func Rot13(data []byte) []byte {
|
||||
out := make([]byte, len(data))
|
||||
copy(out, data)
|
||||
for i, c := range out {
|
||||
switch {
|
||||
case 'A' <= c && c <= 'M' || 'a' <= c && c <= 'm':
|
||||
out[i] = c + 13
|
||||
case 'N' <= c && c <= 'Z' || 'n' <= c && c <= 'z':
|
||||
out[i] = c - 13
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DecodeToTempFile decodes the named file to a temporary location.
|
||||
// If successful, it returns the path of the decoded file.
|
||||
// The caller is responsible for ensuring that the temporary file is removed.
|
||||
|
||||
21
src/internal/safefilepath/path.go
Normal file
21
src/internal/safefilepath/path.go
Normal file
@@ -0,0 +1,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.
|
||||
|
||||
// Package safefilepath manipulates operating-system file paths.
|
||||
package safefilepath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var errInvalidPath = errors.New("invalid path")
|
||||
|
||||
// FromFS converts a slash-separated path into an operating-system path.
|
||||
//
|
||||
// FromFS returns an error if the path cannot be represented by the operating
|
||||
// system. For example, paths containing '\' and ':' characters are rejected
|
||||
// on Windows.
|
||||
func FromFS(path string) (string, error) {
|
||||
return fromFS(path)
|
||||
}
|
||||
23
src/internal/safefilepath/path_other.go
Normal file
23
src/internal/safefilepath/path_other.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package safefilepath
|
||||
|
||||
import "runtime"
|
||||
|
||||
func fromFS(path string) (string, error) {
|
||||
if runtime.GOOS == "plan9" {
|
||||
if len(path) > 0 && path[0] == '#' {
|
||||
return path, errInvalidPath
|
||||
}
|
||||
}
|
||||
for i := range path {
|
||||
if path[i] == 0 {
|
||||
return "", errInvalidPath
|
||||
}
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
88
src/internal/safefilepath/path_test.go
Normal file
88
src/internal/safefilepath/path_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package safefilepath_test
|
||||
|
||||
import (
|
||||
"internal/safefilepath"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type PathTest struct {
|
||||
path, result string
|
||||
}
|
||||
|
||||
const invalid = ""
|
||||
|
||||
var fspathtests = []PathTest{
|
||||
{".", "."},
|
||||
{"/a/b/c", "/a/b/c"},
|
||||
{"a\x00b", invalid},
|
||||
}
|
||||
|
||||
var winreservedpathtests = []PathTest{
|
||||
{`a\b`, `a\b`},
|
||||
{`a:b`, `a:b`},
|
||||
{`a/b:c`, `a/b:c`},
|
||||
{`NUL`, `NUL`},
|
||||
{`./com1`, `./com1`},
|
||||
{`a/nul/b`, `a/nul/b`},
|
||||
}
|
||||
|
||||
// Whether a reserved name with an extension is reserved or not varies by
|
||||
// Windows version.
|
||||
var winreservedextpathtests = []PathTest{
|
||||
{"nul.txt", "nul.txt"},
|
||||
{"a/nul.txt/b", "a/nul.txt/b"},
|
||||
}
|
||||
|
||||
var plan9reservedpathtests = []PathTest{
|
||||
{`#c`, `#c`},
|
||||
}
|
||||
|
||||
func TestFromFS(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
if canWriteFile(t, "NUL") {
|
||||
t.Errorf("can unexpectedly write a file named NUL on Windows")
|
||||
}
|
||||
if canWriteFile(t, "nul.txt") {
|
||||
fspathtests = append(fspathtests, winreservedextpathtests...)
|
||||
} else {
|
||||
winreservedpathtests = append(winreservedpathtests, winreservedextpathtests...)
|
||||
}
|
||||
for i := range winreservedpathtests {
|
||||
winreservedpathtests[i].result = invalid
|
||||
}
|
||||
for i := range fspathtests {
|
||||
fspathtests[i].result = filepath.FromSlash(fspathtests[i].result)
|
||||
}
|
||||
case "plan9":
|
||||
for i := range plan9reservedpathtests {
|
||||
plan9reservedpathtests[i].result = invalid
|
||||
}
|
||||
}
|
||||
tests := fspathtests
|
||||
tests = append(tests, winreservedpathtests...)
|
||||
tests = append(tests, plan9reservedpathtests...)
|
||||
for _, test := range tests {
|
||||
got, err := safefilepath.FromFS(test.path)
|
||||
if (got == "") != (err != nil) {
|
||||
t.Errorf(`FromFS(%q) = %q, %v; want "" only if err != nil`, test.path, got, err)
|
||||
}
|
||||
if got != test.result {
|
||||
t.Errorf("FromFS(%q) = %q, %v; want %q", test.path, got, err, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func canWriteFile(t *testing.T, name string) bool {
|
||||
path := filepath.Join(t.TempDir(), name)
|
||||
os.WriteFile(path, []byte("ok"), 0666)
|
||||
b, _ := os.ReadFile(path)
|
||||
return string(b) == "ok"
|
||||
}
|
||||
95
src/internal/safefilepath/path_windows.go
Normal file
95
src/internal/safefilepath/path_windows.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package safefilepath
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func fromFS(path string) (string, error) {
|
||||
if !utf8.ValidString(path) {
|
||||
return "", errInvalidPath
|
||||
}
|
||||
for len(path) > 1 && path[0] == '/' && path[1] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
containsSlash := false
|
||||
for p := path; p != ""; {
|
||||
// Find the next path element.
|
||||
i := 0
|
||||
dot := -1
|
||||
for i < len(p) && p[i] != '/' {
|
||||
switch p[i] {
|
||||
case 0, '\\', ':':
|
||||
return "", errInvalidPath
|
||||
case '.':
|
||||
if dot < 0 {
|
||||
dot = i
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
part := p[:i]
|
||||
if i < len(p) {
|
||||
containsSlash = true
|
||||
p = p[i+1:]
|
||||
} else {
|
||||
p = ""
|
||||
}
|
||||
// Trim the extension and look for a reserved name.
|
||||
base := part
|
||||
if dot >= 0 {
|
||||
base = part[:dot]
|
||||
}
|
||||
if isReservedName(base) {
|
||||
if dot < 0 {
|
||||
return "", errInvalidPath
|
||||
}
|
||||
// The path element is a reserved name with an extension.
|
||||
// Some Windows versions consider this a reserved name,
|
||||
// while others do not. Use FullPath to see if the name is
|
||||
// reserved.
|
||||
if p, _ := syscall.FullPath(part); len(p) >= 4 && p[:4] == `\\.\` {
|
||||
return "", errInvalidPath
|
||||
}
|
||||
}
|
||||
}
|
||||
if containsSlash {
|
||||
// We can't depend on strings, so substitute \ for / manually.
|
||||
buf := []byte(path)
|
||||
for i, b := range buf {
|
||||
if b == '/' {
|
||||
buf[i] = '\\'
|
||||
}
|
||||
}
|
||||
path = string(buf)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// isReservedName reports if name is a Windows reserved device name.
|
||||
// It does not detect names with an extension, which are also reserved on some Windows versions.
|
||||
//
|
||||
// For details, search for PRN in
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file.
|
||||
func isReservedName(name string) bool {
|
||||
if 3 <= len(name) && len(name) <= 4 {
|
||||
switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
|
||||
case "CON", "PRN", "AUX", "NUL":
|
||||
return len(name) == 3
|
||||
case "COM", "LPT":
|
||||
return len(name) == 4 && '1' <= name[3] && name[3] <= '9'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toUpper(c byte) byte {
|
||||
if 'a' <= c && c <= 'z' {
|
||||
return c - ('a' - 'A')
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -9,6 +9,7 @@ package http
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/safefilepath"
|
||||
"io"
|
||||
"io/fs"
|
||||
"mime"
|
||||
@@ -69,14 +70,15 @@ func mapOpenError(originalErr error, name string, sep rune, stat func(string) (f
|
||||
// Open implements FileSystem using os.Open, opening files for reading rooted
|
||||
// and relative to the directory d.
|
||||
func (d Dir) Open(name string) (File, error) {
|
||||
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {
|
||||
return nil, errors.New("http: invalid character in file path")
|
||||
path, err := safefilepath.FromFS(path.Clean("/" + name))
|
||||
if err != nil {
|
||||
return nil, errors.New("http: invalid or unsafe file path")
|
||||
}
|
||||
dir := string(d)
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
|
||||
fullName := filepath.Join(dir, path)
|
||||
f, err := os.Open(fullName)
|
||||
if err != nil {
|
||||
return nil, mapOpenError(err, fullName, filepath.Separator, os.Stat)
|
||||
|
||||
@@ -702,6 +702,34 @@ func TestFileServerZeroByte(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileServerNamesEscape(t *testing.T) {
|
||||
t.Run("h1", func(t *testing.T) {
|
||||
testFileServerNamesEscape(t, h1Mode)
|
||||
})
|
||||
t.Run("h2", func(t *testing.T) {
|
||||
testFileServerNamesEscape(t, h2Mode)
|
||||
})
|
||||
}
|
||||
func testFileServerNamesEscape(t *testing.T, h2 bool) {
|
||||
defer afterTest(t)
|
||||
ts := newClientServerTest(t, h2, FileServer(Dir("testdata"))).ts
|
||||
defer ts.Close()
|
||||
for _, path := range []string{
|
||||
"/../testdata/file",
|
||||
"/NUL", // don't read from device files on Windows
|
||||
} {
|
||||
res, err := ts.Client().Get(ts.URL + path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
if res.StatusCode < 400 || res.StatusCode > 599 {
|
||||
t.Errorf("Get(%q): got status %v, want 4xx or 5xx", path, res.StatusCode)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type fakeFileInfo struct {
|
||||
dir bool
|
||||
basename string
|
||||
|
||||
@@ -4247,6 +4247,7 @@ type http2serverConn struct {
|
||||
headerTableSize uint32
|
||||
peerMaxHeaderListSize uint32 // zero means unknown (default)
|
||||
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
|
||||
canonHeaderKeysSize int // canonHeader keys size in bytes
|
||||
writingFrame bool // started writing a frame (on serve goroutine or separate)
|
||||
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
|
||||
needsFrameFlush bool // last frame write wasn't a flush
|
||||
@@ -4426,6 +4427,13 @@ func (sc *http2serverConn) condlogf(err error, format string, args ...interface{
|
||||
}
|
||||
}
|
||||
|
||||
// maxCachedCanonicalHeadersKeysSize is an arbitrarily-chosen limit on the size
|
||||
// of the entries in the canonHeader cache.
|
||||
// This should be larger than the size of unique, uncommon header keys likely to
|
||||
// be sent by the peer, while not so high as to permit unreasonable memory usage
|
||||
// if the peer sends an unbounded number of unique header keys.
|
||||
const http2maxCachedCanonicalHeadersKeysSize = 2048
|
||||
|
||||
func (sc *http2serverConn) canonicalHeader(v string) string {
|
||||
sc.serveG.check()
|
||||
http2buildCommonHeaderMapsOnce()
|
||||
@@ -4441,14 +4449,10 @@ func (sc *http2serverConn) canonicalHeader(v string) string {
|
||||
sc.canonHeader = make(map[string]string)
|
||||
}
|
||||
cv = CanonicalHeaderKey(v)
|
||||
// maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of
|
||||
// entries in the canonHeader cache. This should be larger than the number
|
||||
// of unique, uncommon header keys likely to be sent by the peer, while not
|
||||
// so high as to permit unreasonable memory usage if the peer sends an unbounded
|
||||
// number of unique header keys.
|
||||
const maxCachedCanonicalHeaders = 32
|
||||
if len(sc.canonHeader) < maxCachedCanonicalHeaders {
|
||||
size := 100 + len(v)*2 // 100 bytes of map overhead + key + value
|
||||
if sc.canonHeaderKeysSize+size <= http2maxCachedCanonicalHeadersKeysSize {
|
||||
sc.canonHeader[v] = cv
|
||||
sc.canonHeaderKeysSize += size
|
||||
}
|
||||
return cv
|
||||
}
|
||||
@@ -5058,6 +5062,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() {
|
||||
func (sc *http2serverConn) goAway(code http2ErrCode) {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
if sc.goAwayCode == http2ErrCodeNo {
|
||||
sc.goAwayCode = code
|
||||
}
|
||||
return
|
||||
}
|
||||
sc.inGoAway = true
|
||||
|
||||
@@ -261,6 +261,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
p.Director(outreq)
|
||||
if outreq.Form != nil {
|
||||
outreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery)
|
||||
}
|
||||
outreq.Close = false
|
||||
|
||||
reqUpType := upgradeType(outreq.Header)
|
||||
@@ -639,3 +642,36 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
|
||||
_, err := io.Copy(c.backend, c.user)
|
||||
errc <- err
|
||||
}
|
||||
|
||||
func cleanQueryParams(s string) string {
|
||||
reencode := func(s string) string {
|
||||
v, _ := url.ParseQuery(s)
|
||||
return v.Encode()
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case ';':
|
||||
return reencode(s)
|
||||
case '%':
|
||||
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
||||
return reencode(s)
|
||||
}
|
||||
i += 3
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func ishex(c byte) bool {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1537,3 +1537,77 @@ func TestJoinURLPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
testWantsCleanQuery = true
|
||||
testWantsRawQuery = false
|
||||
)
|
||||
|
||||
func TestReverseProxyQueryParameterSmugglingDirectorDoesNotParseForm(t *testing.T) {
|
||||
testReverseProxyQueryParameterSmuggling(t, testWantsRawQuery, func(u *url.URL) *ReverseProxy {
|
||||
proxyHandler := NewSingleHostReverseProxy(u)
|
||||
oldDirector := proxyHandler.Director
|
||||
proxyHandler.Director = func(r *http.Request) {
|
||||
oldDirector(r)
|
||||
}
|
||||
return proxyHandler
|
||||
})
|
||||
}
|
||||
|
||||
func TestReverseProxyQueryParameterSmugglingDirectorParsesForm(t *testing.T) {
|
||||
testReverseProxyQueryParameterSmuggling(t, testWantsCleanQuery, func(u *url.URL) *ReverseProxy {
|
||||
proxyHandler := NewSingleHostReverseProxy(u)
|
||||
oldDirector := proxyHandler.Director
|
||||
proxyHandler.Director = func(r *http.Request) {
|
||||
// Parsing the form causes ReverseProxy to remove unparsable
|
||||
// query parameters before forwarding.
|
||||
r.FormValue("a")
|
||||
oldDirector(r)
|
||||
}
|
||||
return proxyHandler
|
||||
})
|
||||
}
|
||||
|
||||
func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, newProxy func(*url.URL) *ReverseProxy) {
|
||||
const content = "response_content"
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(r.URL.RawQuery))
|
||||
}))
|
||||
defer backend.Close()
|
||||
backendURL, err := url.Parse(backend.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyHandler := newProxy(backendURL)
|
||||
frontend := httptest.NewServer(proxyHandler)
|
||||
defer frontend.Close()
|
||||
|
||||
// Don't spam output with logs of queries containing semicolons.
|
||||
backend.Config.ErrorLog = log.New(io.Discard, "", 0)
|
||||
frontend.Config.ErrorLog = log.New(io.Discard, "", 0)
|
||||
|
||||
for _, test := range []struct {
|
||||
rawQuery string
|
||||
cleanQuery string
|
||||
}{{
|
||||
rawQuery: "a=1&a=2;b=3",
|
||||
cleanQuery: "a=1",
|
||||
}, {
|
||||
rawQuery: "a=1&a=%zz&b=3",
|
||||
cleanQuery: "a=1&b=3",
|
||||
}} {
|
||||
res, err := frontend.Client().Get(frontend.URL + "?" + test.rawQuery)
|
||||
if err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
wantQuery := test.rawQuery
|
||||
if wantCleanQuery {
|
||||
wantQuery = test.cleanQuery
|
||||
}
|
||||
if got, want := string(body), wantQuery; got != want {
|
||||
t.Errorf("proxy forwarded raw query %q as %q, want %q", test.rawQuery, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,10 @@ var lookupGoogleSRVTests = []struct {
|
||||
var backoffDuration = [...]time.Duration{time.Second, 5 * time.Second, 30 * time.Second}
|
||||
|
||||
func TestLookupGoogleSRV(t *testing.T) {
|
||||
// TODO(mknyszek): Figure out next steps for this test. This is just
|
||||
// a quick fix.
|
||||
t.Skip("fails consistently due to an upstream DNS change; see #56707.")
|
||||
|
||||
t.Parallel()
|
||||
mustHaveExternalNetwork(t)
|
||||
|
||||
@@ -631,6 +635,10 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLookupDotsWithRemoteSource(t *testing.T) {
|
||||
// TODO(mknyszek): Figure out next steps for this test. This is just
|
||||
// a quick fix.
|
||||
t.Skip("fails consistently due to an upstream DNS change; see #56707.")
|
||||
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||
testenv.SkipFlaky(t, 27992)
|
||||
}
|
||||
|
||||
@@ -1191,17 +1191,23 @@ func (u *URL) UnmarshalBinary(text []byte) error {
|
||||
// any existing path and the resulting path cleaned of any ./ or ../ elements.
|
||||
// Any sequences of multiple / characters will be reduced to a single /.
|
||||
func (u *URL) JoinPath(elem ...string) *URL {
|
||||
url := *u
|
||||
if len(elem) > 0 {
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
p := path.Join(elem...)
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url.setPath(p)
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
var p string
|
||||
if !strings.HasPrefix(elem[0], "/") {
|
||||
// Return a relative path if u is relative,
|
||||
// but ensure that it contains no ../ elements.
|
||||
elem[0] = "/" + elem[0]
|
||||
p = path.Join(elem...)[1:]
|
||||
} else {
|
||||
p = path.Join(elem...)
|
||||
}
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url := *u
|
||||
url.setPath(p)
|
||||
return &url
|
||||
}
|
||||
|
||||
|
||||
@@ -2080,6 +2080,26 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go", "../../go", "../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/../go",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"./go"},
|
||||
@@ -2112,7 +2132,7 @@ func TestJoinPath(t *testing.T) {
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com",
|
||||
out: "https://go.googlesource.com/",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
@@ -2129,11 +2149,46 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"c%2fd"},
|
||||
out: "https://go.googlesource.com/a%2fb/c%2fd",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/a/b",
|
||||
elem: []string{"/go"},
|
||||
out: "https://go.googlesource.com/a/b/go",
|
||||
},
|
||||
{
|
||||
base: "/",
|
||||
elem: nil,
|
||||
out: "/",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: nil,
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"b"},
|
||||
out: "a/b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"a"},
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"../a"},
|
||||
out: "a",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
wantErr := "nil"
|
||||
|
||||
@@ -11,9 +11,11 @@ import (
|
||||
|
||||
func TestDedupEnv(t *testing.T) {
|
||||
tests := []struct {
|
||||
noCase bool
|
||||
in []string
|
||||
want []string
|
||||
noCase bool
|
||||
nulOK bool
|
||||
in []string
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
noCase: true,
|
||||
@@ -41,11 +43,23 @@ func TestDedupEnv(t *testing.T) {
|
||||
in: []string{"dodgy", "entries"},
|
||||
want: []string{"dodgy", "entries"},
|
||||
},
|
||||
{
|
||||
// Filter out entries containing NULs.
|
||||
in: []string{"A=a\x00b", "B=b", "C\x00C=c"},
|
||||
want: []string{"B=b"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
// Plan 9 needs to preserve environment variables with NUL (#56544).
|
||||
nulOK: true,
|
||||
in: []string{"path=one\x00two"},
|
||||
want: []string{"path=one\x00two"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := dedupEnvCase(tt.noCase, tt.in)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Dedup(%v, %q) = %q; want %q", tt.noCase, tt.in, got, tt.want)
|
||||
got, err := dedupEnvCase(tt.noCase, tt.nulOK, tt.in)
|
||||
if !reflect.DeepEqual(got, tt.want) || (err != nil) != tt.wantErr {
|
||||
t.Errorf("Dedup(%v, %q) = %q, %v; want %q, error:%v", tt.noCase, tt.in, got, err, tt.want, tt.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,7 +912,11 @@ func (c *Cmd) environ() ([]string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return addCriticalEnv(dedupEnv(env)), err
|
||||
env, dedupErr := dedupEnv(env)
|
||||
if err == nil {
|
||||
err = dedupErr
|
||||
}
|
||||
return addCriticalEnv(env), err
|
||||
}
|
||||
|
||||
// Environ returns a copy of the environment in which the command would be run
|
||||
@@ -926,20 +930,31 @@ func (c *Cmd) Environ() []string {
|
||||
// dedupEnv returns a copy of env with any duplicates removed, in favor of
|
||||
// later values.
|
||||
// Items not of the normal environment "key=value" form are preserved unchanged.
|
||||
func dedupEnv(env []string) []string {
|
||||
return dedupEnvCase(runtime.GOOS == "windows", env)
|
||||
// Except on Plan 9, items containing NUL characters are removed, and
|
||||
// an error is returned along with the remaining values.
|
||||
func dedupEnv(env []string) ([]string, error) {
|
||||
return dedupEnvCase(runtime.GOOS == "windows", runtime.GOOS == "plan9", env)
|
||||
}
|
||||
|
||||
// dedupEnvCase is dedupEnv with a case option for testing.
|
||||
// If caseInsensitive is true, the case of keys is ignored.
|
||||
func dedupEnvCase(caseInsensitive bool, env []string) []string {
|
||||
// If nulOK is false, items containing NUL characters are allowed.
|
||||
func dedupEnvCase(caseInsensitive, nulOK bool, env []string) ([]string, error) {
|
||||
// Construct the output in reverse order, to preserve the
|
||||
// last occurrence of each key.
|
||||
var err error
|
||||
out := make([]string, 0, len(env))
|
||||
saw := make(map[string]bool, len(env))
|
||||
for n := len(env); n > 0; n-- {
|
||||
kv := env[n-1]
|
||||
|
||||
// Reject NUL in environment variables to prevent security issues (#56284);
|
||||
// except on Plan 9, which uses NUL as os.PathListSeparator (#56544).
|
||||
if !nulOK && strings.IndexByte(kv, 0) != -1 {
|
||||
err = errors.New("exec: environment variable contains NUL")
|
||||
continue
|
||||
}
|
||||
|
||||
i := strings.Index(kv, "=")
|
||||
if i == 0 {
|
||||
// We observe in practice keys with a single leading "=" on Windows.
|
||||
@@ -974,7 +989,7 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string {
|
||||
out[i], out[j] = out[j], out[i]
|
||||
}
|
||||
|
||||
return out
|
||||
return out, err
|
||||
}
|
||||
|
||||
// addCriticalEnv adds any critical environment variables that are required
|
||||
|
||||
@@ -1053,6 +1053,18 @@ func TestDedupEnvEcho(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvNULCharacter(t *testing.T) {
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("plan9 explicitly allows NUL in the enviroment")
|
||||
}
|
||||
cmd := helperCommand(t, "echoenv", "FOO", "BAR")
|
||||
cmd.Env = append(cmd.Environ(), "FOO=foo\x00BAR=bar")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("output = %q; want error", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
echoPath, err := exec.LookPath("echo")
|
||||
if err != nil {
|
||||
|
||||
@@ -42,6 +42,7 @@ package os
|
||||
import (
|
||||
"errors"
|
||||
"internal/poll"
|
||||
"internal/safefilepath"
|
||||
"internal/testlog"
|
||||
"internal/unsafeheader"
|
||||
"io"
|
||||
@@ -625,6 +626,8 @@ func isWindowsNulName(name string) bool {
|
||||
// a general substitute for a chroot-style security mechanism when the directory tree
|
||||
// contains arbitrary content.
|
||||
//
|
||||
// The directory dir must not be "".
|
||||
//
|
||||
// The result implements fs.StatFS.
|
||||
func DirFS(dir string) fs.FS {
|
||||
return dirFS(dir)
|
||||
@@ -644,10 +647,11 @@ func containsAny(s, chars string) bool {
|
||||
type dirFS string
|
||||
|
||||
func (dir dirFS) Open(name string) (fs.File, error) {
|
||||
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
|
||||
return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
f, err := Open(string(dir) + "/" + name)
|
||||
f, err := Open(fullname)
|
||||
if err != nil {
|
||||
return nil, err // nil fs.File
|
||||
}
|
||||
@@ -655,16 +659,35 @@ func (dir dirFS) Open(name string) (fs.File, error) {
|
||||
}
|
||||
|
||||
func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
|
||||
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
f, err := Stat(string(dir) + "/" + name)
|
||||
f, err := Stat(fullname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// join returns the path for name in dir.
|
||||
func (dir dirFS) join(name string) (string, error) {
|
||||
if dir == "" {
|
||||
return "", errors.New("os: DirFS with empty root")
|
||||
}
|
||||
if !fs.ValidPath(name) {
|
||||
return "", ErrInvalid
|
||||
}
|
||||
name, err := safefilepath.FromFS(name)
|
||||
if err != nil {
|
||||
return "", ErrInvalid
|
||||
}
|
||||
if IsPathSeparator(dir[len(dir)-1]) {
|
||||
return string(dir) + name, nil
|
||||
}
|
||||
return string(dir) + string(PathSeparator) + name, nil
|
||||
}
|
||||
|
||||
// ReadFile reads the named file and returns the contents.
|
||||
// A successful call returns err == nil, not err == EOF.
|
||||
// Because ReadFile reads the whole file, it does not treat an EOF from Read
|
||||
|
||||
@@ -2727,6 +2727,44 @@ func TestDirFS(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf(`Open testdata\dirfs succeeded`)
|
||||
}
|
||||
|
||||
// Test that Open does not open Windows device files.
|
||||
_, err = d.Open(`NUL`)
|
||||
if err == nil {
|
||||
t.Errorf(`Open NUL succeeded`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirFSRootDir(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cwd = cwd[len(filepath.VolumeName(cwd)):] // trim volume prefix (C:) on Windows
|
||||
cwd = filepath.ToSlash(cwd) // convert \ to /
|
||||
cwd = strings.TrimPrefix(cwd, "/") // trim leading /
|
||||
|
||||
// Test that Open can open a path starting at /.
|
||||
d := DirFS("/")
|
||||
f, err := d.Open(cwd + "/testdata/dirfs/a")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
func TestDirFSEmptyDir(t *testing.T) {
|
||||
d := DirFS("")
|
||||
cwd, _ := os.Getwd()
|
||||
for _, path := range []string{
|
||||
"testdata/dirfs/a", // not DirFS(".")
|
||||
filepath.ToSlash(cwd) + "/testdata/dirfs/a", // not DirFS("/")
|
||||
} {
|
||||
_, err := d.Open(path)
|
||||
if err == nil {
|
||||
t.Fatalf(`DirFS("").Open(%q) succeeded`, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirFSPathsValid(t *testing.T) {
|
||||
|
||||
@@ -91,15 +91,49 @@ const (
|
||||
// until we've allocated at least maxHeight Regexp structures.
|
||||
const maxHeight = 1000
|
||||
|
||||
// maxSize is the maximum size of a compiled regexp in Insts.
|
||||
// It too is somewhat arbitrarily chosen, but the idea is to be large enough
|
||||
// to allow significant regexps while at the same time small enough that
|
||||
// the compiled form will not take up too much memory.
|
||||
// 128 MB is enough for a 3.3 million Inst structures, which roughly
|
||||
// corresponds to a 3.3 MB regexp.
|
||||
const (
|
||||
maxSize = 128 << 20 / instSize
|
||||
instSize = 5 * 8 // byte, 2 uint32, slice is 5 64-bit words
|
||||
)
|
||||
|
||||
// maxRunes is the maximum number of runes allowed in a regexp tree
|
||||
// counting the runes in all the nodes.
|
||||
// Ignoring character classes p.numRunes is always less than the length of the regexp.
|
||||
// Character classes can make it much larger: each \pL adds 1292 runes.
|
||||
// 128 MB is enough for 32M runes, which is over 26k \pL instances.
|
||||
// Note that repetitions do not make copies of the rune slices,
|
||||
// so \pL{1000} is only one rune slice, not 1000.
|
||||
// We could keep a cache of character classes we've seen,
|
||||
// so that all the \pL we see use the same rune list,
|
||||
// but that doesn't remove the problem entirely:
|
||||
// consider something like [\pL01234][\pL01235][\pL01236]...[\pL^&*()].
|
||||
// And because the Rune slice is exposed directly in the Regexp,
|
||||
// there is not an opportunity to change the representation to allow
|
||||
// partial sharing between different character classes.
|
||||
// So the limit is the best we can do.
|
||||
const (
|
||||
maxRunes = 128 << 20 / runeSize
|
||||
runeSize = 4 // rune is int32
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
flags Flags // parse mode flags
|
||||
stack []*Regexp // stack of parsed expressions
|
||||
free *Regexp
|
||||
numCap int // number of capturing groups seen
|
||||
wholeRegexp string
|
||||
tmpClass []rune // temporary char class work space
|
||||
numRegexp int // number of regexps allocated
|
||||
height map[*Regexp]int // regexp height for height limit check
|
||||
tmpClass []rune // temporary char class work space
|
||||
numRegexp int // number of regexps allocated
|
||||
numRunes int // number of runes in char classes
|
||||
repeats int64 // product of all repetitions seen
|
||||
height map[*Regexp]int // regexp height, for height limit check
|
||||
size map[*Regexp]int64 // regexp compiled size, for size limit check
|
||||
}
|
||||
|
||||
func (p *parser) newRegexp(op Op) *Regexp {
|
||||
@@ -123,6 +157,104 @@ func (p *parser) reuse(re *Regexp) {
|
||||
p.free = re
|
||||
}
|
||||
|
||||
func (p *parser) checkLimits(re *Regexp) {
|
||||
if p.numRunes > maxRunes {
|
||||
panic(ErrInternalError)
|
||||
}
|
||||
p.checkSize(re)
|
||||
p.checkHeight(re)
|
||||
}
|
||||
|
||||
func (p *parser) checkSize(re *Regexp) {
|
||||
if p.size == nil {
|
||||
// We haven't started tracking size yet.
|
||||
// Do a relatively cheap check to see if we need to start.
|
||||
// Maintain the product of all the repeats we've seen
|
||||
// and don't track if the total number of regexp nodes
|
||||
// we've seen times the repeat product is in budget.
|
||||
if p.repeats == 0 {
|
||||
p.repeats = 1
|
||||
}
|
||||
if re.Op == OpRepeat {
|
||||
n := re.Max
|
||||
if n == -1 {
|
||||
n = re.Min
|
||||
}
|
||||
if n <= 0 {
|
||||
n = 1
|
||||
}
|
||||
if int64(n) > maxSize/p.repeats {
|
||||
p.repeats = maxSize
|
||||
} else {
|
||||
p.repeats *= int64(n)
|
||||
}
|
||||
}
|
||||
if int64(p.numRegexp) < maxSize/p.repeats {
|
||||
return
|
||||
}
|
||||
|
||||
// We need to start tracking size.
|
||||
// Make the map and belatedly populate it
|
||||
// with info about everything we've constructed so far.
|
||||
p.size = make(map[*Regexp]int64)
|
||||
for _, re := range p.stack {
|
||||
p.checkSize(re)
|
||||
}
|
||||
}
|
||||
|
||||
if p.calcSize(re, true) > maxSize {
|
||||
panic(ErrInternalError)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) calcSize(re *Regexp, force bool) int64 {
|
||||
if !force {
|
||||
if size, ok := p.size[re]; ok {
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
switch re.Op {
|
||||
case OpLiteral:
|
||||
size = int64(len(re.Rune))
|
||||
case OpCapture, OpStar:
|
||||
// star can be 1+ or 2+; assume 2 pessimistically
|
||||
size = 2 + p.calcSize(re.Sub[0], false)
|
||||
case OpPlus, OpQuest:
|
||||
size = 1 + p.calcSize(re.Sub[0], false)
|
||||
case OpConcat:
|
||||
for _, sub := range re.Sub {
|
||||
size += p.calcSize(sub, false)
|
||||
}
|
||||
case OpAlternate:
|
||||
for _, sub := range re.Sub {
|
||||
size += p.calcSize(sub, false)
|
||||
}
|
||||
if len(re.Sub) > 1 {
|
||||
size += int64(len(re.Sub)) - 1
|
||||
}
|
||||
case OpRepeat:
|
||||
sub := p.calcSize(re.Sub[0], false)
|
||||
if re.Max == -1 {
|
||||
if re.Min == 0 {
|
||||
size = 2 + sub // x*
|
||||
} else {
|
||||
size = 1 + int64(re.Min)*sub // xxx+
|
||||
}
|
||||
break
|
||||
}
|
||||
// x{2,5} = xx(x(x(x)?)?)?
|
||||
size = int64(re.Max)*sub + int64(re.Max-re.Min)
|
||||
}
|
||||
|
||||
if size < 1 {
|
||||
size = 1
|
||||
}
|
||||
p.size[re] = size
|
||||
return size
|
||||
}
|
||||
|
||||
func (p *parser) checkHeight(re *Regexp) {
|
||||
if p.numRegexp < maxHeight {
|
||||
return
|
||||
@@ -159,6 +291,7 @@ func (p *parser) calcHeight(re *Regexp, force bool) int {
|
||||
|
||||
// push pushes the regexp re onto the parse stack and returns the regexp.
|
||||
func (p *parser) push(re *Regexp) *Regexp {
|
||||
p.numRunes += len(re.Rune)
|
||||
if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] {
|
||||
// Single rune.
|
||||
if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) {
|
||||
@@ -190,7 +323,7 @@ func (p *parser) push(re *Regexp) *Regexp {
|
||||
}
|
||||
|
||||
p.stack = append(p.stack, re)
|
||||
p.checkHeight(re)
|
||||
p.checkLimits(re)
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -300,7 +433,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) (
|
||||
re.Sub = re.Sub0[:1]
|
||||
re.Sub[0] = sub
|
||||
p.stack[n-1] = re
|
||||
p.checkHeight(re)
|
||||
p.checkLimits(re)
|
||||
|
||||
if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) {
|
||||
return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]}
|
||||
@@ -508,6 +641,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
|
||||
|
||||
for j := start; j < i; j++ {
|
||||
sub[j] = p.removeLeadingString(sub[j], len(str))
|
||||
p.checkLimits(sub[j])
|
||||
}
|
||||
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
|
||||
|
||||
@@ -565,6 +699,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
|
||||
for j := start; j < i; j++ {
|
||||
reuse := j != start // prefix came from sub[start]
|
||||
sub[j] = p.removeLeadingRegexp(sub[j], reuse)
|
||||
p.checkLimits(sub[j])
|
||||
}
|
||||
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
|
||||
|
||||
@@ -762,6 +897,8 @@ func parse(s string, flags Flags) (_ *Regexp, err error) {
|
||||
panic(r)
|
||||
case nil:
|
||||
// ok
|
||||
case ErrInternalError: // too big
|
||||
err = &Error{Code: ErrInternalError, Expr: s}
|
||||
case ErrNestingDepth:
|
||||
err = &Error{Code: ErrNestingDepth, Expr: s}
|
||||
}
|
||||
|
||||
@@ -484,12 +484,15 @@ var invalidRegexps = []string{
|
||||
`(?P<>a)`,
|
||||
`[a-Z]`,
|
||||
`(?i)[a-Z]`,
|
||||
`a{100000}`,
|
||||
`a{100000,}`,
|
||||
"((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
|
||||
strings.Repeat("(", 1000) + strings.Repeat(")", 1000),
|
||||
strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000),
|
||||
`\Q\E*`,
|
||||
`a{100000}`, // too much repetition
|
||||
`a{100000,}`, // too much repetition
|
||||
"((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", // too much repetition
|
||||
strings.Repeat("(", 1000) + strings.Repeat(")", 1000), // too deep
|
||||
strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), // too deep
|
||||
"(" + strings.Repeat("(xx?)", 1000) + "){1000}", // too long
|
||||
strings.Repeat("(xx?){1000}", 1000), // too long
|
||||
strings.Repeat(`\pL`, 27000), // too many runes
|
||||
}
|
||||
|
||||
var onlyPerl = []string{
|
||||
|
||||
@@ -387,6 +387,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
RET
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVW R13, R13
|
||||
|
||||
MOVW $0, R7
|
||||
B runtime·morestack(SB)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user