[release] Snap to 85e6685445
Change-Id: I8ee7a2ec9aa2c0bdf7f919aa6d4d878fc86766f3
diff --git a/OWNERS b/OWNERS
index be2231d..157381a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,3 @@
-pascallouis@google.com
maruel@google.com
-tamird@google.com
# COMPONENT: Toolchain>Go
diff --git a/VERSION b/VERSION
index 0524870..03d7710 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-go1.20.5
\ No newline at end of file
+go1.20.6
\ No newline at end of file
diff --git a/api/fuchsia.txt b/api/fuchsia.txt
index 995935b..d32813a 100644
--- a/api/fuchsia.txt
+++ b/api/fuchsia.txt
@@ -1229,6 +1229,8 @@
pkg syscall/zx/fidl, const ErrUnspecifiedHandleRights ErrorCode
pkg syscall/zx/fidl, const ErrUnspecifiedHandleType = 35
pkg syscall/zx/fidl, const ErrUnspecifiedHandleType ErrorCode
+pkg syscall/zx/fidl, const ErrUnsupportedWireFormatVersion = 39
+pkg syscall/zx/fidl, const ErrUnsupportedWireFormatVersion ErrorCode
pkg syscall/zx/fidl, const ErrValueTypeHandles = 36
pkg syscall/zx/fidl, const ErrValueTypeHandles ErrorCode
pkg syscall/zx/fidl, const ErrVectorTooLong = 4
@@ -1263,6 +1265,7 @@
pkg syscall/zx/fidl, method (*MessageHeader) IsSupportedVersion() bool
pkg syscall/zx/fidl, method (*MessageHeader) Marshaler() Marshaler
pkg syscall/zx/fidl, method (*MessageHeader) NewCtx() MarshalerContext
+pkg syscall/zx/fidl, method (*MessageHeader) ValidateWireFormat() error
pkg syscall/zx/fidl, method (ErrorCode) Code() ErrorCode
pkg syscall/zx/fidl, method (ErrorCode) Error() string
pkg syscall/zx/fidl, method (ErrorCode) String() string
diff --git a/meta/go_net_test.cml b/meta/go_net_test.cml
index 44df54c..a94915c 100644
--- a/meta/go_net_test.cml
+++ b/meta/go_net_test.cml
@@ -3,6 +3,7 @@
// found in the LICENSE file.
{
include: [
+ "//sdk/lib/inspect/offer.shard.cml",
"//src/sys/test_runners/gotests/default.shard.cml",
"//src/sys/test_runners/tmp_storage.shard.cml",
],
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 81060c6..ba3c7da 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -80,6 +80,11 @@
cg = d.Doc
}
if cg != nil {
+ if strings.ContainsAny(abspath, "\r\n") {
+ // This should have been checked when the file path was first resolved,
+ // but we double check here just to be sure.
+ fatalf("internal error: ParseGo: abspath contains unexpected newline character: %q", abspath)
+ }
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
f.Preamble += commentText(cg) + "\n"
f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index f78969e..78020ae 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -363,6 +363,12 @@
// Apply trimpath to the file path. The path won't be read from after this point.
input, _ = objabi.ApplyRewrites(input, *trimpath)
+ if strings.ContainsAny(input, "\r\n") {
+ // ParseGo, (*Package).writeOutput, and printer.Fprint in SourcePos mode
+ // all emit line directives, which don't permit newlines in the file path.
+ // Bail early if we see anything newline-like in the trimmed path.
+ fatalf("input path contains newline character: %q", input)
+ }
goFiles[i] = input
f := new(File)
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index d0c6fe3..b2933e2 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -48,7 +48,7 @@
fflg := creat(*objDir + "_cgo_flags")
for k, v := range p.CgoFlags {
for _, arg := range v {
- fmt.Fprintf(fflg, "_CGO_%s=%s\n", arg)
+ fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, arg)
}
if k == "LDFLAGS" && !*gccgo {
for _, arg := range v {
@@ -644,6 +644,11 @@
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
fmt.Fprintf(fgo1, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
+ if strings.ContainsAny(srcfile, "\r\n") {
+ // This should have been checked when the file path was first resolved,
+ // but we double check here just to be sure.
+ fatalf("internal error: writeOutput: srcfile contains unexpected newline character: %q", srcfile)
+ }
fmt.Fprintf(fgo1, "//line %s:1:1\n", srcfile)
fgo1.Write(f.Edit.Bytes())
diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go
index 6855f05..fd80d70 100644
--- a/src/cmd/compile/internal/typecheck/const.go
+++ b/src/cmd/compile/internal/typecheck/const.go
@@ -367,29 +367,7 @@
}
case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT:
- n := n.(*ir.BinaryExpr)
- nl, nr := n.X, n.Y
- if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
- rval := nr.Val()
-
- // check for divisor underflow in complex division (see issue 20227)
- if n.Op() == ir.ODIV && n.Type().IsComplex() && constant.Sign(square(constant.Real(rval))) == 0 && constant.Sign(square(constant.Imag(rval))) == 0 {
- base.Errorf("complex division by zero")
- n.SetType(nil)
- return n
- }
- if (n.Op() == ir.ODIV || n.Op() == ir.OMOD) && constant.Sign(rval) == 0 {
- base.Errorf("division by zero")
- n.SetType(nil)
- return n
- }
-
- tok := tokenForOp[n.Op()]
- if n.Op() == ir.ODIV && n.Type().IsInteger() {
- tok = token.QUO_ASSIGN // integer division
- }
- return OrigConst(n, constant.BinaryOp(nl.Val(), tok, rval))
- }
+ return n
case ir.OOROR, ir.OANDAND:
n := n.(*ir.LogicalExpr)
@@ -406,19 +384,7 @@
}
case ir.OLSH, ir.ORSH:
- n := n.(*ir.BinaryExpr)
- nl, nr := n.X, n.Y
- if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
- // shiftBound from go/types; "so we can express smallestFloat64" (see issue #44057)
- const shiftBound = 1023 - 1 + 52
- s, ok := constant.Uint64Val(nr.Val())
- if !ok || s > shiftBound {
- base.Errorf("invalid shift count %v", nr)
- n.SetType(nil)
- break
- }
- return OrigConst(n, constant.Shift(toint(nl.Val()), tokenForOp[n.Op()], uint(s)))
- }
+ return n
case ir.OCONV, ir.ORUNESTR:
n := n.(*ir.ConvExpr)
diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go
index 0cd69ab..ef61b74 100644
--- a/src/cmd/compile/internal/typecheck/expr.go
+++ b/src/cmd/compile/internal/typecheck/expr.go
@@ -184,13 +184,6 @@
}
}
- if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) {
- if constant.Sign(r.Val()) == 0 {
- base.Errorf("division by zero")
- return l, r, nil
- }
- }
-
return l, r, t
}
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index 74bb500..5fd8b95 100644
--- a/src/cmd/cover/cover.go
+++ b/src/cmd/cover/cover.go
@@ -568,6 +568,11 @@
}
// TODO: process files in parallel here if it matters.
for k, name := range names {
+ if strings.ContainsAny(name, "\r\n") {
+ // annotateFile uses '//line' directives, which don't permit newlines.
+ log.Fatalf("cover: input path contains newline character: %q", name)
+ }
+
last := false
if k == len(names)-1 {
last = true
@@ -645,6 +650,11 @@
}
newContent := file.edit.Bytes()
+ if strings.ContainsAny(name, "\r\n") {
+ // This should have been checked by the caller already, but we double check
+ // here just to be sure we haven't missed a caller somewhere.
+ panic(fmt.Sprintf("annotateFile: name contains unexpected newline character: %q", name))
+ }
fmt.Fprintf(fd, "//line %s:1:1\n", name)
fd.Write(newContent)
diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go
index af266b5..6ed4ae4 100644
--- a/src/cmd/cover/cover_test.go
+++ b/src/cmd/cover/cover_test.go
@@ -574,3 +574,34 @@
}
return string(out)
}
+
+func TestSrcPathWithNewline(t *testing.T) {
+ testenv.MustHaveExec(t)
+ t.Parallel()
+
+ // srcPath is intentionally not clean so that the path passed to testcover
+ // will not normalize the trailing / to a \ on Windows.
+ srcPath := t.TempDir() + string(filepath.Separator) + "\npackage main\nfunc main() { panic(string([]rune{'u', 'h', '-', 'o', 'h'}))\n/*/main.go"
+ mainSrc := ` package main
+
+func main() {
+ /* nothing here */
+ println("ok")
+}
+`
+ if err := os.MkdirAll(filepath.Dir(srcPath), 0777); err != nil {
+ t.Skipf("creating directory with bogus path: %v", err)
+ }
+ if err := os.WriteFile(srcPath, []byte(mainSrc), 0666); err != nil {
+ t.Skipf("writing file with bogus directory: %v", err)
+ }
+
+ cmd := testenv.Command(t, testcover(t), "-mode=atomic", srcPath)
+ cmd.Stderr = new(bytes.Buffer)
+ out, err := cmd.Output()
+ t.Logf("%v:\n%s", cmd, out)
+ t.Logf("stderr:\n%s", cmd.Stderr)
+ if err == nil {
+ t.Errorf("unexpected success; want failure due to newline in file path")
+ }
+}
diff --git a/src/cmd/go/go_unix_test.go b/src/cmd/go/go_unix_test.go
index bab9494..7cc6825 100644
--- a/src/cmd/go/go_unix_test.go
+++ b/src/cmd/go/go_unix_test.go
@@ -2,12 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+//go:build unix
package main_test
import (
+ "bufio"
+ "context"
+ "internal/testenv"
+ "io"
"os"
+ "os/exec"
+ "strings"
"syscall"
"testing"
)
@@ -33,3 +39,80 @@
t.Fatalf("wrote x with mode=%v, wanted no 0077 bits", mode)
}
}
+
+// TestTestInterrupt verifies the fix for issue #60203.
+//
+// If the whole process group for a 'go test' invocation receives
+// SIGINT (as would be sent by pressing ^C on a console),
+// it should return quickly, not deadlock.
+func TestTestInterrupt(t *testing.T) {
+ if testing.Short() {
+ t.Skipf("skipping in short mode: test executes many subprocesses")
+ }
+ // Don't run this test in parallel, for the same reason.
+
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.setenv("GOROOT", testGOROOT)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ cmd := testenv.CommandContext(t, ctx, tg.goTool(), "test", "std", "-short", "-count=1")
+ cmd.Dir = tg.execDir
+
+ // Override $TMPDIR when running the tests: since we're terminating the tests
+ // with a signal they might fail to clean up some temp files, and we don't
+ // want that to cause an "unexpected files" failure at the end of the run.
+ cmd.Env = append(tg.env[:len(tg.env):len(tg.env)], tempEnvName()+"="+t.TempDir())
+
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Setpgid: true,
+ }
+ cmd.Cancel = func() error {
+ pgid := cmd.Process.Pid
+ return syscall.Kill(-pgid, syscall.SIGINT)
+ }
+
+ pipe, err := cmd.StdoutPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("running %v", cmd)
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ stdout := new(strings.Builder)
+ r := bufio.NewReader(pipe)
+ line, err := r.ReadString('\n')
+ if err != nil {
+ t.Fatal(err)
+ }
+ stdout.WriteString(line)
+
+ // The output line for some test was written, so we know things are in progress.
+ //
+ // Cancel the rest of the run by sending SIGINT to the process group:
+ // it should finish up and exit with a nonzero status,
+ // not have to be killed with SIGKILL.
+ cancel()
+
+ io.Copy(stdout, r)
+ if stdout.Len() > 0 {
+ t.Logf("stdout:\n%s", stdout)
+ }
+ err = cmd.Wait()
+
+ ee, _ := err.(*exec.ExitError)
+ if ee == nil {
+ t.Fatalf("unexpectedly finished with nonzero status")
+ }
+ if len(ee.Stderr) > 0 {
+ t.Logf("stderr:\n%s", ee.Stderr)
+ }
+ if !ee.Exited() {
+ t.Fatalf("'go test' did not exit after interrupt: %v", err)
+ }
+
+ t.Logf("interrupted tests without deadlocking")
+}
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index aa59611..29a80b5 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -10,6 +10,7 @@
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"context"
+ "errors"
"fmt"
"os"
"reflect"
@@ -689,8 +690,8 @@
// roots) until the set of roots has converged.
func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
var (
- roots []module.Version
- pathIncluded = map[string]bool{mainModule.Path: true}
+ roots []module.Version
+ pathIsRoot = map[string]bool{mainModule.Path: true}
)
// We start by adding roots for every package in "all".
//
@@ -710,9 +711,9 @@
if !pkg.flags.has(pkgInAll) {
continue
}
- if pkg.fromExternalModule() && !pathIncluded[pkg.mod.Path] {
+ if pkg.fromExternalModule() && !pathIsRoot[pkg.mod.Path] {
roots = append(roots, pkg.mod)
- pathIncluded[pkg.mod.Path] = true
+ pathIsRoot[pkg.mod.Path] = true
}
queue = append(queue, pkg)
queued[pkg] = true
@@ -744,11 +745,12 @@
queue = append(queue, pkg.test)
queued[pkg.test] = true
}
- if !pathIncluded[m.Path] {
+
+ if !pathIsRoot[m.Path] {
if s := mg.Selected(m.Path); cmpVersion(s, m.Version) < 0 {
roots = append(roots, m)
+ pathIsRoot[m.Path] = true
}
- pathIncluded[m.Path] = true
}
}
@@ -758,10 +760,62 @@
}
}
+ roots = tidy.rootModules
_, err := tidy.Graph(ctx)
if err != nil {
return nil, err
}
+
+ // We try to avoid adding explicit requirements for test-only dependencies of
+ // packages in external modules. However, if we drop the explicit
+ // requirements, that may change an import from unambiguous (due to lazy
+ // module loading) to ambiguous (because lazy module loading no longer
+ // disambiguates it). For any package that has become ambiguous, we try
+ // to fix it by promoting its module to an explicit root.
+ // (See https://go.dev/issue/60313.)
+ q := par.NewQueue(runtime.GOMAXPROCS(0))
+ for {
+ var disambiguateRoot sync.Map
+ for _, pkg := range pkgs {
+ if pkg.mod.Path == "" || pathIsRoot[pkg.mod.Path] {
+ // Lazy module loading will cause pkg.mod to be checked before any other modules
+ // that are only indirectly required. It is as unambiguous as possible.
+ continue
+ }
+ pkg := pkg
+ q.Add(func() {
+ skipModFile := true
+ _, _, _, _, err := importFromModules(ctx, pkg.path, tidy, nil, skipModFile)
+ if aie := (*AmbiguousImportError)(nil); errors.As(err, &aie) {
+ disambiguateRoot.Store(pkg.mod, true)
+ }
+ })
+ }
+ <-q.Idle()
+
+ disambiguateRoot.Range(func(k, _ any) bool {
+ m := k.(module.Version)
+ roots = append(roots, m)
+ pathIsRoot[m.Path] = true
+ return true
+ })
+
+ if len(roots) > len(tidy.rootModules) {
+ module.Sort(roots)
+ tidy = newRequirements(pruned, roots, tidy.direct)
+ _, err = tidy.Graph(ctx)
+ if err != nil {
+ return nil, err
+ }
+ // Adding these roots may have pulled additional modules into the module
+ // graph, causing additional packages to become ambiguous. Keep iterating
+ // until we reach a fixed point.
+ continue
+ }
+
+ break
+ }
+
return tidy, nil
}
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 9f07658..58074bb 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -1597,7 +1597,17 @@
// paths of loaded packages. We need to retain sums for all of these modules —
// not just the modules containing the actual packages — in order to rule out
// ambiguous import errors the next time we load the package.
- if ld != nil {
+ keepModSumsForZipSums := true
+ if ld == nil {
+ if cfg.BuildMod != "mod" && semver.Compare("v"+MainModules.GoVersion(), tidyGoModSumVersionV) < 0 {
+ keepModSumsForZipSums = false
+ }
+ } else {
+ keepPkgGoModSums := true
+ if (ld.Tidy || cfg.BuildMod != "mod") && semver.Compare("v"+ld.GoVersion, tidyGoModSumVersionV) < 0 {
+ keepPkgGoModSums = false
+ keepModSumsForZipSums = false
+ }
for _, pkg := range ld.pkgs {
// We check pkg.mod.Path here instead of pkg.inStd because the
// pseudo-package "C" is not in std, but not provided by any module (and
@@ -1611,7 +1621,7 @@
// However, we didn't do so before Go 1.21, and the bug is relatively
// minor, so we maintain the previous (buggy) behavior in 'go mod tidy' to
// avoid introducing unnecessary churn.
- if !ld.Tidy || semver.Compare("v"+ld.GoVersion, tidyGoModSumVersionV) >= 0 {
+ if keepPkgGoModSums {
r := resolveReplacement(pkg.mod)
keep[modkey(r)] = true
}
@@ -1671,7 +1681,9 @@
if which == addBuildListZipSums {
for _, m := range mg.BuildList() {
r := resolveReplacement(m)
- keep[modkey(r)] = true // we need the go version from the go.mod file to do anything useful with the zipfile
+ if keepModSumsForZipSums {
+ keep[modkey(r)] = true // we need the go version from the go.mod file to do anything useful with the zipfile
+ }
keep[r] = true
}
}
diff --git a/src/cmd/go/internal/modload/query_test.go b/src/cmd/go/internal/modload/query_test.go
index fe9ae9f..93f8f0d 100644
--- a/src/cmd/go/internal/modload/query_test.go
+++ b/src/cmd/go/internal/modload/query_test.go
@@ -55,6 +55,7 @@
os.Setenv("GOPATH", dir)
cfg.BuildContext.GOPATH = dir
cfg.GOMODCACHE = filepath.Join(dir, "pkg/mod")
+ cfg.SumdbDir = filepath.Join(dir, "pkg/sumdb")
m.Run()
return nil
}
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 28bfabd..6815bbc 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -1148,7 +1148,15 @@
func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) error {
// Wait for previous test to get started and print its first json line.
- <-r.prev
+ select {
+ case <-r.prev:
+ case <-base.Interrupted:
+ // We can't wait for the previous test action to complete: we don't start
+ // new actions after an interrupt, so if that action wasn't already running
+ // it might never happen. Instead, just don't log anything for this action.
+ base.SetExitStatus(1)
+ return nil
+ }
if a.Failed {
// We were unable to build the binary.
diff --git a/src/cmd/go/testdata/script/build_cwd_newline.txt b/src/cmd/go/testdata/script/build_cwd_newline.txt
index 574464c..91cb57f 100644
--- a/src/cmd/go/testdata/script/build_cwd_newline.txt
+++ b/src/cmd/go/testdata/script/build_cwd_newline.txt
@@ -11,29 +11,47 @@
exec pwd
cp $WORK/go.mod ./go.mod
cp $WORK/main.go ./main.go
+cp $WORK/main_nocgo.go ./main_nocgo.go
cp $WORK/main_test.go ./main_test.go
! go build -o $devnull .
stderr 'package example: invalid package directory .*uh-oh'
-! go build -o $devnull main.go
+[cgo] ! go build -o $devnull main.go
+[!cgo] ! go build -o $devnull main_nocgo.go
stderr 'package command-line-arguments: invalid package directory .*uh-oh'
! go run .
stderr 'package example: invalid package directory .*uh-oh'
-! go run main.go
+[cgo] ! go run main.go
+[!cgo] ! go run main_nocgo.go
stderr 'package command-line-arguments: invalid package directory .*uh-oh'
! go test .
stderr 'package example: invalid package directory .*uh-oh'
-! go test -v main.go main_test.go
+[cgo] ! go test -v main.go main_test.go
+[!cgo] ! go test -v main_nocgo.go main_test.go
stderr 'package command-line-arguments: invalid package directory .*uh-oh'
go list -compiled -e -f '{{with .CompiledGoFiles}}{{.}}{{end}}' .
! stdout .
! stderr .
+! exists obj_
+
+
+# The cgo tool should only accept the source file if the working directory
+# is not written in line directives in the resulting files.
+
+[cgo] ! go tool cgo main.go
+[cgo] stderr 'cgo: input path contains newline character: .*uh-oh'
+[cgo] ! exists _obj
+
+[cgo] go tool cgo -trimpath=$PWD main.go
+[cgo] grep '//line main\.go:1:1' _obj/main.cgo1.go
+[cgo] ! grep 'uh-oh' _obj/main.cgo1.go
+[cgo] rm _obj
# Since we do preserve $PWD (or set it appropriately) for commands, and we do
@@ -46,19 +64,22 @@
symlink $WORK${/}link -> $DIR
-go run $WORK${/}link${/}main.go
+[cgo] go run $WORK${/}link${/}main.go
+[!cgo] go run $WORK${/}link${/}main_nocgo.go
! stdout panic
! stderr panic
stderr '^ok$'
-go test -v $WORK${/}link${/}main.go $WORK${/}link${/}main_test.go
+[cgo] go test -v $WORK${/}link${/}main.go $WORK${/}link${/}main_test.go
+[!cgo] go test -v $WORK${/}link${/}main_nocgo.go $WORK${/}link${/}main_test.go
! stdout panic
! stderr panic
stdout '^ok$' # 'go test' combines the test's stdout into stderr
cd $WORK/link
-! go run $DIR${/}main.go
+[cgo] ! go run $DIR${/}main.go
+[!cgo] ! go run $DIR${/}main_nocgo.go
stderr 'package command-line-arguments: invalid package directory .*uh-oh'
go run .
@@ -66,7 +87,8 @@
! stderr panic
stderr '^ok$'
-go run main.go
+[cgo] go run main.go
+[!cgo] go run main_nocgo.go
! stdout panic
! stderr panic
stderr '^ok$'
@@ -81,6 +103,9 @@
! stderr panic
stdout '^ok$' # 'go test' combines the test's stdout into stderr
+[cgo] go tool cgo main.go
+[cgo] grep '//line .*'${/}'link'${/}'main\.go:1:1' _obj/main.cgo1.go
+[cgo] ! grep 'uh-oh' _obj/main.cgo1.go
-- $WORK/go.mod --
module example
@@ -94,6 +119,15 @@
/* nothing here */
println("ok")
}
+-- $WORK/main_nocgo.go --
+//go:build !cgo
+
+package main
+
+func main() {
+ /* nothing here */
+ println("ok")
+}
-- $WORK/main_test.go --
package main
diff --git a/src/cmd/go/testdata/script/gccgo_link_ldflags.txt b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt
index 4e91ae5..80526c6 100644
--- a/src/cmd/go/testdata/script/gccgo_link_ldflags.txt
+++ b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt
@@ -9,6 +9,9 @@
[!exec:gccgo] skip
+# TODO: remove once gccgo on builder is updated
+[GOOS:aix] [GOARCH:ppc64] skip
+
go build -compiler gccgo
-- go.mod --
diff --git a/src/cmd/go/testdata/script/list_empty_import.txt b/src/cmd/go/testdata/script/list_empty_import.txt
new file mode 100644
index 0000000..4d76f09
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_empty_import.txt
@@ -0,0 +1,9 @@
+! go list a.go
+! stdout .
+stderr 'invalid import path'
+! stderr panic
+
+-- a.go --
+package a
+
+import ""
diff --git a/src/cmd/go/testdata/script/mod_sum_issue56222.txt b/src/cmd/go/testdata/script/mod_sum_issue56222.txt
index 4c071c1..e4a468c 100644
--- a/src/cmd/go/testdata/script/mod_sum_issue56222.txt
+++ b/src/cmd/go/testdata/script/mod_sum_issue56222.txt
@@ -23,6 +23,10 @@
go mod tidy -go=1.20
go clean -modcache # Remove checksums from the module cache, so that only go.sum is used.
+# Issue 60667: 'go list' without -mod=mod shouldn't report the checksums as
+# dirty either.
+go list -m -u all
+
env OLDSUMDB=$GOSUMDB
env GOSUMDB=bad
go mod tidy
diff --git a/src/cmd/go/testdata/script/mod_tidy_issue60313.txt b/src/cmd/go/testdata/script/mod_tidy_issue60313.txt
new file mode 100644
index 0000000..c5cd6c7
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_tidy_issue60313.txt
@@ -0,0 +1,69 @@
+# Regression test for https://go.dev/issue/60313: 'go mod tidy' did not preserve
+# dependencies needed to prevent 'ambiguous import' errors in external test
+# dependencies.
+
+cp go.mod go.mod.orig
+go mod tidy
+cmp go.mod go.mod.orig
+
+-- go.mod --
+module example
+
+go 1.20
+
+require (
+ example.net/a v0.1.0
+ example.net/b v0.1.0
+)
+
+require example.net/outer/inner v0.1.0 // indirect
+
+replace (
+ example.net/a v0.1.0 => ./a
+ example.net/b v0.1.0 => ./b
+ example.net/outer v0.1.0 => ./outer
+ example.net/outer/inner v0.1.0 => ./inner
+)
+-- example.go --
+package example
+
+import (
+ _ "example.net/a"
+ _ "example.net/b"
+)
+-- a/go.mod --
+module example.net/a
+
+go 1.20
+
+require example.net/outer/inner v0.1.0
+-- a/a.go --
+package a
+-- a/a_test.go --
+package a_test
+
+import _ "example.net/outer/inner"
+-- b/go.mod --
+module example.net/b
+
+go 1.20
+
+require example.net/outer v0.1.0
+-- b/b.go --
+package b
+-- b/b_test.go --
+package b_test
+
+import _ "example.net/outer/inner"
+-- inner/go.mod --
+module example.net/outer/inner
+
+go 1.20
+-- inner/inner.go --
+package inner
+-- outer/go.mod --
+module example.net/outer
+
+go 1.20
+-- outer/inner/inner.go --
+package inner
diff --git a/src/cmd/go/testdata/script/test_flags.txt b/src/cmd/go/testdata/script/test_flags.txt
index 63385e6..3f7964b 100644
--- a/src/cmd/go/testdata/script/test_flags.txt
+++ b/src/cmd/go/testdata/script/test_flags.txt
@@ -15,8 +15,7 @@
# Even though ./x looks like a package path, the real package should be
# the implicit '.'.
! go test --answer=42 ./x
-stderr '^no Go files in .+$'
-! stderr '/x'
+stderr '^no Go files in '$PWD'$'
# However, *flags* that appear after unrecognized flags should still be
# interpreted as flags, under the (possibly-erroneous) assumption that
diff --git a/src/context/context_test.go b/src/context/context_test.go
index eb5a86b..1c770cd 100644
--- a/src/context/context_test.go
+++ b/src/context/context_test.go
@@ -670,26 +670,26 @@
}
func XTestWithValueChecksKey(t testingT) {
- panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") })
+ panicVal := recoveredValue(func() { _ = WithValue(Background(), []byte("foo"), "bar") })
if panicVal == nil {
t.Error("expected panic")
}
- panicVal = recoveredValue(func() { WithValue(Background(), nil, "bar") })
+ panicVal = recoveredValue(func() { _ = WithValue(Background(), nil, "bar") })
if got, want := fmt.Sprint(panicVal), "nil key"; got != want {
t.Errorf("panic = %q; want %q", got, want)
}
}
func XTestInvalidDerivedFail(t testingT) {
- panicVal := recoveredValue(func() { WithCancel(nil) })
+ panicVal := recoveredValue(func() { _, _ = WithCancel(nil) })
if panicVal == nil {
t.Error("expected panic")
}
- panicVal = recoveredValue(func() { WithDeadline(nil, time.Now().Add(shortDuration)) })
+ panicVal = recoveredValue(func() { _, _ = WithDeadline(nil, time.Now().Add(shortDuration)) })
if panicVal == nil {
t.Error("expected panic")
}
- panicVal = recoveredValue(func() { WithValue(nil, "foo", "bar") })
+ panicVal = recoveredValue(func() { _ = WithValue(nil, "foo", "bar") })
if panicVal == nil {
t.Error("expected panic")
}
diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go
index 68272af..03a9a72 100644
--- a/src/crypto/ecdsa/ecdsa.go
+++ b/src/crypto/ecdsa/ecdsa.go
@@ -380,7 +380,7 @@
// an integer modulo N. This is the absolute worst of all worlds: we still
// have to reduce, because the result might still overflow N, but to take
// the left-most bits for P-521 we have to do a right shift.
- if size := c.N.Size(); len(hash) > size {
+ if size := c.N.Size(); len(hash) >= size {
hash = hash[:size]
if excess := len(hash)*8 - c.N.BitLen(); excess > 0 {
hash = bytes.Clone(hash)
diff --git a/src/crypto/ecdsa/ecdsa_test.go b/src/crypto/ecdsa/ecdsa_test.go
index 95c78c8..08a0903 100644
--- a/src/crypto/ecdsa/ecdsa_test.go
+++ b/src/crypto/ecdsa/ecdsa_test.go
@@ -9,6 +9,7 @@
"bytes"
"compress/bzip2"
"crypto/elliptic"
+ "crypto/internal/bigmod"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
@@ -398,6 +399,20 @@
}
}
+func TestHashToNat(t *testing.T) {
+ t.Run("P-224", func(t *testing.T) { testHashToNat(t, p224()) })
+ t.Run("P-256", func(t *testing.T) { testHashToNat(t, p256()) })
+ t.Run("P-384", func(t *testing.T) { testHashToNat(t, p384()) })
+ t.Run("P-521", func(t *testing.T) { testHashToNat(t, p521()) })
+}
+
+func testHashToNat[Point nistPoint[Point]](t *testing.T, c *nistCurve[Point]) {
+ for l := 0; l < 600; l++ {
+ h := bytes.Repeat([]byte{0xff}, l)
+ hashToNat(c, bigmod.NewNat(), h)
+ }
+}
+
func TestZeroSignature(t *testing.T) {
testAllCurves(t, testZeroSignature)
}
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index 164c47f..d4cbb82 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -500,22 +500,21 @@
return true
}
- // Every expected chain should match 1 returned chain
+ // Every expected chain should match one (or more) returned chain. We tolerate multiple
+ // matches, as due to root store semantics it is plausible that (at least on the system
+ // verifiers) multiple identical (looking) chains may be returned when two roots with the
+ // same subject are present.
for _, expectedChain := range test.expectedChains {
- nChainMatched := 0
+ var match bool
for _, chain := range chains {
if doesMatch(expectedChain, chain) {
- nChainMatched++
+ match = true
+ break
}
}
- if nChainMatched != 1 {
- t.Errorf("Got %v matches instead of %v for expected chain %v", nChainMatched, 1, expectedChain)
- for _, chain := range chains {
- if doesMatch(expectedChain, chain) {
- t.Errorf("\t matched %v", chainToDebugString(chain))
- }
- }
+ if !match {
+ t.Errorf("No match found for %v", expectedChain)
}
}
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index 37d82ac..6a79862 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -1238,7 +1238,7 @@
func BenchmarkSprintfPadding(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%16f", 1.0)
+ _ = Sprintf("%16f", 1.0)
}
})
}
@@ -1246,7 +1246,7 @@
func BenchmarkSprintfEmpty(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("")
+ _ = Sprintf("")
}
})
}
@@ -1254,7 +1254,7 @@
func BenchmarkSprintfString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%s", "hello")
+ _ = Sprintf("%s", "hello")
}
})
}
@@ -1262,7 +1262,7 @@
func BenchmarkSprintfTruncateString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%.3s", "日本語日本語日本語日本語")
+ _ = Sprintf("%.3s", "日本語日本語日本語日本語")
}
})
}
@@ -1271,7 +1271,7 @@
var bytes any = []byte("日本語日本語日本語日本語")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%.3s", bytes)
+ _ = Sprintf("%.3s", bytes)
}
})
}
@@ -1279,7 +1279,7 @@
func BenchmarkSprintfSlowParsingPath(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%.v", nil)
+ _ = Sprintf("%.v", nil)
}
})
}
@@ -1287,7 +1287,7 @@
func BenchmarkSprintfQuoteString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%q", "日本語日本語日本語")
+ _ = Sprintf("%q", "日本語日本語日本語")
}
})
}
@@ -1295,7 +1295,7 @@
func BenchmarkSprintfInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%d", 5)
+ _ = Sprintf("%d", 5)
}
})
}
@@ -1303,7 +1303,7 @@
func BenchmarkSprintfIntInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%d %d", 5, 6)
+ _ = Sprintf("%d %d", 5, 6)
}
})
}
@@ -1311,7 +1311,7 @@
func BenchmarkSprintfPrefixedInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
+ _ = Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
}
})
}
@@ -1319,7 +1319,7 @@
func BenchmarkSprintfFloat(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%g", 5.23184)
+ _ = Sprintf("%g", 5.23184)
}
})
}
@@ -1327,7 +1327,7 @@
func BenchmarkSprintfComplex(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%f", 5.23184+5.23184i)
+ _ = Sprintf("%f", 5.23184+5.23184i)
}
})
}
@@ -1335,7 +1335,7 @@
func BenchmarkSprintfBoolean(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%t", true)
+ _ = Sprintf("%t", true)
}
})
}
@@ -1343,7 +1343,7 @@
func BenchmarkSprintfHexString(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("% #x", "0123456789abcdef")
+ _ = Sprintf("% #x", "0123456789abcdef")
}
})
}
@@ -1352,7 +1352,7 @@
data := []byte("0123456789abcdef")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("% #x", data)
+ _ = Sprintf("% #x", data)
}
})
}
@@ -1361,7 +1361,7 @@
data := []byte("0123456789abcdef")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%v", data)
+ _ = Sprintf("%v", data)
}
})
}
@@ -1370,7 +1370,7 @@
stringer := I(12345)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%v", stringer)
+ _ = Sprintf("%v", stringer)
}
})
}
@@ -1379,7 +1379,7 @@
s := &[]any{SI{12345}, map[int]string{0: "hello"}}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
- Sprintf("%#v", s)
+ _ = Sprintf("%#v", s)
}
})
}
@@ -1428,14 +1428,14 @@
desc string
fn func()
}{
- {0, `Sprintf("")`, func() { Sprintf("") }},
- {1, `Sprintf("xxx")`, func() { Sprintf("xxx") }},
- {0, `Sprintf("%x")`, func() { Sprintf("%x", 7) }},
- {1, `Sprintf("%x")`, func() { Sprintf("%x", 1<<16) }},
- {3, `Sprintf("%80000s")`, func() { Sprintf("%80000s", "hello") }}, // large buffer (>64KB)
- {1, `Sprintf("%s")`, func() { Sprintf("%s", "hello") }},
- {1, `Sprintf("%x %x")`, func() { Sprintf("%x %x", 7, 112) }},
- {1, `Sprintf("%g")`, func() { Sprintf("%g", float32(3.14159)) }},
+ {0, `Sprintf("")`, func() { _ = Sprintf("") }},
+ {1, `Sprintf("xxx")`, func() { _ = Sprintf("xxx") }},
+ {0, `Sprintf("%x")`, func() { _ = Sprintf("%x", 7) }},
+ {1, `Sprintf("%x")`, func() { _ = Sprintf("%x", 1<<16) }},
+ {3, `Sprintf("%80000s")`, func() { _ = Sprintf("%80000s", "hello") }}, // large buffer (>64KB)
+ {1, `Sprintf("%s")`, func() { _ = Sprintf("%s", "hello") }},
+ {1, `Sprintf("%x %x")`, func() { _ = Sprintf("%x %x", 7, 112) }},
+ {1, `Sprintf("%g")`, func() { _ = Sprintf("%g", float32(3.14159)) }},
{0, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }},
{0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 7) }},
{0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 1<<16) }},
@@ -1773,13 +1773,13 @@
func TestBadVerbRecursion(t *testing.T) {
failed := false
r := &Recur{3, &failed}
- Sprintf("recur@%p value: %d\n", &r, r.i)
+ _ = Sprintf("recur@%p value: %d\n", &r, r.i)
if failed {
t.Error("fail with pointer")
}
failed = false
r = &Recur{4, &failed}
- Sprintf("recur@%p, value: %d\n", r, r.i)
+ _ = Sprintf("recur@%p, value: %d\n", r, r.i)
if failed {
t.Error("fail with value")
}
diff --git a/src/go/build/read.go b/src/go/build/read.go
index 52adfea..adcf82f 100644
--- a/src/go/build/read.go
+++ b/src/go/build/read.go
@@ -11,6 +11,7 @@
"fmt"
"go/ast"
"go/parser"
+ "go/scanner"
"go/token"
"io"
"strconv"
@@ -459,6 +460,13 @@
if err != nil {
return fmt.Errorf("parser returned invalid quoted string: <%s>", quoted)
}
+ if !isValidImport(path) {
+ // The parser used to return a parse error for invalid import paths, but
+ // no longer does, so check for and create the error here instead.
+ info.parseErr = scanner.Error{Pos: info.fset.Position(spec.Pos()), Msg: "invalid import path: " + path}
+ info.imports = nil
+ return nil
+ }
if path == "embed" {
hasEmbed = true
}
@@ -504,6 +512,20 @@
return nil
}
+// isValidImport checks if the import is a valid import using the more strict
+// checks allowed by the implementation restriction in https://go.dev/ref/spec#Import_declarations.
+// It was ported from the function of the same name that was removed from the
+// parser in CL 424855, when the parser stopped doing these checks.
+func isValidImport(s string) bool {
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return false
+ }
+ }
+ return s != ""
+}
+
// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
// This is based on a similar function in cmd/compile/internal/gc/noder.go;
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index 741e3f7..46131c6 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -75,10 +75,11 @@
// white space). If there's a difference and SourcePos is set in
// ConfigMode, //line directives are used in the output to restore
// original source positions for a reader.
- pos token.Position // current position in AST (source) space
- out token.Position // current position in output space
- last token.Position // value of pos after calling writeString
- linePtr *int // if set, record out.Line for the next token in *linePtr
+ pos token.Position // current position in AST (source) space
+ out token.Position // current position in output space
+ last token.Position // value of pos after calling writeString
+ linePtr *int // if set, record out.Line for the next token in *linePtr
+ sourcePosErr error // if non-nil, the first error emitting a //line directive
// The list of all source comments, in order of appearance.
comments []*ast.CommentGroup // may be nil
@@ -196,6 +197,13 @@
// writeLineDirective writes a //line directive if necessary.
func (p *printer) writeLineDirective(pos token.Position) {
if pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) {
+ if strings.ContainsAny(pos.Filename, "\r\n") {
+ if p.sourcePosErr == nil {
+ p.sourcePosErr = fmt.Errorf("go/printer: source filename contains unexpected newline character: %q", pos.Filename)
+ }
+ return
+ }
+
p.output = append(p.output, tabwriter.Escape) // protect '\n' in //line from tabwriter interpretation
p.output = append(p.output, fmt.Sprintf("//line %s:%d\n", pos.Filename, pos.Line)...)
p.output = append(p.output, tabwriter.Escape)
@@ -1169,7 +1177,7 @@
goto unsupported
}
- return nil
+ return p.sourcePosErr
unsupported:
return fmt.Errorf("go/printer: unsupported node type %T", node)
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index cb62b3e..3a8ce60 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -797,3 +797,31 @@
t.Fatalf("got %q, want %q", got, want)
}
}
+
+func TestSourcePosNewline(t *testing.T) {
+ // We don't provide a syntax for escaping or unescaping characters in line
+ // directives (see https://go.dev/issue/24183#issuecomment-372449628).
+ // As a result, we cannot write a line directive with the correct path for a
+ // filename containing newlines. We should return an error rather than
+ // silently dropping or mangling it.
+
+ fname := "foo\nbar/bar.go"
+ src := `package bar`
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, fname, src, parser.ParseComments|parser.AllErrors|parser.SkipObjectResolution)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cfg := &Config{
+ Mode: SourcePos, // emit line comments
+ Tabwidth: 8,
+ }
+ var buf bytes.Buffer
+ if err := cfg.Fprint(&buf, fset, f); err == nil {
+ t.Errorf("Fprint did not error for source file path containing newline")
+ }
+ if buf.Len() != 0 {
+ t.Errorf("unexpected Fprint output:\n%s", buf.Bytes())
+ }
+}
diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go
index 0d92fe5..f03272a 100644
--- a/src/net/http/http_test.go
+++ b/src/net/http/http_test.go
@@ -48,35 +48,6 @@
}
}
-func TestCleanHost(t *testing.T) {
- tests := []struct {
- in, want string
- }{
- {"www.google.com", "www.google.com"},
- {"www.google.com foo", "www.google.com"},
- {"www.google.com/foo", "www.google.com"},
- {" first character is a space", ""},
- {"[1::6]:8080", "[1::6]:8080"},
-
- // Punycode:
- {"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"},
- {"bücher.de", "xn--bcher-kva.de"},
- {"bücher.de:8080", "xn--bcher-kva.de:8080"},
- // Verify we convert to lowercase before punycode:
- {"BÜCHER.de", "xn--bcher-kva.de"},
- {"BÜCHER.de:8080", "xn--bcher-kva.de:8080"},
- // Verify we normalize to NFC before punycode:
- {"gophér.nfc", "xn--gophr-esa.nfc"}, // NFC input; no work needed
- {"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input
- }
- for _, tt := range tests {
- got := cleanHost(tt.in)
- if tt.want != got {
- t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want)
- }
- }
-}
-
// Test that cmd/go doesn't link in the HTTP server.
//
// This catches accidental dependencies between the HTTP transport and
diff --git a/src/net/http/request.go b/src/net/http/request.go
index a45c9e3..9c888b3 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -17,7 +17,6 @@
"io"
"mime"
"mime/multipart"
- "net"
"net/http/httptrace"
"net/http/internal/ascii"
"net/textproto"
@@ -27,6 +26,7 @@
"strings"
"sync"
+ "golang.org/x/net/http/httpguts"
"golang.org/x/net/idna"
)
@@ -575,12 +575,19 @@
// is not given, use the host from the request URL.
//
// Clean the host, in case it arrives with unexpected stuff in it.
- host := cleanHost(r.Host)
+ host := r.Host
if host == "" {
if r.URL == nil {
return errMissingHost
}
- host = cleanHost(r.URL.Host)
+ host = r.URL.Host
+ }
+ host, err = httpguts.PunycodeHostPort(host)
+ if err != nil {
+ return err
+ }
+ if !httpguts.ValidHostHeader(host) {
+ return errors.New("http: invalid Host header")
}
// According to RFC 6874, an HTTP client, proxy, or other
@@ -737,40 +744,6 @@
return idna.Lookup.ToASCII(v)
}
-// cleanHost cleans up the host sent in request's Host header.
-//
-// It both strips anything after '/' or ' ', and puts the value
-// into Punycode form, if necessary.
-//
-// Ideally we'd clean the Host header according to the spec:
-//
-// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
-// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
-// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host)
-//
-// But practically, what we are trying to avoid is the situation in
-// issue 11206, where a malformed Host header used in the proxy context
-// would create a bad request. So it is enough to just truncate at the
-// first offending character.
-func cleanHost(in string) string {
- if i := strings.IndexAny(in, " /"); i != -1 {
- in = in[:i]
- }
- host, port, err := net.SplitHostPort(in)
- if err != nil { // input was just a host
- a, err := idnaASCII(in)
- if err != nil {
- return in // garbage in, garbage out
- }
- return a
- }
- a, err := idnaASCII(host)
- if err != nil {
- return in // garbage in, garbage out
- }
- return net.JoinHostPort(a, port)
-}
-
// removeZone removes IPv6 zone identifier from host.
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
func removeZone(host string) string {
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index 23e49d6..86c68e4 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -774,15 +774,8 @@
}
req.Host = "foo.com with spaces"
req.URL.Host = "foo.com with spaces"
- req.Write(logWrites{t, &got})
- want := []string{
- "GET /after HTTP/1.1\r\n",
- "Host: foo.com\r\n",
- "User-Agent: " + DefaultUserAgent + "\r\n",
- "\r\n",
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("Writes = %q\n Want = %q", got, want)
+ if err := req.Write(logWrites{t, &got}); err == nil {
+ t.Errorf("Writing request with invalid Host: succeded, want error")
}
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 245f73b..f4896c5 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -6654,3 +6654,22 @@
}
wg.Wait()
}
+
+func TestRequestSanitization(t *testing.T) { run(t, testRequestSanitization) }
+func testRequestSanitization(t *testing.T, mode testMode) {
+ if mode == http2Mode {
+ // Remove this after updating x/net.
+ t.Skip("https://go.dev/issue/60374 test fails when run with HTTP/2")
+ }
+ ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
+ if h, ok := req.Header["X-Evil"]; ok {
+ t.Errorf("request has X-Evil header: %q", h)
+ }
+ })).ts
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req.Host = "go.dev\r\nX-Evil:evil"
+ resp, _ := ts.Client().Do(req)
+ if resp != nil {
+ resp.Body.Close()
+ }
+}
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 6268c08..862f72d 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -14,6 +14,7 @@
such as breaking addresses across lines.
- No unicode normalization is performed.
- The special characters ()[]:;@\, are allowed to appear unquoted in names.
+ - A leading From line is permitted, as in mbox format (RFC 4155).
*/
package mail
@@ -53,7 +54,7 @@
func ReadMessage(r io.Reader) (msg *Message, err error) {
tp := textproto.NewReader(bufio.NewReader(r))
- hdr, err := tp.ReadMIMEHeader()
+ hdr, err := readHeader(tp)
if err != nil {
return nil, err
}
@@ -64,6 +65,54 @@
}, nil
}
+// readHeader reads the message headers from r.
+// This is like textproto.ReadMIMEHeader, but doesn't validate.
+// The fix for issue #53188 tightened up net/textproto to enforce
+// restrictions of RFC 7230.
+// This package implements RFC 5322, which does not have those restrictions.
+// This function copies the relevant code from net/textproto,
+// simplified for RFC 5322.
+func readHeader(r *textproto.Reader) (map[string][]string, error) {
+ m := make(map[string][]string)
+
+ // The first line cannot start with a leading space.
+ if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
+ line, err := r.ReadLine()
+ if err != nil {
+ return m, err
+ }
+ return m, errors.New("malformed initial line: " + line)
+ }
+
+ for {
+ kv, err := r.ReadContinuedLine()
+ if kv == "" {
+ return m, err
+ }
+
+ // Key ends at first colon.
+ k, v, ok := strings.Cut(kv, ":")
+ if !ok {
+ return m, errors.New("malformed header line: " + kv)
+ }
+ key := textproto.CanonicalMIMEHeaderKey(k)
+
+ // Permit empty key, because that is what we did in the past.
+ if key == "" {
+ continue
+ }
+
+ // Skip initial spaces in value.
+ value := strings.TrimLeft(v, " \t")
+
+ m[key] = append(m[key], value)
+
+ if err != nil {
+ return m, err
+ }
+ }
+}
+
// Layouts suitable for passing to time.Parse.
// These are tried in order.
var (
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index 61e50cc..c37f392 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -39,6 +39,34 @@
},
body: "This is a message just to say hello.\nSo, \"Hello\".\n",
},
+ {
+ // RFC 5322 permits any printable ASCII character,
+ // except colon, in a header key. Issue #58862.
+ in: `From: iant@golang.org
+Custom/Header: v
+
+Body
+`,
+ header: Header{
+ "From": []string{"iant@golang.org"},
+ "Custom/Header": []string{"v"},
+ },
+ body: "Body\n",
+ },
+ {
+ // RFC 4155 mbox format. We've historically permitted this,
+ // so we continue to permit it. Issue #60332.
+ in: `From iant@golang.org Mon Jun 19 00:00:00 2023
+From: iant@golang.org
+
+Hello, gophers!
+`,
+ header: Header{
+ "From": []string{"iant@golang.org"},
+ "From iant@golang.org Mon Jun 19 00": []string{"00:00 2023"},
+ },
+ body: "Hello, gophers!\n",
+ },
}
func TestParsing(t *testing.T) {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 8d5456a..043f4f2 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -2351,10 +2351,15 @@
// Callers passing a non-nil P must call from a non-preemptible context. See
// comment on acquirem below.
//
+// Argument lockheld indicates whether the caller already acquired the
+// scheduler lock. Callers holding the lock when making the call must pass
+// true. The lock might be temporarily dropped, but will be reacquired before
+// returning.
+//
// Must not have write barriers because this may be called without a P.
//
//go:nowritebarrierrec
-func startm(pp *p, spinning bool) {
+func startm(pp *p, spinning, lockheld bool) {
// Disable preemption.
//
// Every owned P must have an owner that will eventually stop it in the
@@ -2372,7 +2377,9 @@
// startm. Callers passing a nil P may be preemptible, so we must
// disable preemption before acquiring a P from pidleget below.
mp := acquirem()
- lock(&sched.lock)
+ if !lockheld {
+ lock(&sched.lock)
+ }
if pp == nil {
if spinning {
// TODO(prattmic): All remaining calls to this function
@@ -2382,7 +2389,9 @@
}
pp, _ = pidleget(0)
if pp == nil {
- unlock(&sched.lock)
+ if !lockheld {
+ unlock(&sched.lock)
+ }
releasem(mp)
return
}
@@ -2396,6 +2405,8 @@
// could find no idle P while checkdead finds a runnable G but
// no running M's because this new M hasn't started yet, thus
// throwing in an apparent deadlock.
+ // This apparent deadlock is possible when startm is called
+ // from sysmon, which doesn't count as a running M.
//
// Avoid this situation by pre-allocating the ID for the new M,
// thus marking it as 'running' before we drop sched.lock. This
@@ -2410,12 +2421,18 @@
fn = mspinning
}
newm(fn, pp, id)
+
+ if lockheld {
+ lock(&sched.lock)
+ }
// Ownership transfer of pp committed by start in newm.
// Preemption is now safe.
releasem(mp)
return
}
- unlock(&sched.lock)
+ if !lockheld {
+ unlock(&sched.lock)
+ }
if nmp.spinning {
throw("startm: m is spinning")
}
@@ -2444,24 +2461,24 @@
// if it has local work, start it straight away
if !runqempty(pp) || sched.runqsize != 0 {
- startm(pp, false)
+ startm(pp, false, false)
return
}
// if there's trace work to do, start it straight away
if (trace.enabled || trace.shutdown) && traceReaderAvailable() != nil {
- startm(pp, false)
+ startm(pp, false, false)
return
}
// if it has GC work, start it straight away
if gcBlackenEnabled != 0 && gcMarkWorkAvailable(pp) {
- startm(pp, false)
+ startm(pp, false, false)
return
}
// no local work, check that there are no spinning/idle M's,
// otherwise our help is not required
if sched.nmspinning.Load()+sched.npidle.Load() == 0 && sched.nmspinning.CompareAndSwap(0, 1) { // TODO: fast atomic
sched.needspinning.Store(0)
- startm(pp, true)
+ startm(pp, true, false)
return
}
lock(&sched.lock)
@@ -2483,14 +2500,14 @@
}
if sched.runqsize != 0 {
unlock(&sched.lock)
- startm(pp, false)
+ startm(pp, false, false)
return
}
// If this is the last running P and nobody is polling network,
// need to wakeup another M to poll network.
if sched.npidle.Load() == gomaxprocs-1 && sched.lastpoll.Load() != 0 {
unlock(&sched.lock)
- startm(pp, false)
+ startm(pp, false, false)
return
}
@@ -2539,7 +2556,7 @@
// see at least one running M (ours).
unlock(&sched.lock)
- startm(pp, true)
+ startm(pp, true, false)
releasem(mp)
}
@@ -3292,8 +3309,8 @@
break
}
+ startm(pp, false, true)
unlock(&sched.lock)
- startm(pp, false)
releasem(mp)
}
}
@@ -4334,6 +4351,7 @@
pp.goidcache++
if raceenabled {
newg.racectx = racegostart(callerpc)
+ newg.raceignore = 0
if newg.labels != nil {
// See note in proflabel.go on labelSync's role in synchronizing
// with the reads in the signal handler.
@@ -5397,7 +5415,7 @@
// See issue 42515 and
// https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094.
if next := timeSleepUntil(); next < now {
- startm(nil, false)
+ startm(nil, false, false)
}
}
if scavenger.sysmonWake.Load() != 0 {
@@ -5669,7 +5687,7 @@
globrunqputbatch(&sched.disable.runnable, n)
unlock(&sched.lock)
for ; n != 0 && sched.npidle.Load() != 0; n-- {
- startm(nil, false)
+ startm(nil, false, false)
}
} else {
unlock(&sched.lock)
diff --git a/src/runtime/race/race_linux_test.go b/src/runtime/race/race_linux_test.go
index e8a2d0f..947ed7c 100644
--- a/src/runtime/race/race_linux_test.go
+++ b/src/runtime/race/race_linux_test.go
@@ -35,3 +35,31 @@
t.Fatalf("bad atomic value: %v, want 2", *a)
}
}
+
+func TestAtomicPageBoundary(t *testing.T) {
+ // Test that atomic access near (but not cross) a page boundary
+ // doesn't fault. See issue 60825.
+
+ // Mmap two pages of memory, and make the second page inaccessible,
+ // so we have an address at the end of a page.
+ pagesize := syscall.Getpagesize()
+ b, err := syscall.Mmap(0, 0, 2*pagesize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
+ if err != nil {
+ t.Fatalf("mmap failed %s", err)
+ }
+ defer syscall.Munmap(b)
+ err = syscall.Mprotect(b[pagesize:], syscall.PROT_NONE)
+ if err != nil {
+ t.Fatalf("mprotect high failed %s\n", err)
+ }
+
+ // This should not fault.
+ a := (*uint32)(unsafe.Pointer(&b[pagesize-4]))
+ atomic.StoreUint32(a, 1)
+ if x := atomic.LoadUint32(a); x != 1 {
+ t.Fatalf("bad atomic value: %v, want 1", x)
+ }
+ if x := atomic.AddUint32(a, 1); x != 2 {
+ t.Fatalf("bad atomic value: %v, want 2", x)
+ }
+}
diff --git a/src/runtime/race/testdata/mop_test.go b/src/runtime/race/testdata/mop_test.go
index 0d79091..4a9ce26 100644
--- a/src/runtime/race/testdata/mop_test.go
+++ b/src/runtime/race/testdata/mop_test.go
@@ -2092,3 +2092,40 @@
<-done
}
}
+
+func TestNoRaceIssue60934(t *testing.T) {
+ // Test that runtime.RaceDisable state doesn't accidentally get applied to
+ // new goroutines.
+
+ // Create several goroutines that end after calling runtime.RaceDisable.
+ var wg sync.WaitGroup
+ ready := make(chan struct{})
+ wg.Add(32)
+ for i := 0; i < 32; i++ {
+ go func() {
+ <-ready // ensure we have multiple goroutines running at the same time
+ runtime.RaceDisable()
+ wg.Done()
+ }()
+ }
+ close(ready)
+ wg.Wait()
+
+ // Make sure race detector still works. If the runtime.RaceDisable state
+ // leaks, the happens-before edges here will be ignored and a race on x will
+ // be reported.
+ var x int
+ ch := make(chan struct{}, 0)
+ wg.Add(2)
+ go func() {
+ x = 1
+ ch <- struct{}{}
+ wg.Done()
+ }()
+ go func() {
+ <-ch
+ _ = x
+ wg.Done()
+ }()
+ wg.Wait()
+}
diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s
index c679a87..34ec200 100644
--- a/src/runtime/race_amd64.s
+++ b/src/runtime/race_amd64.s
@@ -333,7 +333,7 @@
TEXT racecallatomic<>(SB), NOSPLIT, $0-0
// Trigger SIGSEGV early.
MOVQ 16(SP), R12
- MOVL (R12), R13
+ MOVBLZX (R12), R13
// Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend).
CMPQ R12, runtime·racearenastart(SB)
JB racecallatomic_data
diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s
index edbb3b1..c818345 100644
--- a/src/runtime/race_arm64.s
+++ b/src/runtime/race_arm64.s
@@ -348,7 +348,7 @@
// Trigger SIGSEGV early.
MOVD 40(RSP), R3 // 1st arg is addr. after two times BL, get it at 40(RSP)
- MOVD (R3), R13 // segv here if addr is bad
+ MOVB (R3), R13 // segv here if addr is bad
// Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend).
MOVD runtime·racearenastart(SB), R10
CMP R10, R3
diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s
index ac335b1..2826501 100644
--- a/src/runtime/race_ppc64le.s
+++ b/src/runtime/race_ppc64le.s
@@ -362,7 +362,7 @@
TEXT racecallatomic<>(SB), NOSPLIT, $0-0
// Trigger SIGSEGV early if address passed to atomic function is bad.
MOVD (R6), R7 // 1st arg is addr
- MOVD (R7), R9 // segv here if addr is bad
+ MOVB (R7), R9 // segv here if addr is bad
// Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend).
MOVD runtime·racearenastart(SB), R9
CMP R7, R9
diff --git a/src/runtime/signal_windows_test.go b/src/runtime/signal_windows_test.go
index c9b8e90..4c7a476 100644
--- a/src/runtime/signal_windows_test.go
+++ b/src/runtime/signal_windows_test.go
@@ -254,3 +254,59 @@
t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
}
}
+
+func TestIssue59213(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ t.Skip("skipping windows only test")
+ }
+ if *flagQuick {
+ t.Skip("-quick")
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ goEnv := func(arg string) string {
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "env", arg)
+ cmd.Stderr = new(bytes.Buffer)
+
+ line, err := cmd.Output()
+ if err != nil {
+ t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
+ }
+ out := string(bytes.TrimSpace(line))
+ t.Logf("%v: %q", cmd, out)
+ return out
+ }
+
+ cc := goEnv("CC")
+ cgoCflags := goEnv("CGO_CFLAGS")
+
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+ dllfile := filepath.Join(tmpdir, "test.dll")
+ exefile := filepath.Join(tmpdir, "gotest.exe")
+
+ // build go dll
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", dllfile, "-buildmode", "c-shared", "testdata/testwintls/main.go")
+ out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to build go library: %s\n%s", err, out)
+ }
+
+ // build c program
+ cmd = testenv.Command(t, cc, "-o", exefile, "testdata/testwintls/main.c")
+ testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "CGO_CFLAGS="+cgoCflags)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to build c exe: %s\n%s", err, out)
+ }
+
+ // run test program
+ cmd = testenv.Command(t, exefile, dllfile, "GoFunc")
+ out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed: %s\n%s", err, out)
+ }
+}
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index 777726f..4027770 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -10,6 +10,7 @@
// Offsets into Thread Environment Block (pointer in GS)
#define TEB_TlsSlots 0x1480
+#define TEB_ArbitraryPtr 0x28
// void runtime·asmstdcall(void *c);
TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0
@@ -427,7 +428,11 @@
// Assert that slot is less than 64 so we can use _TEB->TlsSlots
CMPQ CX, $64
JB ok
- CALL runtime·abort(SB)
+
+ // Fallback to the TEB arbitrary pointer.
+ // TODO: don't use the arbitrary pointer (see go.dev/issue/59824)
+ MOVQ $TEB_ArbitraryPtr, CX
+ JMP settls
ok:
// Convert the TLS index at CX into
// an offset from TEB_TlsSlots.
@@ -435,5 +440,6 @@
// Save offset from TLS into tls_g.
ADDQ $TEB_TlsSlots, CX
+settls:
MOVQ CX, runtime·tls_g(SB)
RET
diff --git a/src/runtime/sys_windows_arm64.s b/src/runtime/sys_windows_arm64.s
index 4702a4d..e3082a1 100644
--- a/src/runtime/sys_windows_arm64.s
+++ b/src/runtime/sys_windows_arm64.s
@@ -12,6 +12,7 @@
// Offsets into Thread Environment Block (pointer in R18)
#define TEB_error 0x68
#define TEB_TlsSlots 0x1480
+#define TEB_ArbitraryPtr 0x28
// Note: R0-R7 are args, R8 is indirect return value address,
// R9-R15 are caller-save, R19-R29 are callee-save.
@@ -415,12 +416,15 @@
// Assert that slot is less than 64 so we can use _TEB->TlsSlots
CMP $64, R0
BLT ok
- MOVD $runtime·abort(SB), R1
- BL (R1)
+ // Fallback to the TEB arbitrary pointer.
+ // TODO: don't use the arbitrary pointer (see go.dev/issue/59824)
+ MOVD $TEB_ArbitraryPtr, R0
+ B settls
ok:
// Save offset from R18 into tls_g.
LSL $3, R0
ADD $TEB_TlsSlots, R0
+settls:
MOVD R0, runtime·tls_g(SB)
RET
diff --git a/src/runtime/testdata/testprog/syscall_windows.go b/src/runtime/testdata/testprog/syscall_windows.go
index b4b6644..71bf384 100644
--- a/src/runtime/testdata/testprog/syscall_windows.go
+++ b/src/runtime/testdata/testprog/syscall_windows.go
@@ -66,5 +66,8 @@
if err != nil {
panic(err)
}
- print((mem2 - mem1) / threadCount)
+ // assumes that this process creates 1 thread for each
+ // thread locked goroutine plus extra 5 threads
+ // like sysmon and others
+ print((mem2 - mem1) / (threadCount + 5))
}
diff --git a/src/runtime/testdata/testprogcgo/stack_windows.go b/src/runtime/testdata/testprogcgo/stack_windows.go
index 846297a..0be1126 100644
--- a/src/runtime/testdata/testprogcgo/stack_windows.go
+++ b/src/runtime/testdata/testprogcgo/stack_windows.go
@@ -50,5 +50,8 @@
if err != nil {
panic(err)
}
- print((mem2 - mem1) / threadCount)
+ // assumes that this process creates 1 thread for each
+ // thread locked goroutine plus extra 5 threads
+ // like sysmon and others
+ print((mem2 - mem1) / (threadCount + 5))
}
diff --git a/src/runtime/testdata/testwintls/main.c b/src/runtime/testdata/testwintls/main.c
new file mode 100644
index 0000000..6061828
--- /dev/null
+++ b/src/runtime/testdata/testwintls/main.c
@@ -0,0 +1,29 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <windows.h>
+
+int main(int argc, char **argv) {
+ if (argc < 3) {
+ return 1;
+ }
+ // Allocate more than 64 TLS indices
+ // so the Go runtime doesn't find
+ // enough space in the TEB TLS slots.
+ for (int i = 0; i < 65; i++) {
+ TlsAlloc();
+ }
+ HMODULE hlib = LoadLibrary(argv[1]);
+ if (hlib == NULL) {
+ return 2;
+ }
+ FARPROC proc = GetProcAddress(hlib, argv[2]);
+ if (proc == NULL) {
+ return 3;
+ }
+ if (proc() != 42) {
+ return 4;
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/src/runtime/testdata/testwintls/main.go b/src/runtime/testdata/testwintls/main.go
new file mode 100644
index 0000000..1cf296c
--- /dev/null
+++ b/src/runtime/testdata/testwintls/main.go
@@ -0,0 +1,12 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() int { return 42 }
+
+func main() {}
diff --git a/src/syscall/zx/fidl/ctx_and_header.go b/src/syscall/zx/fidl/ctx_and_header.go
index 45b2a53..7f79a5d 100644
--- a/src/syscall/zx/fidl/ctx_and_header.go
+++ b/src/syscall/zx/fidl/ctx_and_header.go
@@ -13,17 +13,14 @@
// This is currently empty. We keep it around to ease the implementation of
// context-dependent behavior for future migrations.
type MarshalerContext struct {
+ // TODO(fxbug.dev/79584): This is now unused. Remove.
UseV2WireFormat bool
}
-func (ctx MarshalerContext) isV2WireFormatDecodingEnabled() bool {
- return ctx.UseV2WireFormat
-}
-
// NewCtx returns the default context.
// During migrations, this controls the default write path.
func NewCtx() MarshalerContext {
- return MarshalerContext{UseV2WireFormat: true}
+ return MarshalerContext{}
}
// MessageHeader represents a transactional message header.
@@ -35,14 +32,25 @@
Ordinal uint64 `fidl:"8" fidl_offset_v2:"8" fidl_bounds:""`
}
+// TODO(fxbug.dev/79584): Remove in favor of ValidateWireFormat.
func (msg *MessageHeader) IsSupportedVersion() bool {
return msg.Magic == FidlWireFormatMagicNumberInitial
}
+func (msg *MessageHeader) ValidateWireFormat() error {
+ if msg.Magic != FidlWireFormatMagicNumberInitial {
+ return ErrUnknownMagic
+ }
+ if msg.Flags[0]&FidlV2WireFormatFlagMask == 0 {
+ return ErrUnsupportedWireFormatVersion
+ }
+ return nil
+}
+
// NewCtx creates a new MarshalerContext for unmarshaling based on msg.Flags.
// During migrations, this controls dynamic behavior in the read path.
func (msg *MessageHeader) NewCtx() MarshalerContext {
- return MarshalerContext{UseV2WireFormat: true}
+ return MarshalerContext{}
}
var mMessageHeader = MustCreateMarshaler(MessageHeader{})
@@ -57,12 +65,8 @@
// set based on the MarshalerContext. It is the caller's responsibilty to set
// the transaction ID and method ordinal
func (ctx MarshalerContext) NewHeader() MessageHeader {
- var flagByte0 uint8
- if ctx.UseV2WireFormat {
- flagByte0 = FidlV2WireFormatFlagMask
- }
return MessageHeader{
- Flags: [3]uint8{flagByte0, 0, 0},
+ Flags: [3]uint8{FidlV2WireFormatFlagMask, 0, 0},
Magic: FidlWireFormatMagicNumberInitial,
}
}
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 5a19b59..82aeb73 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -1224,10 +1224,7 @@
}
func envelopeSize(ctx MarshalerContext) int {
- if ctx.UseV2WireFormat {
- return 8
- }
- return 16
+ return 8
}
type envelopeState int
@@ -1252,7 +1249,7 @@
func marshalEnvelopePresent(ctx MarshalerContext, m Marshaler, v unsafevalue.Value, out *encoder, offset int, depth int) error {
numHandleDispositions := len(out.handleDispositions)
- if ctx.UseV2WireFormat && m.getMarshalSize(ctx) <= 4 {
+ if m.getMarshalSize(ctx) <= 4 {
if err := m.marshal(ctx, v, out, offset, depth+1); err != nil {
return err
}
@@ -1275,26 +1272,14 @@
numHandleDispositions = len(out.handleDispositions) - numHandleDispositions
numBytes = len(out.buffer) - numBytes
- if ctx.UseV2WireFormat {
- out.writeUint32(offset, uint32(numBytes))
- out.writeUint16(offset+4, uint16(numHandleDispositions))
- out.writeUint16(offset+6, 0)
- return nil
- }
-
out.writeUint32(offset, uint32(numBytes))
- out.writeUint32(offset+4, uint32(numHandleDispositions))
- out.writeUint64(offset+8, allocPresent)
+ out.writeUint16(offset+4, uint16(numHandleDispositions))
+ out.writeUint16(offset+6, 0)
return nil
}
func marshalEnvelopeAbsent(ctx MarshalerContext, out *encoder, offset int) {
- if ctx.UseV2WireFormat {
- out.writeUint64(offset, 0) // zero envelope
- } else {
- out.writeUint64(offset, 0) // both numBytes, and numHandleDispositions
- out.writeUint64(offset+8, noAlloc)
- }
+ out.writeUint64(offset, 0) // zero envelope
}
func marshalEnvelopeUnknown(ctx MarshalerContext, out *encoder, offset int, depth int, unknownData UnknownData) error {
@@ -1308,7 +1293,7 @@
})
}
- if ctx.UseV2WireFormat && len(unknownData.Bytes) <= 4 {
+ if len(unknownData.Bytes) <= 4 {
copy(out.buffer[offset:], unknownData.Bytes)
out.writeUint16(offset+4, uint16(len(unknownData.Handles)))
out.writeUint16(offset+6, 1)
@@ -1322,16 +1307,9 @@
copy(out.buffer[outOfLineOffset:], unknownData.Bytes)
- if ctx.UseV2WireFormat {
- out.writeUint32(offset, uint32(len(unknownData.Bytes)))
- out.writeUint16(offset+4, uint16(len(unknownData.Handles)))
- out.writeUint16(offset+6, 0)
- return nil
- }
-
out.writeUint32(offset, uint32(len(unknownData.Bytes)))
- out.writeUint32(offset+4, uint32(len(unknownData.Handles)))
- out.writeUint64(offset+8, allocPresent)
+ out.writeUint16(offset+4, uint16(len(unknownData.Handles)))
+ out.writeUint16(offset+6, 0)
return nil
}
@@ -1353,63 +1331,30 @@
func unmarshalEnvelopeHeader(ctx MarshalerContext, in *decoder, offset int) (envelopeHeader, error) {
var h envelopeHeader
- if ctx.isV2WireFormatDecodingEnabled() {
- inlineIndicator := in.readUint16(offset + 6)
- switch inlineIndicator {
- case 1:
- h = envelopeHeader{
- byteValue: in.buffer[offset : offset+4],
- handleCount: uint32(in.readUint16(offset + 4)),
- state: inlineEnvelope,
- }
- case 0:
- h = envelopeHeader{
- byteCount: in.readUint32(offset),
- handleCount: uint32(in.readUint16(offset + 4)),
- }
- if h.byteCount == 0 && h.handleCount == 0 {
- h.state = emptyEnvelope
- } else {
- h.state = outOfLineEnvelope
- }
- if h.byteCount%8 != 0 {
- return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
- }
- default:
- return h, newValueError(ErrBadInlineIndicatorEncoding, h)
+ inlineIndicator := in.readUint16(offset + 6)
+ switch inlineIndicator {
+ case 1:
+ h = envelopeHeader{
+ byteValue: in.buffer[offset : offset+4],
+ handleCount: uint32(in.readUint16(offset + 4)),
+ state: inlineEnvelope,
}
-
- } else {
+ case 0:
h = envelopeHeader{
byteCount: in.readUint32(offset),
- handleCount: in.readUint32(offset + 4),
+ handleCount: uint32(in.readUint16(offset + 4)),
}
- presence := in.readUint64(offset + 8)
- switch presence {
- case allocPresent:
- h.state = outOfLineEnvelope
- if h.byteCount == 0 {
- return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
- }
- if h.byteCount%8 != 0 {
- return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
- }
- case noAlloc:
+ if h.byteCount == 0 && h.handleCount == 0 {
h.state = emptyEnvelope
- if h.byteCount != 0 {
- return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
- }
- if h.handleCount != 0 {
- return h, newValueError(ErrInvalidNumHandlesInEnvelope, h.handleCount)
- }
- default:
- return h, newValueError(ErrBadRefEncoding, h)
+ } else {
+ h.state = outOfLineEnvelope
}
+ if h.byteCount%8 != 0 {
+ return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
+ }
+ default:
+ return h, newValueError(ErrBadInlineIndicatorEncoding, h)
}
- if h.byteCount%8 != 0 {
- return h, newValueError(ErrInvalidNumBytesInEnvelope, h.byteCount)
- }
-
return h, nil
}
@@ -1453,7 +1398,7 @@
}
func unmarshalEnvelopeContent(ctx MarshalerContext, header envelopeHeader, m Marshaler, in *decoder, depth int, v unsafevalue.Value, mode unmarshalEnvelopeMode) (bool, error) {
- if ctx.isV2WireFormatDecodingEnabled() && header.state == inlineEnvelope {
+ if header.state == inlineEnvelope {
// Inline envelope.
var innerDecoderHandleInfos []zx.HandleInfo
if header.handleCount != 0 {
@@ -1500,13 +1445,11 @@
return false, err
}
- if ctx.UseV2WireFormat {
- if m.getUnmarshalSize(ctx) > 4 && header.state == inlineEnvelope {
- return false, newExpectError(ErrInvalidInlineBitValueInEnvelope, header.state, outOfLineEnvelope)
- }
- if m.getUnmarshalSize(ctx) <= 4 && header.state == outOfLineEnvelope {
- return false, newExpectError(ErrInvalidInlineBitValueInEnvelope, header.state, inlineEnvelope)
- }
+ if m.getUnmarshalSize(ctx) > 4 && header.state == inlineEnvelope {
+ return false, newExpectError(ErrInvalidInlineBitValueInEnvelope, header.state, outOfLineEnvelope)
+ }
+ if m.getUnmarshalSize(ctx) <= 4 && header.state == outOfLineEnvelope {
+ return false, newExpectError(ErrInvalidInlineBitValueInEnvelope, header.state, inlineEnvelope)
}
if !header.isPresent(ctx) {
@@ -1632,11 +1575,7 @@
}
ordinal++
- if ctx.UseV2WireFormat {
- envelopeOffset += 8
- } else {
- envelopeOffset += 16
- }
+ envelopeOffset += 8
if fieldKnown {
index++
}
diff --git a/src/syscall/zx/fidl/errors.go b/src/syscall/zx/fidl/errors.go
index 26acfe8..cbd9248 100644
--- a/src/syscall/zx/fidl/errors.go
+++ b/src/syscall/zx/fidl/errors.go
@@ -69,6 +69,7 @@
ErrValueTypeHandles
ErrExceededMaxOutOfLineDepth
ErrInvalidInlineBitValueInEnvelope
+ ErrUnsupportedWireFormatVersion
)
var errorCodeNames = []string{
@@ -111,6 +112,7 @@
ErrValueTypeHandles: "ErrValueTypeHandles",
ErrExceededMaxOutOfLineDepth: "ErrExceededMaxOutOfLineDepth",
ErrInvalidInlineBitValueInEnvelope: "ErrInvalidInlineBitValueInEnvelope",
+ ErrUnsupportedWireFormatVersion: "ErrUnsupportedWireFormatVersion",
}
func (c ErrorCode) String() string {
@@ -197,6 +199,8 @@
return "exceeded maxOutOfLineDepth"
case ErrInvalidInlineBitValueInEnvelope:
return "invalid inline bit value in envelope"
+ case ErrUnsupportedWireFormatVersion:
+ return "wire format version is not supported"
default:
return e.String()
}
@@ -260,6 +264,10 @@
return e.ErrorCode.Error() + ": " + toString(e.value)
}
+func (e valueError) Unwrap() error {
+ return e.ErrorCode
+}
+
// expectError represents an error that refers to the expectation of a
// certain value, and displays a comparison between the actual value and
// the expected value.
@@ -280,3 +288,7 @@
func (e expectError) Error() string {
return e.ErrorCode.Error() + ": expected " + toString(e.expect) + ", got " + toString(e.actual)
}
+
+func (e expectError) Unwrap() error {
+ return e.ErrorCode
+}
diff --git a/src/syscall/zx/fidl/interface.go b/src/syscall/zx/fidl/interface.go
index 9497c42..46f1646 100644
--- a/src/syscall/zx/fidl/interface.go
+++ b/src/syscall/zx/fidl/interface.go
@@ -218,8 +218,8 @@
if err != nil {
return nil, nil, err
}
- if !header.IsSupportedVersion() {
- return nil, nil, ErrUnknownMagic
+ if err := header.ValidateWireFormat(); err != nil {
+ return nil, nil, err
}
return objBytes[MessageHeaderSize:], handleInfos, nil
}
diff --git a/src/syscall/zx/zxwait/zxwait.go b/src/syscall/zx/zxwait/zxwait.go
index a7a7c4f..4d00f24 100644
--- a/src/syscall/zx/zxwait/zxwait.go
+++ b/src/syscall/zx/zxwait/zxwait.go
@@ -79,7 +79,36 @@
g uintptr
done chan<- struct{}
- obs zx.Signals
+ mu struct {
+ sync.Mutex
+ // A canceled waitingG has had its pending waiters canceled. This
+ // prevents duplicate calls to `zx.Port.Cancel` when there is a race
+ // between handle closure, and context cancelation (which can lead to
+ // the port's packet queue being in an unknown state).
+ canceled bool
+ }
+ obs zx.Signals
+}
+
+// cancelPendingWait cancels any pending waiters associated with this waitingG.
+//
+// The returned boolean is true if a pending waiter was canceled and false if
+// there were no waiters to cancel.
+func (w *waitingG) cancelPendingWait(port zx.Port, handle zx.Handle) (bool, error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if !w.mu.canceled {
+ w.mu.canceled = true
+ switch status := zx.Sys_port_cancel(zx.Handle(port), handle, w.key); status {
+ case zx.ErrOk:
+ return true, nil
+ case zx.ErrNotFound:
+ return false, nil
+ default:
+ return false, &zx.Error{Status: status, Text: "zx.Port.Cancel"}
+ }
+ }
+ return false, nil
}
// A waiter is a zircon port that parks goroutines waiting on signals.
@@ -157,18 +186,19 @@
w.mu.Lock()
defer w.mu.Unlock()
for waiting := range w.mu.allByHandle[handle] {
- switch status := zx.Sys_port_cancel(zx.Handle(w.port), handle, waiting.key); status {
- case zx.ErrOk:
+ canceled, err := waiting.cancelPendingWait(w.port, handle)
+ if err != nil {
+ return err
+ }
+ if canceled {
+ // The waiter was canceled. Artificially notify it to wake the
+ // `Dequeue` routine.
if err := w.port.Queue(zx.PortPacket{
Key: waiting.key,
Type: zx.PortPacketTypeUser,
}); err != nil {
return err
}
- case zx.ErrNotFound:
- // Nobody is waiting, no need to notify.
- default:
- return &zx.Error{Status: status, Text: "zx.Port.Cancel"}
}
}
return cb(handle)
@@ -180,6 +210,14 @@
func (w *waiter) Wait(ctx context.Context, handle zx.Handle, signals zx.Signals) (zx.Signals, error) {
var waiting *waitingG
+ // If we observe `zx.SignalHandleClosed`, generate a `zx.ErrCanceled` error.
+ errorFromSignals := func(obs zx.Signals) error {
+ if obs == zx.SignalHandleClosed {
+ return &zx.Error{Status: zx.ErrCanceled, Text: "zxwait.Wait"}
+ }
+ return nil
+ }
+
w.mu.Lock()
if len(w.mu.free) == 0 {
waiting = &waitingG{
@@ -204,18 +242,23 @@
select {
case <-ch:
// Wait complete.
- return waiting.obs, nil
+ obs := waiting.obs
+ return obs, errorFromSignals(obs)
case <-done:
// Context canceled.
- switch status := zx.Sys_port_cancel(zx.Handle(w.port), handle, waiting.key); status {
- case zx.ErrOk:
+ canceled, err := waiting.cancelPendingWait(w.port, handle)
+ if err != nil {
+ return 0, err
+ }
+ if canceled {
return 0, ctx.Err()
- case zx.ErrNotFound:
- // We lost the race.
+ } else {
+ // We lost the race and didn't cancel the waiter in time.
+ // Observe channel closure to keep the `Dequeue` routine in
+ // sync.
<-ch
- return waiting.obs, nil
- default:
- return 0, &zx.Error{Status: status, Text: "zx.Port.Cancel"}
+ obs := waiting.obs
+ return obs, errorFromSignals(obs)
}
}
}
@@ -235,6 +278,11 @@
delete(w.mu.allByHandle, handle)
}
w.mu.free = append(w.mu.free, waiting)
+
+ waiting.mu.Lock()
+ waiting.mu.canceled = false
+ waiting.mu.Unlock()
+
w.mu.Unlock()
}()
@@ -251,13 +299,7 @@
gopark(w.unlockf, waiting, waitReasonIOWait, traceEvGoBlockSelect, 0)
obs := waiting.obs
-
- return obs, func() error {
- if obs == zx.SignalHandleClosed {
- return &zx.Error{Status: zx.ErrCanceled, Text: "zxwait.Wait"}
- }
- return nil
- }()
+ return obs, errorFromSignals(obs)
}
// unlockf is passed as a callback to gopark.
diff --git a/src/syscall/zx/zxwait/zxwait_test.go b/src/syscall/zx/zxwait/zxwait_test.go
index 93a21e1..c4f7387 100644
--- a/src/syscall/zx/zxwait/zxwait_test.go
+++ b/src/syscall/zx/zxwait/zxwait_test.go
@@ -207,10 +207,13 @@
func TestWaitContext_LocalCloseRace(t *testing.T) {
var wg sync.WaitGroup
- for i := 0; i < 100; i++ {
- // Start a wait, assert the signal, and close the handle. This is a
- // regression test for an issue where internal state was reused without
- // cancelling pending waits, causing future waits to catch stale wakeups.
+ for i := 0; i < 1000; i++ {
+ // Start a wait, assert the signal, close the handle, and cancel the
+ // context. This is a regression test for two issues:
+ // 1) internal state was reused without canceling pending waits, and
+ // 2) internal state was not flagged as defunct after failing to
+ // cleanly cancel a pending wait,
+ // Both of which caused future waits to catch stale wakeups.
wg.Add(1)
go func() {
defer wg.Done()
@@ -224,14 +227,17 @@
_ = event.Close()
}()
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
// Buffer the channel to avoid leaking a goroutine in case of error
// below.
ch := make(chan error, 1)
// Capture the event by value to avoid racing against the Close call,
// which resets its receiver.
- go func(event zx.Handle) {
+ go func(event zx.Handle, ctx context.Context) {
ch <- func() error {
- obs, err := zxwait.WaitContext(context.Background(), event, zx.SignalUser0)
+ obs, err := zxwait.WaitContext(ctx, event, zx.SignalUser0)
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
@@ -252,7 +258,7 @@
}
return nil
}()
- }(event)
+ }(event, ctx)
// Yield to allow the goroutine to be scheduled, beginning the wait.
runtime.Gosched()
@@ -263,6 +269,7 @@
if err := event.Close(); err != nil {
return fmt.Errorf("failed to close event: %w", err)
}
+ cancel()
return <-ch
}(); err != nil {
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index fb60c17..fd7db65 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -361,19 +361,27 @@
// mark top of stack before any variables in the body are pushed.
mark := s.mark()
oneIteration := func(index, elem reflect.Value) {
- // Set top var (lexically the second if there are two) to the element.
if len(r.Pipe.Decl) > 0 {
if r.Pipe.IsAssign {
- s.setVar(r.Pipe.Decl[0].Ident[0], elem)
+ // With two variables, index comes first.
+ // With one, we use the element.
+ if len(r.Pipe.Decl) > 1 {
+ s.setVar(r.Pipe.Decl[0].Ident[0], index)
+ } else {
+ s.setVar(r.Pipe.Decl[0].Ident[0], elem)
+ }
} else {
+ // Set top var (lexically the second if there
+ // are two) to the element.
s.setTopVar(1, elem)
}
}
- // Set next var (lexically the first if there are two) to the index.
if len(r.Pipe.Decl) > 1 {
if r.Pipe.IsAssign {
- s.setVar(r.Pipe.Decl[1].Ident[0], index)
+ s.setVar(r.Pipe.Decl[1].Ident[0], elem)
} else {
+ // Set next var (lexically the first if there
+ // are two) to the index.
s.setTopVar(2, index)
}
}
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index 6b163f0..a7c010d 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -694,6 +694,7 @@
{"bug18c", "{{eq . 'P'}}", "true", 'P', true},
{"issue56490", "{{$i := 0}}{{$x := 0}}{{range $i = .AI}}{{end}}{{$i}}", "5", tVal, true},
+ {"issue60801", "{{$k := 0}}{{$v := 0}}{{range $k, $v = .AI}}{{$k}}={{$v}} {{end}}", "0=3 1=4 2=5 ", tVal, true},
}
func zeroArgs() string {
diff --git a/test/fixedbugs/issue60601.go b/test/fixedbugs/issue60601.go
new file mode 100644
index 0000000..5308989
--- /dev/null
+++ b/test/fixedbugs/issue60601.go
@@ -0,0 +1,50 @@
+// run
+
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "strings"
+ "unsafe"
+)
+
+func shift[T any]() int64 {
+ return 1 << unsafe.Sizeof(*new(T))
+}
+
+func div[T any]() uintptr {
+ return 1 / unsafe.Sizeof(*new(T))
+}
+
+func add[T any]() int64 {
+ return 1<<63 - 1 + int64(unsafe.Sizeof(*new(T)))
+}
+
+func main() {
+ shift[[62]byte]()
+ shift[[63]byte]()
+ shift[[64]byte]()
+ shift[[100]byte]()
+ shift[[1e6]byte]()
+
+ add[[1]byte]()
+ shouldPanic("divide by zero", func() { div[[0]byte]() })
+}
+
+func shouldPanic(str string, f func()) {
+ defer func() {
+ err := recover()
+ if err == nil {
+ panic("did not panic")
+ }
+ s := err.(error).Error()
+ if !strings.Contains(s, str) {
+ panic("got panic " + s + ", want " + str)
+ }
+ }()
+
+ f()
+}