From d6224d3807534d1d6638e5bbd1d5a1ce23e99ec9 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Sun, 16 Feb 2020 12:01:02 +1100 Subject: [PATCH 01/12] [release-branch.go1.13] runtime: ignore error returned by PowerRegisterSuspendResumeNotification It appears that PowerRegisterSuspendResumeNotification is not supported when running inside Docker - see issues #35447, #36557 and #37149. Our current code relies on error number to determine Docker environment. But we already saw PowerRegisterSuspendResumeNotification return ERROR_FILE_NOT_FOUND, ERROR_INVALID_PARAMETERS and ERROR_ACCESS_DENIED (see issues above). So this approach is not sustainable. Just ignore PowerRegisterSuspendResumeNotification returned error. For #37149 Fixes #37230 Change-Id: I2beba9d45cdb8c1efac5e974e747827a6261915a Reviewed-on: https://go-review.googlesource.com/c/go/+/219657 Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements Reviewed-by: Jason A. Donenfeld (cherry picked from commit d467f3bbc9c76805ae16ab1924c28ec3be487875) Reviewed-on: https://go-review.googlesource.com/c/go/+/224585 Run-TryBot: Ian Lance Taylor --- src/runtime/os_windows.go | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 2cf81f61a922cd..bb631b66a9adfd 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -263,9 +263,7 @@ func loadOptionalSyscalls() { func monitorSuspendResume() { const ( - _DEVICE_NOTIFY_CALLBACK = 2 - _ERROR_FILE_NOT_FOUND = 2 - _ERROR_INVALID_PARAMETERS = 87 + _DEVICE_NOTIFY_CALLBACK = 2 ) type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct { callback uintptr @@ -292,25 +290,8 @@ func monitorSuspendResume() { callback: compileCallback(*efaceOf(&fn), true), } handle := uintptr(0) - ret := stdcall3(powerRegisterSuspendResumeNotification, _DEVICE_NOTIFY_CALLBACK, + stdcall3(powerRegisterSuspendResumeNotification, _DEVICE_NOTIFY_CALLBACK, uintptr(unsafe.Pointer(¶ms)), uintptr(unsafe.Pointer(&handle))) - // This function doesn't use GetLastError(), so we use the return value directly. - switch ret { - case 0: - return // Successful, nothing more to do. - case _ERROR_FILE_NOT_FOUND: - // Systems without access to the suspend/resume notifier - // also have their clock on "program time", and therefore - // don't want or need this anyway. - return - case _ERROR_INVALID_PARAMETERS: - // This is seen when running in Windows Docker. - // See issue 36557. - return - default: - println("runtime: PowerRegisterSuspendResumeNotification failed with errno=", ret) - throw("runtime: PowerRegisterSuspendResumeNotification failure") - } } //go:nosplit From b2797dc8739d19ed788357df852daa332f632853 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Wed, 25 Mar 2020 22:24:44 -0400 Subject: [PATCH 02/12] [release-branch.go1.13] cmd/go: do not append to the global cfg.OrigEnv slice Appending to a global slice is only safe if its length is already equal to its capacity. That property is not guaranteed for slices in general, and empirically does not hold for this one. This is a minimal fix to make it easier to backport. A more robust cleanup of the base.EnvForDir function will be sent in a subsequent CL. Fixes #38082 Updates #38077 Change-Id: I731d5bbd0e516642c2cf43e713eeea15402604e5 Reviewed-on: https://go-review.googlesource.com/c/go/+/225577 Run-TryBot: Bryan C. Mills TryBot-Result: Gobot Gobot Reviewed-by: Jay Conrod Reviewed-by: Michael Matloob (cherry picked from commit bfb1342a40216cba0ff5ae3a1b102823b7603068) Reviewed-on: https://go-review.googlesource.com/c/go/+/225660 --- src/cmd/go/internal/generate/generate.go | 3 ++- src/cmd/go/internal/test/test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go index f2ae80e5dc68f5..317d80204091bb 100644 --- a/src/cmd/go/internal/generate/generate.go +++ b/src/cmd/go/internal/generate/generate.go @@ -22,6 +22,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/load" "cmd/go/internal/modload" + "cmd/go/internal/str" "cmd/go/internal/work" ) @@ -438,7 +439,7 @@ func (g *Generator) exec(words []string) { cmd.Stderr = os.Stderr // Run the command in the package directory. cmd.Dir = g.dir - cmd.Env = append(cfg.OrigEnv, g.env...) + cmd.Env = str.StringList(cfg.OrigEnv, g.env) err := cmd.Run() if err != nil { g.errorf("running %q: %s", words[0], err) diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 8141e31c991aee..636d2fef55dab7 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -1142,7 +1142,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error { cmd := exec.Command(args[0], args[1:]...) cmd.Dir = a.Package.Dir - cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv) + cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)]) cmd.Stdout = stdout cmd.Stderr = stdout From 6fcea1e380ae5b9a7a1c78be2830c335ee994193 Mon Sep 17 00:00:00 2001 From: Liam 'Auzzie' Haworth Date: Tue, 25 Feb 2020 00:11:28 +0000 Subject: [PATCH 03/12] [release-branch.go1.13] os/exec: use environment variables for user token when present Builds upon the changes from #32000 which supported sourcing environment variables for a new process from the environment of a Windows user token when supplied. But due to the logic of os/exec, the Env field of a process was always non-nil when it reached that change. This change moves the logic up to os/exec, specifically when os.ProcAttr is being built for the os.StartProcess call, this ensures that if a user token has been supplied and no Env slice has been provided on the command it will be sourced from the user's environment. If no token is provided, or the program is compiled for any other platform than Windows, the default environment will be sourced from syscall.Environ(). For #35314 Fixes #37433 Change-Id: I4c1722e90b91945eb6980d5c5928183269b50487 GitHub-Last-Rev: 32216b7291418f9285147a93ed6d0ba028f94ef2 GitHub-Pull-Request: golang/go#37402 Reviewed-on: https://go-review.googlesource.com/c/go/+/220587 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-on: https://go-review.googlesource.com/c/go/+/226280 --- src/go/build/deps_test.go | 5 +++-- .../syscall/execenv/execenv_default.go | 19 +++++++++++++++++++ .../syscall/execenv/execenv_windows.go} | 18 ++++++++++++++---- src/os/env_default.go | 13 ------------- src/os/exec/exec.go | 15 ++++++++++----- src/os/exec_posix.go | 3 ++- 6 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 src/internal/syscall/execenv/execenv_default.go rename src/{os/env_windows.go => internal/syscall/execenv/execenv_windows.go} (59%) delete mode 100644 src/os/env_default.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 179b0bfef553ae..40f29a2c301307 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -152,6 +152,7 @@ var pkgDeps = map[string][]string{ "internal/syscall/unix": {"L0", "syscall"}, "internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll"}, "internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"}, + "internal/syscall/execenv": {"L0", "syscall", "internal/syscall/windows", "unicode/utf16"}, "time": { // "L0" without the "io" package: "errors", @@ -169,10 +170,10 @@ var pkgDeps = map[string][]string{ "internal/cfg": {"L0"}, "internal/poll": {"L0", "internal/oserror", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"}, "internal/testlog": {"L0"}, - "os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/testlog"}, + "os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/syscall/execenv", "internal/testlog"}, "path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"}, "io/ioutil": {"L2", "os", "path/filepath", "time"}, - "os/exec": {"L2", "os", "context", "path/filepath", "syscall"}, + "os/exec": {"L2", "os", "context", "path/filepath", "syscall", "internal/syscall/execenv"}, "os/signal": {"L2", "os", "syscall"}, // OS enables basic operating system functionality, diff --git a/src/internal/syscall/execenv/execenv_default.go b/src/internal/syscall/execenv/execenv_default.go new file mode 100644 index 00000000000000..4bdbb55edbf98b --- /dev/null +++ b/src/internal/syscall/execenv/execenv_default.go @@ -0,0 +1,19 @@ +// Copyright 2020 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. + +// +build !windows + +package execenv + +import "syscall" + +// Default will return the default environment +// variables based on the process attributes +// provided. +// +// Defaults to syscall.Environ() on all platforms +// other than Windows. +func Default(sys *syscall.SysProcAttr) ([]string, error) { + return syscall.Environ(), nil +} diff --git a/src/os/env_windows.go b/src/internal/syscall/execenv/execenv_windows.go similarity index 59% rename from src/os/env_windows.go rename to src/internal/syscall/execenv/execenv_windows.go index e8f647e7ac778e..3a41a7a3fd5441 100644 --- a/src/os/env_windows.go +++ b/src/internal/syscall/execenv/execenv_windows.go @@ -1,8 +1,10 @@ -// Copyright 2019 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package os +// +build windows + +package execenv import ( "internal/syscall/windows" @@ -11,9 +13,17 @@ import ( "unsafe" ) -func environForSysProcAttr(sys *syscall.SysProcAttr) (env []string, err error) { +// Default will return the default environment +// variables based on the process attributes +// provided. +// +// If the process attributes contain a token, then +// the environment variables will be sourced from +// the defaults for that user token, otherwise they +// will be sourced from syscall.Environ(). +func Default(sys *syscall.SysProcAttr) (env []string, err error) { if sys == nil || sys.Token == 0 { - return Environ(), nil + return syscall.Environ(), nil } var block *uint16 err = windows.CreateEnvironmentBlock(&block, sys.Token, false) diff --git a/src/os/env_default.go b/src/os/env_default.go deleted file mode 100644 index c11ccce7e3d872..00000000000000 --- a/src/os/env_default.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019 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. - -// +build !windows - -package os - -import "syscall" - -func environForSysProcAttr(sys *syscall.SysProcAttr) ([]string, error) { - return Environ(), nil -} diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 17ef003eca02b4..5552fd858f10b6 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -24,6 +24,7 @@ import ( "bytes" "context" "errors" + "internal/syscall/execenv" "io" "os" "path/filepath" @@ -222,11 +223,11 @@ func interfaceEqual(a, b interface{}) bool { return a == b } -func (c *Cmd) envv() []string { +func (c *Cmd) envv() ([]string, error) { if c.Env != nil { - return c.Env + return c.Env, nil } - return os.Environ() + return execenv.Default(c.SysProcAttr) } func (c *Cmd) argv() []string { @@ -412,11 +413,15 @@ func (c *Cmd) Start() error { } c.childFiles = append(c.childFiles, c.ExtraFiles...) - var err error + envv, err := c.envv() + if err != nil { + return err + } + c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ Dir: c.Dir, Files: c.childFiles, - Env: addCriticalEnv(dedupEnv(c.envv())), + Env: addCriticalEnv(dedupEnv(envv)), Sys: c.SysProcAttr, }) if err != nil { diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go index 505931b488323b..83fdae851444c6 100644 --- a/src/os/exec_posix.go +++ b/src/os/exec_posix.go @@ -7,6 +7,7 @@ package os import ( + "internal/syscall/execenv" "syscall" ) @@ -38,7 +39,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e Sys: attr.Sys, } if sysattr.Env == nil { - sysattr.Env, err = environForSysProcAttr(sysattr.Sys) + sysattr.Env, err = execenv.Default(sysattr.Sys) if err != nil { return nil, err } From 7261619113b1f35fc9260a44b66bc1f9a13f142f Mon Sep 17 00:00:00 2001 From: Constantin Konstantinidis Date: Fri, 1 Nov 2019 15:46:47 +0100 Subject: [PATCH 04/12] [release-branch.go1.13] net/http: deflake TestCancelRequestWithChannelBeforeDo_Cancel Goroutines clean up takes longer when using deprecated CloseNotifier. For #35122. Fixes #37892. Change-Id: Id820a3012b5c781ddfb294b38ee3b009624e398c Reviewed-on: https://go-review.googlesource.com/c/go/+/204661 Run-TryBot: Brad Fitzpatrick Reviewed-by: Brad Fitzpatrick (cherry picked from commit 1e4a358454987ef5104e45081c8e2ecdc9f32513) Reviewed-on: https://go-review.googlesource.com/c/go/+/223699 Run-TryBot: Alexander Rakoczy Reviewed-by: Alexander Rakoczy --- src/net/http/main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go index 7936fb3044e65d..85aa9096c31cea 100644 --- a/src/net/http/main_test.go +++ b/src/net/http/main_test.go @@ -122,7 +122,7 @@ func afterTest(t testing.TB) { ").noteClientGone(": "a closenotifier sender", } var stacks string - for i := 0; i < 4; i++ { + for i := 0; i < 10; i++ { bad = "" stacks = strings.Join(interestingGoroutines(), "\n\n") for substr, what := range badSubstring { From f353662952b48af110dc878adfa533745e168716 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Fri, 25 Oct 2019 15:37:00 -0400 Subject: [PATCH 05/12] [release-branch.go1.13] os: use an actual RemoveAll failure in TestRemoveAllWithMoreErrorThanReqSize Previously we injected an error, and the injection points were (empirically) not realistic on some platforms. Instead, we now make the directory read-only, which (on most platforms) suffices to prevent the removal of its files. Also remove unused test hook, as was done in CL 204060. For #35117. For #29921. Fixes #37895. Change-Id: Ica4e2818566f8c14df3eed7c3b8de5c0abeb6963 Reviewed-on: https://go-review.googlesource.com/c/go/+/203502 Reviewed-by: Ian Lance Taylor Reviewed-by: Brad Fitzpatrick (cherry picked from commit 06bdd52f7540eca9e3ade6e78234d00703f3ee23) Reviewed-on: https://go-review.googlesource.com/c/go/+/223700 Run-TryBot: Dmitri Shuralyov Reviewed-by: Alexander Rakoczy --- src/os/export_test.go | 1 - src/os/path.go | 3 --- src/os/removeall_at.go | 1 - src/os/removeall_noat.go | 1 - src/os/removeall_test.go | 50 +++++++++++++++++++++++++++------------- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/os/export_test.go b/src/os/export_test.go index d17d5e62308a7b..812432cee4869c 100644 --- a/src/os/export_test.go +++ b/src/os/export_test.go @@ -9,4 +9,3 @@ package os var Atime = atime var LstatP = &lstat var ErrWriteAtInAppendMode = errWriteAtInAppendMode -var RemoveAllTestHook = &removeAllTestHook diff --git a/src/os/path.go b/src/os/path.go index 9d7ecad79298ad..ba43ea352547b9 100644 --- a/src/os/path.go +++ b/src/os/path.go @@ -58,9 +58,6 @@ func MkdirAll(path string, perm FileMode) error { return nil } -// removeAllTestHook is a hook for testing. -var removeAllTestHook = func(err error) error { return err } - // RemoveAll removes path and any children it contains. // It removes everything it can but returns the first error // it encounters. If the path does not exist, RemoveAll diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go index bc632f5a751bd1..e619851f9c8276 100644 --- a/src/os/removeall_at.go +++ b/src/os/removeall_at.go @@ -153,7 +153,6 @@ func removeAllFrom(parent *File, base string) error { // Remove the directory itself. unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR) - unlinkError = removeAllTestHook(unlinkError) if unlinkError == nil || IsNotExist(unlinkError) { return nil } diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go index a0694fa4ce03b1..32673c0ab003a7 100644 --- a/src/os/removeall_noat.go +++ b/src/os/removeall_noat.go @@ -124,7 +124,6 @@ func removeAll(path string) error { // Remove directory. err1 := Remove(path) - err1 = removeAllTestHook(err1) if err1 == nil || IsNotExist(err1) { return nil } diff --git a/src/os/removeall_test.go b/src/os/removeall_test.go index 4d556f977e4239..050635f5332576 100644 --- a/src/os/removeall_test.go +++ b/src/os/removeall_test.go @@ -5,7 +5,6 @@ package os_test import ( - "errors" "fmt" "io/ioutil" . "os" @@ -413,14 +412,6 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) { t.Skip("skipping in short mode") } - defer func(oldHook func(error) error) { - *RemoveAllTestHook = oldHook - }(*RemoveAllTestHook) - - *RemoveAllTestHook = func(err error) error { - return errors.New("error from RemoveAllTestHook") - } - tmpDir, err := ioutil.TempDir("", "TestRemoveAll-") if err != nil { t.Fatal(err) @@ -429,7 +420,7 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) { path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_") - // Make directory with 1025 files and remove. + // Make directory with 1025 read-only files. if err := MkdirAll(path, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", path, err) } @@ -442,13 +433,40 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) { fd.Close() } - // This call should not hang - if err := RemoveAll(path); err == nil { - t.Fatal("Want error from RemoveAllTestHook, got nil") + // Make the parent directory read-only. On some platforms, this is what + // prevents os.Remove from removing the files within that directory. + if err := Chmod(path, 0555); err != nil { + t.Fatal(err) } + defer Chmod(path, 0755) - // We hook to inject error, but the actual files must be deleted - if _, err := Lstat(path); err == nil { - t.Fatal("directory must be deleted even with removeAllTetHook run") + // This call should not hang, even on a platform that disallows file deletion + // from read-only directories. + err = RemoveAll(path) + + if Getuid() == 0 { + // On many platforms, root can remove files from read-only directories. + return + } + if err == nil { + t.Fatal("RemoveAll() = nil; want error") + } + + dir, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer dir.Close() + + if runtime.GOOS == "windows" { + // Marking a directory in Windows does not prevent the os package from + // creating or removing files within it. + // (See https://golang.org/issue/35042.) + return + } + + names, _ := dir.Readdirnames(1025) + if len(names) < 1025 { + t.Fatalf("RemoveAll() unexpectedly removed %d read-only files from that directory", 1025-len(names)) } } From 5a31a971f32e390bd3664898c03b23f5b6a7c27d Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 28 Oct 2019 16:55:06 -0700 Subject: [PATCH 06/12] [release-branch.go1.13] cmd/compile/internal/syntax: don't hardwire path separator in test Windows uses '\' not '/'. For #35175. Fixes #37901. Change-Id: Ib3d01dcf148fc0675496d5213f5bcc9cf210a6fc Reviewed-on: https://go-review.googlesource.com/c/go/+/203889 Reviewed-by: Bryan C. Mills Run-TryBot: Brad Fitzpatrick (cherry picked from commit a754d2993db1771ca3903d0a5d0e3add1883cf9b) Reviewed-on: https://go-review.googlesource.com/c/go/+/223703 Reviewed-by: Robert Griesemer Run-TryBot: Andrew Bonventre --- src/cmd/compile/internal/syntax/parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 3cf55defc774dc..673339d667c180 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -96,7 +96,7 @@ func walkDirs(t *testing.T, dir string, action func(string)) { } } else if fi.IsDir() && fi.Name() != "testdata" { path := filepath.Join(dir, fi.Name()) - if !strings.HasSuffix(path, "/test") { + if !strings.HasSuffix(path, string(filepath.Separator)+"test") { dirs = append(dirs, path) } } From c67f9cc6c0753bf4aa994edfe09e869c7268b5d1 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Tue, 17 Mar 2020 10:22:41 -0400 Subject: [PATCH 07/12] [release-branch.go1.13] cmd/doc: skip failing TestDotSlashLookup on Windows This test was fixed by changing cmd/doc behavior in CL 204442. Backporting that non-test code change is unlikely to be appropriate this late in Go 1.13 release cycle. A failing test can cover up other regressions, so skip this known failing test to fix the builder. For #35236. For #36181. Change-Id: I07e795e75d7e37bc96ab68607d5d5cc9254342f8 Reviewed-on: https://go-review.googlesource.com/c/go/+/223780 Run-TryBot: Dmitri Shuralyov Reviewed-by: Alexander Rakoczy Reviewed-by: Carlos Amedee --- src/cmd/doc/doc_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go index 11d0bdafd99432..9a5c04fe4c8d45 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/doc/doc_test.go @@ -920,6 +920,9 @@ func TestDotSlashLookup(t *testing.T) { t.Skip("scanning file system takes too long") } maybeSkip(t) + if runtime.GOOS == "windows" { + t.Skip("known Windows test failure on release-branch.go1.13; fix is in CL 204442 but requires non-test code changes unlikely to be appropriate for backporting this late") + } where, err := os.Getwd() if err != nil { t.Fatal(err) From 9ed3fb84bc6148590757995c651bd4a6e85b4531 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Tue, 17 Mar 2020 17:50:25 -0400 Subject: [PATCH 08/12] [release-branch.go1.13] cmd/go: fix and skip known Windows test failures These non-short Windows test failures were resolved fully in CL 206144. Both TestScript/build_trimpath and TestScript/version tests can be fixed by backporting the changes to test scripts only, so that is done here. Fixing TestScript/mod_list_dir requires backporting non-test changes in addition to the test script changes, which is unlikely to be appropriate this late in Go 1.13 release cycle. A failing test can cover up other regressions, so skip this known failing test to fix the builder. For #36181. Change-Id: I4f140bd373554eb4664f04638666dee77986ec3e Reviewed-on: https://go-review.googlesource.com/c/go/+/223782 Run-TryBot: Dmitri Shuralyov TryBot-Result: Gobot Gobot Reviewed-by: Jay Conrod --- src/cmd/go/testdata/script/build_trimpath.txt | 103 +++++++++++++----- src/cmd/go/testdata/script/mod_list_dir.txt | 1 + src/cmd/go/testdata/script/version.txt | 5 + 3 files changed, 82 insertions(+), 27 deletions(-) diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt index 668f75599e77b4..ba414372d347ab 100644 --- a/src/cmd/go/testdata/script/build_trimpath.txt +++ b/src/cmd/go/testdata/script/build_trimpath.txt @@ -1,44 +1,93 @@ [short] skip - -env -r GOROOT_REGEXP=$GOROOT -env -r WORK_REGEXP='$WORK' # don't expand $WORK; grep replaces $WORK in text before matching. -env GOROOT GOROOT_REGEXP WORK WORK_REGEXP +env GO111MODULE=on # A binary built without -trimpath should contain the current workspace # and GOROOT for debugging and stack traces. cd a -go build -o hello.exe hello.go -grep -q $WORK_REGEXP hello.exe -grep -q $GOROOT_REGEXP hello.exe +go build -o $WORK/paths-a.exe paths.go +exec $WORK/paths-a.exe $WORK/paths-a.exe +stdout 'binary contains GOPATH: true' +stdout 'binary contains GOROOT: true' # A binary built with -trimpath should not contain the current workspace # or GOROOT. -go build -trimpath -o hello.exe hello.go -! grep -q $GOROOT_REGEXP hello.exe -! grep -q $WORK_REGEXP hello.exe -cd .. +go build -trimpath -o $WORK/paths-a.exe paths.go +exec $WORK/paths-a.exe $WORK/paths-a.exe +stdout 'binary contains GOPATH: false' +stdout 'binary contains GOROOT: false' # A binary from an external module built with -trimpath should not contain # the current workspace or GOROOT. -env GO111MODULE=on -go build -trimpath -o fortune.exe rsc.io/fortune -! grep -q $GOROOT_REGEXP fortune.exe -! grep -q $WORK_REGEXP fortune.exe +cd $WORK +go get -trimpath rsc.io/fortune +exec $WORK/paths-a.exe $GOPATH/bin/fortune$GOEXE +stdout 'binary contains GOPATH: false' +stdout 'binary contains GOROOT: false' # Two binaries built from identical packages in different directories # should be identical. -mkdir b -cp a/go.mod a/hello.go b -cd a -go build -trimpath -o ../a.exe . -cd ../b -go build -trimpath -o ../b.exe . -cd .. -cmp -q a.exe b.exe +# TODO(golang.org/issue/35435): at the moment, they are not. +#mkdir $GOPATH/src/b +#cp $GOPATH/src/a/go.mod $GOPATH/src/b/go.mod +#cp $GOPATH/src/a/paths.go $GOPATH/src/b/paths.go +#cd $GOPATH/src/b +#go build -trimpath -o $WORK/paths-b.exe . +#cmp -q $WORK/paths-a.exe $WORK/paths-b.exe + +[!exec:gccgo] stop + +# A binary built with gccgo without -trimpath should contain the current +# GOPATH and GOROOT. +env GO111MODULE=off # The current released gccgo does not support builds in module mode. +cd $GOPATH/src/a +go build -compiler=gccgo -o $WORK/gccgo-paths-a.exe . +exec $WORK/gccgo-paths-a.exe $WORK/gccgo-paths-b.exe +stdout 'binary contains GOPATH: true' +stdout 'binary contains GOROOT: true' + +# A binary built with gccgo with -trimpath should not contain GOPATH or GOROOT. +go build -compiler=gccgo -trimpath -o $WORK/gccgo-paths-a.exe . +exec $WORK/gccgo-paths-a.exe $WORK/gccgo-paths-b.exe +stdout 'binary contains GOPATH: false' +stdout 'binary contains GOROOT: false' --- a/hello.go -- +# Two binaries built from identical packages in different directories +# should be identical. +# TODO(golang.org/issue/35435): at the moment, they are not. +#cd ../b +#go build -compiler=gccgo -trimpath -o $WORK/gccgo-paths-b.exe . +#cmp -q $WORK/gccgo-paths-a.exe $WORK/gccgo-paths-b.exe + +-- $GOPATH/src/a/paths.go -- package main -func main() { println("hello") } --- a/go.mod -- -module m +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" +) + +func main() { + exe := os.Args[1] + data, err := ioutil.ReadFile(exe) + if err != nil { + log.Fatal(err) + } + + gopath := []byte(filepath.ToSlash(os.Getenv("GOPATH"))) + if len(gopath) == 0 { + log.Fatal("GOPATH not set") + } + fmt.Printf("binary contains GOPATH: %v\n", bytes.Contains(data, gopath)) + + goroot := []byte(filepath.ToSlash(os.Getenv("GOROOT"))) + if len(goroot) == 0 { + log.Fatal("GOROOT not set") + } + fmt.Printf("binary contains GOROOT: %v\n", bytes.Contains(data, goroot)) +} +-- $GOPATH/src/a/go.mod -- +module example.com/a diff --git a/src/cmd/go/testdata/script/mod_list_dir.txt b/src/cmd/go/testdata/script/mod_list_dir.txt index a8023cce9c5b78..39834f2249cf91 100644 --- a/src/cmd/go/testdata/script/mod_list_dir.txt +++ b/src/cmd/go/testdata/script/mod_list_dir.txt @@ -1,4 +1,5 @@ [short] skip +[windows] skip # known Windows test failure on release-branch.go1.13; fix is in CL 206144 but requires non-test code changes unlikely to be appropriate for backporting this late # go list with path to directory should work diff --git a/src/cmd/go/testdata/script/version.txt b/src/cmd/go/testdata/script/version.txt index 9086f047e4bbfb..42526247f1e7e6 100644 --- a/src/cmd/go/testdata/script/version.txt +++ b/src/cmd/go/testdata/script/version.txt @@ -1,6 +1,7 @@ env GO111MODULE=on [short] skip +# Check that 'go version' and 'go version -m' work on a binary built in module mode. go build -o fortune.exe rsc.io/fortune go version fortune.exe stdout '^fortune.exe: .+' @@ -8,6 +9,10 @@ go version -m fortune.exe stdout '^\tpath\trsc.io/fortune' stdout '^\tmod\trsc.io/fortune\tv1.0.0' +# Repeat the test with -buildmode=pie. +# TODO(golang.org/issue/27144): don't skip after -buildmode=pie is implemented +# on Windows. +[windows] skip # -buildmode=pie not supported go build -buildmode=pie -o external.exe rsc.io/fortune go version external.exe stdout '^external.exe: .+' From b79c36dc9949177aa6e2e33fe5af61d4461676a4 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Seo Date: Fri, 17 Jan 2020 17:59:59 -0300 Subject: [PATCH 09/12] [release-branch.go1.13] runtime: fix wrong offset when calling ppc64x nanotime syscall There is a wrong offset when getting the results of a clock_gettime syscall. Although the syscall will never be called in native ppc64x, QEMU doesn't implement VDSO, so it will return wrong values. For #36592 Fixes #38236 Change-Id: Icf838075228dcdd62cf2c1279aa983e5993d66ee Reviewed-on: https://go-review.googlesource.com/c/go/+/215397 Reviewed-by: Tobias Klauser (cherry picked from commit 71239b4f491698397149868c88d2c851de2cd49b) Reviewed-on: https://go-review.googlesource.com/c/go/+/227179 Reviewed-by: Carlos Eduardo Seo Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/runtime/sys_linux_ppc64x.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index 13d23156bdbbe8..435cc2809a47cc 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -251,7 +251,7 @@ fallback: ADD $32, R1, R4 SYSCALL $SYS_clock_gettime MOVD 32(R1), R3 - MOVD 48(R1), R5 + MOVD 40(R1), R5 JMP finish TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 From 2f6dd92c1ef5a202a3d13ff16254d2a3d7b03c02 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Fri, 28 Feb 2020 16:31:19 -0500 Subject: [PATCH 10/12] [release-branch.go1.13] cmd/go: make module zip extraction more robust Currently, we extract module zip files to temporary directories, then atomically rename them into place. On Windows, this can fail with ERROR_ACCESS_DENIED if another process (antivirus) has files open before the rename. In CL 220978, we repeated the rename operation in a loop over 500 ms, but this didn't solve the problem for everyone. A better solution will extract module zip files to their permanent locations in the cache and will keep a ".partial" marker file, indicating when a module hasn't been fully extracted (CL 221157). This approach is not safe if current versions of Go access the module cache concurrently, since the module directory is detected with a single os.Stat. In the interim, this CL makes two changes: 1. Flaky file system operations are repeated over 2000 ms to reduce the chance of this error occurring. 2. cmd/go will now check for .partial files created by future versions. If a .partial file is found, it will lock the lock file, then remove the .partial file and directory if needed. After some time has passed and Go versions lacking this CL are no longer supported, we can start extracting module zip files in place. Updates #37802 Change-Id: I467ee11aa59a90b63cf0e3e761c4fec89d57d3b6 Reviewed-on: https://go-review.googlesource.com/c/go/+/221820 Run-TryBot: Jay Conrod TryBot-Result: Gobot Gobot Reviewed-by: Michael Matloob Reviewed-by: Bryan C. Mills (cherry picked from commit 093049b3709eda7537ece92a2991918cf53782d6) Reviewed-on: https://go-review.googlesource.com/c/go/+/223146 --- src/cmd/go/internal/modcmd/verify.go | 11 ++-- src/cmd/go/internal/modfetch/cache.go | 40 +++++++++++- src/cmd/go/internal/modfetch/fetch.go | 62 ++++++++++++------- src/cmd/go/internal/modload/build.go | 4 +- .../go/internal/robustio/robustio_windows.go | 2 +- .../testdata/script/mod_download_partial.txt | 56 +++++++++++++++++ 6 files changed, 141 insertions(+), 34 deletions(-) create mode 100644 src/cmd/go/testdata/script/mod_download_partial.txt diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go index 81fc44dc97ae60..dd1665619b6241 100644 --- a/src/cmd/go/internal/modcmd/verify.go +++ b/src/cmd/go/internal/modcmd/verify.go @@ -7,6 +7,7 @@ package modcmd import ( "bytes" "cmd/go/internal/cfg" + "errors" "fmt" "io/ioutil" "os" @@ -61,12 +62,10 @@ func verifyMod(mod module.Version) bool { _, zipErr = os.Stat(zip) } dir, dirErr := modfetch.DownloadDir(mod) - if dirErr == nil { - _, dirErr = os.Stat(dir) - } data, err := ioutil.ReadFile(zip + "hash") if err != nil { - if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) { + if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) && + dirErr != nil && errors.Is(dirErr, os.ErrNotExist) { // Nothing downloaded yet. Nothing to verify. return true } @@ -75,7 +74,7 @@ func verifyMod(mod module.Version) bool { } h := string(bytes.TrimSpace(data)) - if zipErr != nil && os.IsNotExist(zipErr) { + if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) { // ok } else { hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash) @@ -87,7 +86,7 @@ func verifyMod(mod module.Version) bool { ok = false } } - if dirErr != nil && os.IsNotExist(dirErr) { + if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) { // ok } else { hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash) diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go index c0062809d172de..446c8fec8b5388 100644 --- a/src/cmd/go/internal/modfetch/cache.go +++ b/src/cmd/go/internal/modfetch/cache.go @@ -7,6 +7,7 @@ package modfetch import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -57,8 +58,11 @@ func CachePath(m module.Version, suffix string) (string, error) { return filepath.Join(dir, encVer+"."+suffix), nil } -// DownloadDir returns the directory to which m should be downloaded. -// Note that the directory may not yet exist. +// DownloadDir returns the directory to which m should have been downloaded. +// An error will be returned if the module path or version cannot be escaped. +// An error satisfying errors.Is(err, os.ErrNotExist) will be returned +// along with the directory if the directory does not exist or if the directory +// is not completely populated. func DownloadDir(m module.Version) (string, error) { if PkgMod == "" { return "", fmt.Errorf("internal error: modfetch.PkgMod not set") @@ -77,9 +81,39 @@ func DownloadDir(m module.Version) (string, error) { if err != nil { return "", err } - return filepath.Join(PkgMod, enc+"@"+encVer), nil + + dir := filepath.Join(PkgMod, enc+"@"+encVer) + if fi, err := os.Stat(dir); os.IsNotExist(err) { + return dir, err + } else if err != nil { + return dir, &DownloadDirPartialError{dir, err} + } else if !fi.IsDir() { + return dir, &DownloadDirPartialError{dir, errors.New("not a directory")} + } + partialPath, err := CachePath(m, "partial") + if err != nil { + return dir, err + } + if _, err := os.Stat(partialPath); err == nil { + return dir, &DownloadDirPartialError{dir, errors.New("not completely extracted")} + } else if !os.IsNotExist(err) { + return dir, err + } + return dir, nil +} + +// DownloadDirPartialError is returned by DownloadDir if a module directory +// exists but was not completely populated. +// +// DownloadDirPartialError is equivalent to os.ErrNotExist. +type DownloadDirPartialError struct { + Dir string + Err error } +func (e *DownloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) } +func (e *DownloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist } + // lockVersion locks a file within the module cache that guards the downloading // and extraction of the zipfile for the given module version. func lockVersion(mod module.Version) (unlock func(), err error) { diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go index 51a56028c4acbe..d5516dd8d9b0dd 100644 --- a/src/cmd/go/internal/modfetch/fetch.go +++ b/src/cmd/go/internal/modfetch/fetch.go @@ -7,6 +7,7 @@ package modfetch import ( "archive/zip" "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -22,6 +23,7 @@ import ( "cmd/go/internal/module" "cmd/go/internal/par" "cmd/go/internal/renameio" + "cmd/go/internal/robustio" ) var downloadCache par.Cache @@ -41,24 +43,27 @@ func Download(mod module.Version) (dir string, err error) { err error } c := downloadCache.Do(mod, func() interface{} { - dir, err := DownloadDir(mod) + dir, err := download(mod) if err != nil { return cached{"", err} } - if err := download(mod, dir); err != nil { - return cached{"", err} - } checkMod(mod) return cached{dir, nil} }).(cached) return c.dir, c.err } -func download(mod module.Version, dir string) (err error) { - // If the directory exists, the module has already been extracted. - fi, err := os.Stat(dir) - if err == nil && fi.IsDir() { - return nil +func download(mod module.Version) (dir string, err error) { + // If the directory exists, and no .partial file exists, + // the module has already been completely extracted. + // .partial files may be created when future versions of cmd/go + // extract module zip directories in place instead of extracting + // to a random temporary directory and renaming. + dir, err = DownloadDir(mod) + if err == nil { + return dir, nil + } else if dir == "" || !errors.Is(err, os.ErrNotExist) { + return "", err } // To avoid cluttering the cache with extraneous files, @@ -66,7 +71,7 @@ func download(mod module.Version, dir string) (err error) { // Invoke DownloadZip before locking the file. zipfile, err := DownloadZip(mod) if err != nil { - return err + return "", err } if cfg.CmdName != "mod download" { @@ -75,17 +80,19 @@ func download(mod module.Version, dir string) (err error) { unlock, err := lockVersion(mod) if err != nil { - return err + return "", err } defer unlock() // Check whether the directory was populated while we were waiting on the lock. - fi, err = os.Stat(dir) - if err == nil && fi.IsDir() { - return nil + _, dirErr := DownloadDir(mod) + if dirErr == nil { + return dir, nil } + _, dirExists := dirErr.(*DownloadDirPartialError) - // Clean up any remaining temporary directories from previous runs. + // Clean up any remaining temporary directories from previous runs, as well + // as partially extracted diectories created by future versions of cmd/go. // This is only safe to do because the lock file ensures that their writers // are no longer active. parentDir := filepath.Dir(dir) @@ -95,6 +102,19 @@ func download(mod module.Version, dir string) (err error) { RemoveAll(path) // best effort } } + if dirExists { + if err := RemoveAll(dir); err != nil { + return "", err + } + } + + partialPath, err := CachePath(mod, "partial") + if err != nil { + return "", err + } + if err := os.Remove(partialPath); err != nil && !os.IsNotExist(err) { + return "", err + } // Extract the zip file to a temporary directory, then rename it to the // final path. That way, we can use the existence of the source directory to @@ -102,11 +122,11 @@ func download(mod module.Version, dir string) (err error) { // the entire directory (e.g. as an attempt to prune out file corruption) // the module cache will still be left in a recoverable state. if err := os.MkdirAll(parentDir, 0777); err != nil { - return err + return "", err } tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix) if err != nil { - return err + return "", err } defer func() { if err != nil { @@ -117,17 +137,17 @@ func download(mod module.Version, dir string) (err error) { modpath := mod.Path + "@" + mod.Version if err := Unzip(tmpDir, zipfile, modpath, 0); err != nil { fmt.Fprintf(os.Stderr, "-> %s\n", err) - return err + return "", err } - if err := os.Rename(tmpDir, dir); err != nil { - return err + if err := robustio.Rename(tmpDir, dir); err != nil { + return "", err } // Make dir read-only only *after* renaming it. // os.Rename was observed to fail for read-only directories on macOS. makeDirsReadOnly(dir) - return nil + return dir, nil } var downloadZipCache par.Cache diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go index ff42516c807ddc..a0c6b0bf74f466 100644 --- a/src/cmd/go/internal/modload/build.go +++ b/src/cmd/go/internal/modload/build.go @@ -143,9 +143,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { } dir, err := modfetch.DownloadDir(mod) if err == nil { - if info, err := os.Stat(dir); err == nil && info.IsDir() { - m.Dir = dir - } + m.Dir = dir } } } diff --git a/src/cmd/go/internal/robustio/robustio_windows.go b/src/cmd/go/internal/robustio/robustio_windows.go index a3d94e566fe420..1eb8cfbccfae60 100644 --- a/src/cmd/go/internal/robustio/robustio_windows.go +++ b/src/cmd/go/internal/robustio/robustio_windows.go @@ -14,7 +14,7 @@ import ( "time" ) -const arbitraryTimeout = 500 * time.Millisecond +const arbitraryTimeout = 2000 * time.Millisecond // retry retries ephemeral errors from f up to an arbitrary timeout // to work around spurious filesystem errors on Windows diff --git a/src/cmd/go/testdata/script/mod_download_partial.txt b/src/cmd/go/testdata/script/mod_download_partial.txt new file mode 100644 index 00000000000000..cda358b99f0012 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_partial.txt @@ -0,0 +1,56 @@ +# Download a module +go mod download rsc.io/quote +exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod + +# 'go mod verify' should fail if we delete a file. +go mod verify +chmod 0755 $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod +! go mod verify + +# Create a .partial file to simulate an failure extracting the zip file. +cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial + +# 'go mod verify' should not fail, since the module hasn't been completely +# ingested into the cache. +go mod verify + +# 'go list' should not load packages from the directory. +# NOTE: the message "directory $dir outside available modules" is reported +# for directories not in the main module, active modules in the module cache, +# or local replacements. In this case, the directory is in the right place, +# but it's incomplete, so 'go list' acts as if it's not an active module. +! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +stderr 'outside available modules' + +# 'go list -m' should not print the directory. +go list -m -f '{{.Dir}}' rsc.io/quote +! stdout . + +# 'go mod download' should re-extract the module and remove the .partial file. +go mod download rsc.io/quote +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial +exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod + +# 'go list' should succeed. +go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +stdout '^rsc.io/quote$' + +# 'go list -m' should print the directory. +go list -m -f '{{.Dir}}' rsc.io/quote +stdout 'pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2' + +# go mod verify should fail if we delete a file. +go mod verify +chmod 0755 $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 +rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod +! go mod verify + +-- go.mod -- +module m + +go 1.14 + +require rsc.io/quote v1.5.2 + +-- empty -- From 3a275aab75e40735c6c0184c1a61d4db0016d0a7 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 26 Feb 2020 15:12:33 -0500 Subject: [PATCH 11/12] [release-branch.go1.13] runtime: fix rounding in materializeGCProg materializeGCProg allocates a temporary buffer for unrolling a GC program. Unfortunately, when computing the size of the buffer, it rounds *down* the number of bytes needed to store bitmap before rounding up the number of pages needed to store those bytes. The fact that it rounds up to pages usually mitigates the rounding down, but the type from #37470 exists right on the boundary where this doesn't work: type Sequencer struct { htable [1 << 17]uint32 buf []byte } On 64-bit, this GC bitmap is exactly 8 KiB of zeros, followed by three one bits. Hence, this needs 8193 bytes of storage, but the current math in materializeGCProg rounds *down* the three one bits to 8192 bytes. Since this is exactly pageSize, the next step of rounding up to the page size doesn't mitigate this error, and materializeGCProg allocates a buffer that is one byte too small. runGCProg then writes one byte past the end of this buffer, causing either a segfault (if you're lucky!) or memory corruption. Updates #37470. Fixes #37483. Change-Id: Iad24c463c501cd9b1dc1924bc2ad007991a094a0 Reviewed-on: https://go-review.googlesource.com/c/go/+/224418 Run-TryBot: Austin Clements Reviewed-by: Cherry Zhang TryBot-Result: Gobot Gobot --- src/runtime/mbitmap.go | 6 +++++- src/runtime/stubs.go | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 30ec5f1cc905e3..71efe33741f39a 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -1912,7 +1912,11 @@ Run: // The bitmask starts at s.startAddr. // The result must be deallocated with dematerializeGCProg. func materializeGCProg(ptrdata uintptr, prog *byte) *mspan { - s := mheap_.allocManual((ptrdata/(8*sys.PtrSize)+pageSize-1)/pageSize, &memstats.gc_sys) + // Each word of ptrdata needs one bit in the bitmap. + bitmapBytes := divRoundUp(ptrdata, 8*sys.PtrSize) + // Compute the number of pages needed for bitmapBytes. + pages := divRoundUp(bitmapBytes, pageSize) + s := mheap_.allocManual(pages, &memstats.gc_sys) runGCProg(addb(prog, 4), nil, (*byte)(unsafe.Pointer(s.startAddr)), 1) return s } diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 26aaf2224d519f..8d1c6984008595 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -295,6 +295,13 @@ func round(n, a uintptr) uintptr { return (n + a - 1) &^ (a - 1) } +// divRoundUp returns ceil(n / a). +func divRoundUp(n, a uintptr) uintptr { + // a is generally a power of two. This will get inlined and + // the compiler will optimize the division. + return (n + a - 1) / a +} + // checkASM reports whether assembly runtime checks have passed. func checkASM() bool From a57f07aac237d366630e85d080ef1ce0c34f0d09 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Wed, 8 Apr 2020 13:55:51 -0400 Subject: [PATCH 12/12] [release-branch.go1.13] go1.13.10 Change-Id: I1ed1bc6652724d2e365f89de802c79ecc5c2660d Reviewed-on: https://go-review.googlesource.com/c/go/+/227639 Run-TryBot: Andrew Bonventre TryBot-Result: Gobot Gobot Reviewed-by: Alexander Rakoczy --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4c9ff5797284b2..6d0e854fc6442a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -go1.13.9 \ No newline at end of file +go1.13.10 \ No newline at end of file