9 Commits

Author SHA1 Message Date
Vorapol Rinsatitnon
a95773b5a1 Update to go1.23.3 2024-11-09 19:25:36 +11:00
Vorapol Rinsatitnon
998d564602 Update patch 4 2024-11-09 18:59:13 +11:00
Vorapol Rinsatitnon
a50237a54f Add patch files 2024-11-09 17:58:11 +11:00
Vorapol Rinsatitnon
aeb9ac9a7f Update README to reflect changes 2024-10-05 15:18:36 +10:00
Vorapol Rinsatitnon
d97201a53d Add Windows version info to syscall 2024-10-05 15:10:54 +10:00
Vorapol Rinsatitnon
0468b8b0ad Add sysSocket fallback (revert 7c1157f) 2024-10-05 14:27:19 +10:00
Vorapol Rinsatitnon
60f9e8454d Add Windows 7 console handle workaround (revert 48042aa) 2024-10-05 14:17:43 +10:00
Vorapol Rinsatitnon
45c3c1f20a Update to go v1.23.2 2024-10-02 08:34:23 +10:00
Vorapol Rinsatitnon
439cdb4d4e Update README.md 2024-09-27 06:25:14 +10:00
45 changed files with 4670 additions and 123 deletions

View File

@@ -23,10 +23,12 @@ _Gopher image by [Renee French][rf], licensed under [Creative Commons 4.0 Attrib
Every release includes the following modifications:
- Restored Windows 7 and Windows Server 2008 R2 support (reverted [693def1](https://github.com/golang/go/commit/693def151adff1af707d82d28f55dba81ceb08e1))
- Switched back to RtlGenRandom from ProcessPrng, which breaks Win7/2008R2 (reverted [693def1](https://github.com/golang/go/commit/693def151adff1af707d82d28f55dba81ceb08e1))
- Added back LoadLibraryA fallback to load system libraries (reverted [a17d959](https://github.com/golang/go/commit/a17d959debdb04cd550016a3501dd09d50cd62e7))
- Added back sysSocket fallback for socket syscalls (reverted [7c1157f](https://github.com/golang/go/commit/7c1157f9544922e96945196b47b95664b1e39108))
- Added back Windows 7 console handle workaround (reverted [48042aa](https://github.com/golang/go/commit/48042aa09c2f878c4faa576948b07fe625c4707a))
- Restored deprecated `go get` behavior for use outside modules (reverted [de4d503](https://github.com/golang/go/commit/de4d50316fb5c6d1529aa5377dc93b26021ee843))
- Includes all improvements and bug fixes from Go 1.23.1
- Includes all improvements and bug fixes from the corresponding upstream Go release
The Windows binary provided here also supports Windows 7 and Windows Server 2008 R2
@@ -38,7 +40,7 @@ Binary distributions are **available at the [release page](https://github.com/th
#### Windows Installation
1. Download the `go-legacy-win7-<version>.windows-<arch>.zip` file.
1. Download the `go-legacy-win7-<version>.windows_<arch>.zip` file.
2. Extract the ZIP to `C:\` (or any preferred location). This will create a `go-legacy-win7` folder.
3. Add the following to your system environment variables:
- Add `C:\go-legacy-win7\bin` (or your chosen path) to the system `PATH`.
@@ -49,15 +51,15 @@ Binary distributions are **available at the [release page](https://github.com/th
#### macOS and Linux Installation
1. Download the appropriate `go-legacy-win7-<version>.<os>-<arch>.tar.gz` file.
1. Download the appropriate `go-legacy-win7-<version>.<os>_<arch>.tar.gz` file.
- For macOS: `go-legacy-win7-<version>.darwin-<arch>.tar.gz`
- For Linux: `go-legacy-win7-<version>.linux-<arch>.tar.gz`
- For macOS: `go-legacy-win7-<version>.darwin_<arch>.tar.gz`
- For Linux: `go-legacy-win7-<version>.linux_<arch>.tar.gz`
2. Extract the archive to `/usr/local`:
```
sudo tar -C /usr/local -xzf go-legacy-win7-<version>.<os>-<arch>.tar.gz
sudo tar -C /usr/local -xzf go-legacy-win7-<version>.<os>_<arch>.tar.gz
```
3. Add the following to your shell configuration file:

View File

@@ -1,2 +1,2 @@
go1.23.1
time 2024-08-29T20:56:24Z
go1.23.3
time 2024-11-06T18:46:45Z

View File

@@ -0,0 +1,159 @@
From f1f146d4534fc925bceffe523852fe2261841008 Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sat, 21 Sep 2024 23:56:11 +1000
Subject: [PATCH] Switch ProcessPrng back to RtlGenRandom (revert 693def1)
---
src/crypto/rand/rand.go | 2 +-
src/crypto/rand/rand_windows.go | 7 +++--
.../syscall/windows/syscall_windows.go | 2 +-
.../syscall/windows/zsyscall_windows.go | 7 ++---
src/runtime/os_windows.go | 30 ++++++++++++-------
5 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
index d16d7a1..cdfeb06 100644
--- a/src/crypto/rand/rand.go
+++ b/src/crypto/rand/rand.go
@@ -16,7 +16,7 @@ import "io"
// - On macOS and iOS, Reader uses arc4random_buf(3).
// - On OpenBSD and NetBSD, Reader uses getentropy(2).
// - On other Unix-like systems, Reader reads from /dev/urandom.
-// - On Windows, Reader uses the ProcessPrng API.
+// - On Windows, Reader uses the RtlGenRandom API.
// - On js/wasm, Reader uses the Web Crypto API.
// - On wasip1/wasm, Reader uses random_get from wasi_snapshot_preview1.
var Reader io.Reader
diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
index 7380f1f..6c0655c 100644
--- a/src/crypto/rand/rand_windows.go
+++ b/src/crypto/rand/rand_windows.go
@@ -15,8 +15,11 @@ func init() { Reader = &rngReader{} }
type rngReader struct{}
-func (r *rngReader) Read(b []byte) (int, error) {
- if err := windows.ProcessPrng(b); err != nil {
+func (r *rngReader) Read(b []byte) (n int, err error) {
+ // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at
+ // most 1<<31-1 bytes at a time so that this works the same on 32-bit
+ // and 64-bit systems.
+ if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {
return 0, err
}
return len(b), nil
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index cc26a50..b0b5a64 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -414,7 +414,7 @@ func ErrorLoadingGetTempPath2() error {
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
//sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW
-//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng
+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
type FILE_ID_BOTH_DIR_INFO struct {
NextEntryOffset uint32
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index 414ad26..062641c 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -38,7 +38,6 @@ func errnoErr(e syscall.Errno) error {
var (
modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
- modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll"))
modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
@@ -57,7 +56,7 @@ var (
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
procCreateEventW = modkernel32.NewProc("CreateEventW")
procGetACP = modkernel32.NewProc("GetACP")
@@ -183,12 +182,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32
return
}
-func ProcessPrng(buf []byte) (err error) {
+func RtlGenRandom(buf []byte) (err error) {
var _p0 *byte
if len(buf) > 0 {
_p0 = &buf[0]
}
- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 4aabc29..0273580 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -127,8 +127,15 @@ var (
_WriteFile,
_ stdFunction
- // Use ProcessPrng to generate cryptographically random data.
- _ProcessPrng stdFunction
+ // Use RtlGenRandom to generate cryptographically random data.
+ // This approach has been recommended by Microsoft (see issue
+ // 15589 for details).
+ // The RtlGenRandom is not listed in advapi32.dll, instead
+ // RtlGenRandom function can be found by searching for SystemFunction036.
+ // Also some versions of Mingw cannot link to SystemFunction036
+ // when building executable as Cgo. So load SystemFunction036
+ // manually during runtime startup.
+ _RtlGenRandom stdFunction
// Load ntdll.dll manually during startup, otherwise Mingw
// links wrong printf function to cgo executable (see issue
@@ -146,10 +153,11 @@ var (
)
var (
- bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}
- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
+ advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0}
+ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
+ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
+ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
+ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
)
// Function to be called by windows CreateThread
@@ -263,11 +271,11 @@ func windows_QueryPerformanceFrequency() int64 {
}
func loadOptionalSyscalls() {
- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])
- if bcryptPrimitives == 0 {
- throw("bcryptprimitives.dll not found")
+ a32 := windowsLoadSystemLib(advapi32dll[:])
+ if a32 == 0 {
+ throw("advapi32.dll not found")
}
- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000"))
+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
n32 := windowsLoadSystemLib(ntdlldll[:])
if n32 == 0 {
@@ -500,7 +508,7 @@ func osinit() {
//go:nosplit
func readRandom(r []byte) int {
n := 0
- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
n = len(r)
}
return n
--
2.47.0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
From 3593bfc89de341818aefadf365ca615b78a8c958 Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sun, 22 Sep 2024 00:34:20 +1000
Subject: [PATCH] Restore related GOPATH-mode go get functions
---
src/cmd/go/internal/load/pkg.go | 59 +++++++++++++++++++++++++++++++++
src/cmd/go/internal/par/work.go | 38 +++++++++++++++++++++
src/cmd/go/internal/vcs/vcs.go | 39 +++++++++++++++++++---
3 files changed, 132 insertions(+), 4 deletions(-)
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 7c402b4..cb38b53 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -604,6 +604,51 @@ func (sp *ImportStack) shorterThan(t []string) bool {
// we return the same pointer each time.
var packageCache = map[string]*Package{}
+// ClearPackageCache clears the in-memory package cache and the preload caches.
+// It is only for use by GOPATH-based "go get".
+// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function.
+func ClearPackageCache() {
+ clear(packageCache)
+ resolvedImportCache.Clear()
+ packageDataCache.Clear()
+}
+
+// ClearPackageCachePartial clears packages with the given import paths from the
+// in-memory package cache and the preload caches. It is only for use by
+// GOPATH-based "go get".
+// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function.
+func ClearPackageCachePartial(args []string) {
+ shouldDelete := make(map[string]bool)
+ for _, arg := range args {
+ shouldDelete[arg] = true
+ if p := packageCache[arg]; p != nil {
+ delete(packageCache, arg)
+ }
+ }
+ resolvedImportCache.DeleteIf(func(key importSpec) bool {
+ return shouldDelete[key.path]
+ })
+ packageDataCache.DeleteIf(func(key string) bool {
+ return shouldDelete[key]
+ })
+}
+
+// ReloadPackageNoFlags is like LoadImport but makes sure
+// not to use the package cache.
+// It is only for use by GOPATH-based "go get".
+// TODO(rsc): When GOPATH-based "go get" is removed, delete this function.
+func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package {
+ p := packageCache[arg]
+ if p != nil {
+ delete(packageCache, arg)
+ resolvedImportCache.DeleteIf(func(key importSpec) bool {
+ return key.path == p.ImportPath
+ })
+ packageDataCache.Delete(p.ImportPath)
+ }
+ return LoadPackage(context.TODO(), PackageOpts{}, arg, base.Cwd(), stk, nil, 0)
+}
+
// dirToImportPath returns the pseudo-import path we use for a package
// outside the Go path. It begins with _/ and then contains the full path
// to the directory. If the package lives in c:\home\gopher\my\pkg then
@@ -655,6 +700,20 @@ const (
cmdlinePkgLiteral
)
+// LoadImport scans the directory named by path, which must be an import path,
+// but possibly a local import path (an absolute file system path or one beginning
+// with ./ or ../). A local relative path is interpreted relative to srcDir.
+// It returns a *Package describing the package found in that directory.
+// LoadImport does not set tool flags and should only be used by
+// this package, as part of a bigger load operation, and by GOPATH-based "go get".
+// TODO(rsc): When GOPATH-based "go get" is removed, unexport this function.
+// The returned PackageError, if any, describes why parent is not allowed
+// to import the named package, with the error referring to importPos.
+// The PackageError can only be non-nil when parent is not nil.
+func LoadImport(ctx context.Context, opts PackageOpts, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) (*Package, *PackageError) {
+ return loadImport(ctx, opts, nil, path, srcDir, parent, stk, importPos, mode)
+}
+
// LoadPackage does Load import, but without a parent package load contezt
func LoadPackage(ctx context.Context, opts PackageOpts, path, srcDir string, stk *ImportStack, importPos []token.Position, mode int) *Package {
p, err := loadImport(ctx, opts, nil, path, srcDir, nil, stk, importPos, mode)
diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go
index 881b51b..3f1e69a 100644
--- a/src/cmd/go/internal/par/work.go
+++ b/src/cmd/go/internal/par/work.go
@@ -180,3 +180,41 @@ func (c *Cache[K, V]) Get(key K) (V, bool) {
}
return e.result, true
}
+
+// Clear removes all entries in the cache.
+//
+// Concurrent calls to Get may return old values. Concurrent calls to Do
+// may return old values or store results in entries that have been deleted.
+//
+// TODO(jayconrod): Delete this after the package cache clearing functions
+// in internal/load have been removed.
+func (c *Cache[K, V]) Clear() {
+ c.m.Clear()
+}
+
+// Delete removes an entry from the map. It is safe to call Delete for an
+// entry that does not exist. Delete will return quickly, even if the result
+// for a key is still being computed; the computation will finish, but the
+// result won't be accessible through the cache.
+//
+// TODO(jayconrod): Delete this after the package cache clearing functions
+// in internal/load have been removed.
+func (c *Cache[K, V]) Delete(key K) {
+ c.m.Delete(key)
+}
+
+// DeleteIf calls pred for each key in the map. If pred returns true for a key,
+// DeleteIf removes the corresponding entry. If the result for a key is
+// still being computed, DeleteIf will remove the entry without waiting for
+// the computation to finish. The result won't be accessible through the cache.
+//
+// TODO(jayconrod): Delete this after the package cache clearing functions
+// in internal/load have been removed.
+func (c *Cache[K, V]) DeleteIf(pred func(key K) bool) {
+ c.m.Range(func(key, _ any) bool {
+ if key := key.(K); pred(key) {
+ c.Delete(key)
+ }
+ return true
+ })
+}
diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go
index 19a6a5e..044d02e 100644
--- a/src/cmd/go/internal/vcs/vcs.go
+++ b/src/cmd/go/internal/vcs/vcs.go
@@ -1013,11 +1013,11 @@ var defaultGOVCS = govcsConfig{
{"public", []string{"git", "hg"}},
}
-// checkGOVCS checks whether the policy defined by the environment variable
+// CheckGOVCS checks whether the policy defined by the environment variable
// GOVCS allows the given vcs command to be used with the given repository
// root path. Note that root may not be a real package or module path; it's
// the same as the root path in the go-import meta tag.
-func checkGOVCS(vcs *Cmd, root string) error {
+func CheckGOVCS(vcs *Cmd, root string) error {
if vcs == vcsMod {
// Direct module (proxy protocol) fetches don't
// involve an external version control system
@@ -1045,6 +1045,37 @@ func checkGOVCS(vcs *Cmd, root string) error {
return nil
}
+// CheckNested checks for an incorrectly-nested VCS-inside-VCS
+// situation for dir, checking parents up until srcRoot.
+func CheckNested(vcs *Cmd, dir, srcRoot string) error {
+ if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
+ return fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
+ }
+
+ otherDir := dir
+ for len(otherDir) > len(srcRoot) {
+ for _, otherVCS := range vcsList {
+ if isVCSRoot(otherDir, otherVCS.RootNames) {
+ // Allow expected vcs in original dir.
+ if otherDir == dir && otherVCS == vcs {
+ continue
+ }
+ // Otherwise, we have one VCS inside a different VCS.
+ return fmt.Errorf("directory %q uses %s, but parent %q uses %s", dir, vcs.Cmd, otherDir, otherVCS.Cmd)
+ }
+ }
+ // Move to parent.
+ newDir := filepath.Dir(otherDir)
+ if len(newDir) >= len(otherDir) {
+ // Shouldn't happen, but just in case, stop.
+ break
+ }
+ otherDir = newDir
+ }
+
+ return nil
+}
+
// RepoRoot describes the repository root for a tree of source code.
type RepoRoot struct {
Repo string // repository URL, including scheme
@@ -1160,7 +1191,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
if vcs == nil {
return nil, fmt.Errorf("unknown version control system %q", match["vcs"])
}
- if err := checkGOVCS(vcs, match["root"]); err != nil {
+ if err := CheckGOVCS(vcs, match["root"]); err != nil {
return nil, err
}
var repoURL string
@@ -1349,7 +1380,7 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
}
}
- if err := checkGOVCS(vcs, mmi.Prefix); err != nil {
+ if err := CheckGOVCS(vcs, mmi.Prefix); err != nil {
return nil, err
}
--
2.47.0

View File

@@ -0,0 +1,307 @@
From ed249cf4dbfe31b9cea185100b74c38e5c6e3bd9 Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sat, 9 Nov 2024 18:54:55 +1100
Subject: [PATCH] Add back LoadLibraryA fallback
---
src/runtime/export_windows_test.go | 4 ++
src/runtime/os_windows.go | 60 ++++++++++++++++++++++++++++-
src/runtime/syscall_windows.go | 17 +++++++-
src/runtime/syscall_windows_test.go | 23 ++++++++++-
src/syscall/dll_windows.go | 28 +++++++++++++-
src/syscall/security_windows.go | 1 +
src/syscall/zsyscall_windows.go | 10 +++++
7 files changed, 136 insertions(+), 7 deletions(-)
diff --git a/src/runtime/export_windows_test.go b/src/runtime/export_windows_test.go
index 4880e62a55..8bfff0bc93 100644
--- a/src/runtime/export_windows_test.go
+++ b/src/runtime/export_windows_test.go
@@ -36,3 +36,7 @@ func NewContextStub() *ContextStub {
ctx.set_fp(getcallerfp())
return &ContextStub{ctx}
}
+
+func LoadLibraryExStatus() (useEx, haveEx, haveFlags bool) {
+ return useLoadLibraryEx, _LoadLibraryExW != nil, _AddDllDirectory != nil
+}
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 02735802d4..c76df9da22 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -41,6 +41,7 @@ const (
//go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll"
@@ -74,6 +75,7 @@ var (
// Following syscalls are available on every Windows PC.
// All these variables are set by the Windows executable
// loader before the Go program starts.
+ _AddDllDirectory,
_AddVectoredContinueHandler,
_AddVectoredExceptionHandler,
_CloseHandle,
@@ -99,6 +101,7 @@ var (
_SetThreadContext,
_LoadLibraryExW,
_LoadLibraryW,
+ _LoadLibraryA,
_PostQueuedCompletionStatus,
_QueryPerformanceCounter,
_QueryPerformanceFrequency,
@@ -157,7 +160,6 @@ var (
ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
)
// Function to be called by windows CreateThread
@@ -253,7 +255,36 @@ func windows_GetSystemDirectory() string {
}
func windowsLoadSystemLib(name []uint16) uintptr {
- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
+ if useLoadLibraryEx {
+ return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
+ } else {
+ var nameBytes [_MAX_PATH]byte
+ n := len(name)
+ if n > len(nameBytes) {
+ n = len(nameBytes)
+ }
+ for i := 0; i < n && name[i] != 0; i++ {
+ nameBytes[i] = byte(name[i])
+ }
+
+ // Construct the full path
+ var fullPath [_MAX_PATH]byte
+ copy(fullPath[:], sysDirectory[:sysDirectoryLen])
+ pathLen := sysDirectoryLen
+ for i := 0; i < len(nameBytes) && nameBytes[i] != 0 && pathLen < _MAX_PATH; i++ {
+ fullPath[pathLen] = nameBytes[i]
+ pathLen++
+ }
+
+ // Ensure null-termination
+ if pathLen < _MAX_PATH {
+ fullPath[pathLen] = 0
+ } else {
+ fullPath[_MAX_PATH-1] = 0
+ }
+
+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&fullPath[0])))
+ }
}
//go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter
@@ -271,6 +302,15 @@ func windows_QueryPerformanceFrequency() int64 {
}
func loadOptionalSyscalls() {
+ var kernel32dll = []byte("kernel32.dll\000")
+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))
+ if k32 == 0 {
+ throw("kernel32.dll not found")
+ }
+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000"))
+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
+ useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
+
a32 := windowsLoadSystemLib(advapi32dll[:])
if a32 == 0 {
throw("advapi32.dll not found")
@@ -365,6 +405,22 @@ const (
// in sys_windows_386.s and sys_windows_amd64.s:
func getlasterror() uint32
+// When loading DLLs, we prefer to use LoadLibraryEx with
+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not
+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*
+// flags are not available on some versions of Windows without a
+// security patch.
+//
+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
+// systems that have KB2533623 installed. To determine whether the
+// flags are available, use GetProcAddress to get the address of the
+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
+// flags can be used with LoadLibraryEx."
+var useLoadLibraryEx bool
+
var timeBeginPeriodRetValue uint32
// osRelaxMinNS indicates that sysmon shouldn't osRelax if the next
diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go
index 85b1b8c902..eb808feea5 100644
--- a/src/runtime/syscall_windows.go
+++ b/src/runtime/syscall_windows.go
@@ -413,10 +413,23 @@ func callbackWrap(a *callbackArgs) {
const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
+// When available, this function will use LoadLibraryEx with the filename
+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that
+// do not have that option, absoluteFilepath should contain a fallback
+// to the full path inside of system32 for use with vanilla LoadLibrary.
+//
//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {
+ if useLoadLibraryEx {
+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
+ } else {
+ handle, _, err = syscall_SyscallN(
+ uintptr(unsafe.Pointer(_LoadLibraryW)),
+ uintptr(unsafe.Pointer(absoluteFilepath)),
+ )
+ }
KeepAlive(filename)
+ KeepAlive(absoluteFilepath)
if handle != 0 {
err = 0
}
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 156cf3eb8e..2db5b61a9b 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -1166,7 +1166,10 @@ uintptr_t cfunc(void) {
dll, err = syscall.LoadDLL(name)
if err == nil {
dll.Release()
- t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
+ if wantLoadLibraryEx() {
+ t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
+ }
+ t.Skip("insecure load of DLL, but expected")
}
}
@@ -1219,6 +1222,24 @@ func TestSyscallStackUsage(t *testing.T) {
syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
}
+// wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
+func wantLoadLibraryEx() bool {
+ return testenv.Builder() != "" && (runtime.GOARCH == "amd64" || runtime.GOARCH == "386")
+}
+
+func TestLoadLibraryEx(t *testing.T) {
+ use, have, flags := runtime.LoadLibraryExStatus()
+ if use {
+ return // success.
+ }
+ if wantLoadLibraryEx() {
+ t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)",
+ have, flags)
+ }
+ t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
+ have, flags)
+}
+
var (
modwinmm = syscall.NewLazyDLL("winmm.dll")
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go
index 81134cb0bd..b3554d349b 100644
--- a/src/syscall/dll_windows.go
+++ b/src/syscall/dll_windows.go
@@ -44,7 +44,7 @@ func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a
func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
// A DLL implements access to a single DLL.
@@ -53,6 +53,26 @@ type DLL struct {
Handle Handle
}
+// We use this for computing the absolute path for system DLLs on systems
+// where SEARCH_SYSTEM32 is not available.
+var systemDirectoryPrefix string
+
+func init() {
+ n := uint32(MAX_PATH)
+ for {
+ b := make([]uint16, n)
+ l, e := getSystemDirectory(&b[0], n)
+ if e != nil {
+ panic("Unable to determine system directory: " + e.Error())
+ }
+ if l <= n {
+ systemDirectoryPrefix = UTF16ToString(b[:l]) + "\\"
+ break
+ }
+ n = l
+ }
+}
+
// LoadDLL loads the named DLL file into memory.
//
// If name is not an absolute path and is not a known system DLL used by
@@ -69,7 +89,11 @@ func LoadDLL(name string) (*DLL, error) {
var h uintptr
var e Errno
if sysdll.IsSystemDLL[name] {
- h, e = loadsystemlibrary(namep)
+ absoluteFilepathp, err := UTF16PtrFromString(systemDirectoryPrefix + name)
+ if err != nil {
+ return nil, err
+ }
+ h, e = loadsystemlibrary(namep, absoluteFilepathp)
} else {
h, e = loadlibrary(namep)
}
diff --git a/src/syscall/security_windows.go b/src/syscall/security_windows.go
index 4e988c418a..45b1908b71 100644
--- a/src/syscall/security_windows.go
+++ b/src/syscall/security_windows.go
@@ -290,6 +290,7 @@ type Tokenprimarygroup struct {
//sys OpenProcessToken(h Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
//sys GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) = advapi32.GetTokenInformation
//sys GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
+//sys getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW
// An access token contains the security information for a logon session.
// The system creates an access token when a user logs on, and every
diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go
index d8d8594a55..28369e3b91 100644
--- a/src/syscall/zsyscall_windows.go
+++ b/src/syscall/zsyscall_windows.go
@@ -128,6 +128,7 @@ var (
procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
procGetStdHandle = modkernel32.NewProc("GetStdHandle")
+ procGetSystemDirectoryW = modkernel32.NewProc("GetSystemDirectoryW")
procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
procGetTempPathW = modkernel32.NewProc("GetTempPathW")
procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
@@ -870,6 +871,15 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) {
return
}
+func getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) {
+ r0, _, e1 := Syscall(procGetSystemDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0)
+ len = uint32(r0)
+ if len == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func GetSystemTimeAsFileTime(time *Filetime) {
Syscall(procGetSystemTimeAsFileTime.Addr(), 1, uintptr(unsafe.Pointer(time)), 0, 0)
return
--
2.47.0

View File

@@ -0,0 +1,66 @@
From 60f9e8454df41affe07266e795f8a1d22567fd3e Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sat, 5 Oct 2024 14:17:43 +1000
Subject: [PATCH] Add Windows 7 console handle workaround (revert 48042aa)
---
src/syscall/exec_windows.go | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 1220de4..815dfd6 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -317,6 +317,17 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
}
}
+ var maj, min, build uint32
+ rtlGetNtVersionNumbers(&maj, &min, &build)
+ isWin7 := maj < 6 || (maj == 6 && min <= 1)
+ // NT kernel handles are divisible by 4, with the bottom 3 bits left as
+ // a tag. The fully set tag correlates with the types of handles we're
+ // concerned about here. Except, the kernel will interpret some
+ // special handle values, like -1, -2, and so forth, so kernelbase.dll
+ // checks to see that those bottom three bits are checked, but that top
+ // bit is not checked.
+ isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
+
p, _ := GetCurrentProcess()
parentProcess := p
if sys.ParentProcess != 0 {
@@ -325,7 +336,15 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
fd := make([]Handle, len(attr.Files))
for i := range attr.Files {
if attr.Files[i] > 0 {
- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
+ destinationProcessHandle := parentProcess
+
+ // On Windows 7, console handles aren't real handles, and can only be duplicated
+ // into the current process, not a parent one, which amounts to the same thing.
+ if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
+ destinationProcessHandle = p
+ }
+
+ err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
if err != nil {
return 0, 0, err
}
@@ -356,6 +375,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
fd = append(fd, sys.AdditionalInheritedHandles...)
+ // On Windows 7, console handles aren't real handles, so don't pass them
+ // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ for i := range fd {
+ if isLegacyWin7ConsoleHandle(fd[i]) {
+ fd[i] = 0
+ }
+ }
+
// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
// to treat the entire list as empty, so remove NULL handles.
j := 0
--
2.47.0

View File

@@ -0,0 +1,177 @@
From 0468b8b0addf825a274d81630087d62db495a562 Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sat, 5 Oct 2024 14:27:19 +1000
Subject: [PATCH] Add sysSocket fallback (revert 7c1157f)
---
src/net/hook_windows.go | 1 +
src/net/internal/socktest/main_test.go | 2 +-
.../internal/socktest/main_windows_test.go | 22 ++++++++++++++
src/net/internal/socktest/sys_windows.go | 29 +++++++++++++++++++
src/net/main_windows_test.go | 3 ++
src/net/sock_windows.go | 14 +++++++++
src/syscall/exec_windows.go | 1 -
7 files changed, 70 insertions(+), 2 deletions(-)
create mode 100644 src/net/internal/socktest/main_windows_test.go
diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go
index f7c5b5a..6b82be5 100644
--- a/src/net/hook_windows.go
+++ b/src/net/hook_windows.go
@@ -13,6 +13,7 @@ var (
hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts"
// Placeholders for socket system calls.
+ socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket
connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
listenFunc func(syscall.Handle, int) error = syscall.Listen
diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
index 967ce67..0197feb 100644
--- a/src/net/internal/socktest/main_test.go
+++ b/src/net/internal/socktest/main_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !js && !plan9 && !wasip1 && !windows
+//go:build !js && !plan9 && !wasip1
package socktest_test
diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go
new file mode 100644
index 0000000..df1cb97
--- /dev/null
+++ b/src/net/internal/socktest/main_windows_test.go
@@ -0,0 +1,22 @@
+// Copyright 2015 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 socktest_test
+
+import "syscall"
+
+var (
+ socketFunc func(int, int, int) (syscall.Handle, error)
+ closeFunc func(syscall.Handle) error
+)
+
+func installTestHooks() {
+ socketFunc = sw.Socket
+ closeFunc = sw.Closesocket
+}
+
+func uninstallTestHooks() {
+ socketFunc = syscall.Socket
+ closeFunc = syscall.Closesocket
+}
diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
index 2f02446..2b89362 100644
--- a/src/net/internal/socktest/sys_windows.go
+++ b/src/net/internal/socktest/sys_windows.go
@@ -9,6 +9,35 @@ import (
"syscall"
)
+// Socket wraps syscall.Socket.
+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
+ sw.once.Do(sw.init)
+ so := &Status{Cookie: cookie(family, sotype, proto)}
+ sw.fmu.RLock()
+ f, _ := sw.fltab[FilterSocket]
+ sw.fmu.RUnlock()
+ af, err := f.apply(so)
+ if err != nil {
+ return syscall.InvalidHandle, err
+ }
+ s, so.Err = syscall.Socket(family, sotype, proto)
+ if err = af.apply(so); err != nil {
+ if so.Err == nil {
+ syscall.Closesocket(s)
+ }
+ return syscall.InvalidHandle, err
+ }
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
+ if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).OpenFailed++
+ return syscall.InvalidHandle, so.Err
+ }
+ nso := sw.addLocked(s, family, sotype, proto)
+ sw.stats.getLocked(nso.Cookie).Opened++
+ return s, nil
+}
+
// WSASocket wraps [syscall.WSASocket].
func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {
sw.once.Do(sw.init)
diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go
index bc024c0..07f21b7 100644
--- a/src/net/main_windows_test.go
+++ b/src/net/main_windows_test.go
@@ -8,6 +8,7 @@ import "internal/poll"
var (
// Placeholders for saving original socket system calls.
+ origSocket = socketFunc
origWSASocket = wsaSocketFunc
origClosesocket = poll.CloseFunc
origConnect = connectFunc
@@ -17,6 +18,7 @@ var (
)
func installTestHooks() {
+ socketFunc = sw.Socket
wsaSocketFunc = sw.WSASocket
poll.CloseFunc = sw.Closesocket
connectFunc = sw.Connect
@@ -26,6 +28,7 @@ func installTestHooks() {
}
func uninstallTestHooks() {
+ socketFunc = origSocket
wsaSocketFunc = origWSASocket
poll.CloseFunc = origClosesocket
connectFunc = origConnect
diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go
index a519909..ebdf4c3 100644
--- a/src/net/sock_windows.go
+++ b/src/net/sock_windows.go
@@ -20,6 +20,20 @@ func maxListenerBacklog() int {
func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),
nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)
+ if err == nil {
+ return s, nil
+ }
+ // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some
+ // old versions of Windows, see
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
+ // for details. Just use syscall.Socket, if windows.WSASocket failed.
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ syscall.ForkLock.RLock()
+ s, err = socketFunc(family, sotype, proto)
+ if err == nil {
+ syscall.CloseOnExec(s)
+ }
+ syscall.ForkLock.RUnlock()
if err != nil {
return syscall.InvalidHandle, os.NewSyscallError("socket", err)
}
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 815dfd6..d197380 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -14,7 +14,6 @@ import (
"unsafe"
)
-// ForkLock is not used on Windows.
var ForkLock sync.RWMutex
// EscapeArg rewrites command line argument s as prescribed
--
2.47.0

View File

@@ -0,0 +1,82 @@
From d97201a53d5ec76fa81b091bc0d4d64f6ff6ff8c Mon Sep 17 00:00:00 2001
From: Vorapol Rinsatitnon <vorapol.r@pm.me>
Date: Sat, 5 Oct 2024 15:10:54 +1000
Subject: [PATCH] Add Windows version info to syscall
---
src/syscall/exec_windows.go | 7 ++++---
src/syscall/types_windows.go | 10 ++++++++++
src/syscall/zsyscall_windows.go | 7 +++++++
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index d197380..f099a6f 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -316,9 +316,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
}
}
- var maj, min, build uint32
- rtlGetNtVersionNumbers(&maj, &min, &build)
- isWin7 := maj < 6 || (maj == 6 && min <= 1)
+ info := _OSVERSIONINFOW{}
+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
+ rtlGetVersion(&info)
+ isWin7 := info.majorVersion < 6 || (info.majorVersion == 6 && info.minorVersion <= 1)
// NT kernel handles are divisible by 4, with the bottom 3 bits left as
// a tag. The fully set tag correlates with the types of handles we're
// concerned about here. Except, the kernel will interpret some
diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go
index 6743675..37d0eff 100644
--- a/src/syscall/types_windows.go
+++ b/src/syscall/types_windows.go
@@ -1169,3 +1169,13 @@ const (
)
const UNIX_PATH_MAX = 108 // defined in afunix.h
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
+type _OSVERSIONINFOW struct {
+ osVersionInfoSize uint32
+ majorVersion uint32
+ minorVersion uint32
+ buildNumber uint32
+ platformId uint32
+ csdVersion [128]uint16
+}
diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go
index 28369e3..a47b090 100644
--- a/src/syscall/zsyscall_windows.go
+++ b/src/syscall/zsyscall_windows.go
@@ -43,6 +43,7 @@ var (
modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll"))
modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll"))
modnetapi32 = NewLazyDLL(sysdll.Add("netapi32.dll"))
+ modntdll = NewLazyDLL(sysdll.Add("ntdll.dll"))
modsecur32 = NewLazyDLL(sysdll.Add("secur32.dll"))
modshell32 = NewLazyDLL(sysdll.Add("shell32.dll"))
moduserenv = NewLazyDLL(sysdll.Add("userenv.dll"))
@@ -169,6 +170,7 @@ var (
procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
+ procRtlGetVersion = modntdll.NewProc("RtlGetVersion")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
@@ -1228,6 +1230,11 @@ func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err er
return
}
+func rtlGetVersion(info *_OSVERSIONINFOW) {
+ Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0)
+ return
+}
+
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
if r1&0xff == 0 {
--
2.47.0

View File

@@ -318,9 +318,9 @@ func containsClosure(f, c *ir.Func) bool {
return false
}
// Closures within function Foo are named like "Foo.funcN..."
// Closures within function Foo are named like "Foo.funcN..." or "Foo-rangeN".
// TODO(mdempsky): Better way to recognize this.
fn := f.Sym().Name
cn := c.Sym().Name
return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
return len(cn) > len(fn) && cn[:len(fn)] == fn && (cn[len(fn)] == '.' || cn[len(fn)] == '-')
}

View File

@@ -805,13 +805,19 @@ func elfwritefreebsdsig(out *OutBuf) int {
return int(sh.Size)
}
func addbuildinfo(val string) {
func addbuildinfo(ctxt *Link) {
val := *flagHostBuildid
if val == "gobuildid" {
buildID := *flagBuildid
if buildID == "" {
Exitf("-B gobuildid requires a Go build ID supplied via -buildid")
}
if ctxt.IsDarwin() {
buildinfo = uuidFromGoBuildId(buildID)
return
}
hashedBuildID := notsha256.Sum256([]byte(buildID))
buildinfo = hashedBuildID[:20]
@@ -821,11 +827,13 @@ func addbuildinfo(val string) {
if !strings.HasPrefix(val, "0x") {
Exitf("-B argument must start with 0x: %s", val)
}
ov := val
val = val[2:]
const maxLen = 32
maxLen := 32
if ctxt.IsDarwin() {
maxLen = 16
}
if hex.DecodedLen(len(val)) > maxLen {
Exitf("-B option too long (max %d digits): %s", maxLen, ov)
}

View File

@@ -297,6 +297,8 @@ func getMachoHdr() *MachoHdr {
return &machohdr
}
// Create a new Mach-O load command. ndata is the number of 32-bit words for
// the data (not including the load command header).
func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
if arch.PtrSize == 8 && (ndata&1 != 0) {
ndata++
@@ -849,6 +851,20 @@ func asmbMacho(ctxt *Link) {
}
}
if ctxt.IsInternal() && len(buildinfo) > 0 {
ml := newMachoLoad(ctxt.Arch, LC_UUID, 4)
// Mach-O UUID is 16 bytes
if len(buildinfo) < 16 {
buildinfo = append(buildinfo, make([]byte, 16)...)
}
// By default, buildinfo is already in UUIDv3 format
// (see uuidFromGoBuildId).
ml.data[0] = ctxt.Arch.ByteOrder.Uint32(buildinfo)
ml.data[1] = ctxt.Arch.ByteOrder.Uint32(buildinfo[4:])
ml.data[2] = ctxt.Arch.ByteOrder.Uint32(buildinfo[8:])
ml.data[3] = ctxt.Arch.ByteOrder.Uint32(buildinfo[12:])
}
if ctxt.IsInternal() && ctxt.NeedCodeSign() {
ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
ml.data[0] = uint32(codesigOff)

View File

@@ -42,7 +42,7 @@ func uuidFromGoBuildId(buildID string) []byte {
// to use this UUID flavor than any of the others. This is similar
// to how other linkers handle this (for example this code in lld:
// https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524).
rv[6] &= 0xcf
rv[6] &= 0x0f
rv[6] |= 0x30
rv[8] &= 0x3f
rv[8] |= 0xc0

View File

@@ -95,6 +95,7 @@ var (
flagN = flag.Bool("n", false, "no-op (deprecated)")
FlagS = flag.Bool("s", false, "disable symbol table")
flag8 bool // use 64-bit addresses in symbol table
flagHostBuildid = flag.String("B", "", "set ELF NT_GNU_BUILD_ID `note` or Mach-O UUID; use \"gobuildid\" to generate it from the Go build ID")
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references")
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
@@ -196,7 +197,6 @@ func Main(arch *sys.Arch, theArch Arch) {
flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF; use \"gobuildid\" to generate it from the Go build ID", addbuildinfo)
objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
objabi.AddVersionFlag() // -V
objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
@@ -294,6 +294,10 @@ func Main(arch *sys.Arch, theArch Arch) {
*flagBuildid = "go-openbsd"
}
if *flagHostBuildid != "" {
addbuildinfo(ctxt)
}
// enable benchmarking
var bench *benchmark.Metrics
if len(*benchmarkFlag) != 0 {

View File

@@ -32,28 +32,46 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
if int64(n) > remain {
n = int(remain)
}
m := n
pos1 := pos
n, err = syscall.Sendfile(dst, src, &pos1, n)
if n > 0 {
pos += int64(n)
written += int64(n)
remain -= int64(n)
// (n, nil) indicates that sendfile(2) has transferred
// the exact number of bytes we requested, or some unretryable
// error have occurred with partial bytes sent. Either way, we
// don't need to go through the following logic to check EINTR
// or fell into dstFD.pd.waitWrite, just continue to send the
// next chunk or break the loop.
if n == m {
continue
} else if err != syscall.EAGAIN &&
err != syscall.EINTR &&
err != syscall.EBUSY {
// Particularly, EPIPE. Errors like that would normally lead
// the subsequent sendfile(2) call to (-1, EBADF).
break
}
} else if err != syscall.EAGAIN && err != syscall.EINTR {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
// don't implement sendfile), and other errors.
// We should end the loop when there is no error
// returned from sendfile(2) or it is not a retryable error.
break
}
if err == syscall.EINTR {
continue
}
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
// don't implement sendfile), and other errors.
// We should end the loop when there is no error
// returned from sendfile(2) or it is not a retryable error.
if err != syscall.EAGAIN {
break
}
if err = dstFD.pd.waitWrite(dstFD.isFile); err != nil {
break
}
}
if err == syscall.EAGAIN {
err = nil
}
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
return
}

View File

@@ -50,6 +50,9 @@ func SendFile(dstFD *FD, src int, remain int64) (written int64, err error, handl
break
}
}
if err == syscall.EAGAIN {
err = nil
}
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
return
}

View File

@@ -61,6 +61,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
break
}
}
if err == syscall.EAGAIN {
err = nil
}
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
return
}

View File

@@ -13,6 +13,7 @@ var (
hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts"
// Placeholders for socket system calls.
socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket
connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
listenFunc func(syscall.Handle, int) error = syscall.Listen

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !js && !plan9 && !wasip1 && !windows
//go:build !js && !plan9 && !wasip1
package socktest_test

View File

@@ -0,0 +1,22 @@
// Copyright 2015 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 socktest_test
import "syscall"
var (
socketFunc func(int, int, int) (syscall.Handle, error)
closeFunc func(syscall.Handle) error
)
func installTestHooks() {
socketFunc = sw.Socket
closeFunc = sw.Closesocket
}
func uninstallTestHooks() {
socketFunc = syscall.Socket
closeFunc = syscall.Closesocket
}

View File

@@ -9,6 +9,35 @@ import (
"syscall"
)
// Socket wraps syscall.Socket.
func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
sw.once.Do(sw.init)
so := &Status{Cookie: cookie(family, sotype, proto)}
sw.fmu.RLock()
f, _ := sw.fltab[FilterSocket]
sw.fmu.RUnlock()
af, err := f.apply(so)
if err != nil {
return syscall.InvalidHandle, err
}
s, so.Err = syscall.Socket(family, sotype, proto)
if err = af.apply(so); err != nil {
if so.Err == nil {
syscall.Closesocket(s)
}
return syscall.InvalidHandle, err
}
sw.smu.Lock()
defer sw.smu.Unlock()
if so.Err != nil {
sw.stats.getLocked(so.Cookie).OpenFailed++
return syscall.InvalidHandle, so.Err
}
nso := sw.addLocked(s, family, sotype, proto)
sw.stats.getLocked(nso.Cookie).Opened++
return s, nil
}
// WSASocket wraps [syscall.WSASocket].
func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {
sw.once.Do(sw.init)

View File

@@ -8,6 +8,7 @@ import "internal/poll"
var (
// Placeholders for saving original socket system calls.
origSocket = socketFunc
origWSASocket = wsaSocketFunc
origClosesocket = poll.CloseFunc
origConnect = connectFunc
@@ -17,6 +18,7 @@ var (
)
func installTestHooks() {
socketFunc = sw.Socket
wsaSocketFunc = sw.WSASocket
poll.CloseFunc = sw.Closesocket
connectFunc = sw.Connect
@@ -26,6 +28,7 @@ func installTestHooks() {
}
func uninstallTestHooks() {
socketFunc = origSocket
wsaSocketFunc = origWSASocket
poll.CloseFunc = origClosesocket
connectFunc = origConnect

View File

@@ -20,6 +20,20 @@ func maxListenerBacklog() int {
func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),
nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)
if err == nil {
return s, nil
}
// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some
// old versions of Windows, see
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
// for details. Just use syscall.Socket, if windows.WSASocket failed.
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
s, err = socketFunc(family, sotype, proto)
if err == nil {
syscall.CloseOnExec(s)
}
syscall.ForkLock.RUnlock()
if err != nil {
return syscall.InvalidHandle, os.NewSyscallError("socket", err)
}

154
src/os/copy_test.go Normal file
View File

@@ -0,0 +1,154 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package os_test
import (
"bytes"
"errors"
"io"
"math/rand/v2"
"net"
"os"
"runtime"
"sync"
"testing"
"golang.org/x/net/nettest"
)
// Exercise sendfile/splice fast paths with a moderately large file.
//
// https://go.dev/issue/70000
func TestLargeCopyViaNetwork(t *testing.T) {
const size = 10 * 1024 * 1024
dir := t.TempDir()
src, err := os.Create(dir + "/src")
if err != nil {
t.Fatal(err)
}
defer src.Close()
if _, err := io.CopyN(src, newRandReader(), size); err != nil {
t.Fatal(err)
}
if _, err := src.Seek(0, 0); err != nil {
t.Fatal(err)
}
dst, err := os.Create(dir + "/dst")
if err != nil {
t.Fatal(err)
}
defer dst.Close()
client, server := createSocketPair(t, "tcp")
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
if n, err := io.Copy(dst, server); n != size || err != nil {
t.Errorf("copy to destination = %v, %v; want %v, nil", n, err, size)
}
}()
go func() {
defer wg.Done()
defer client.Close()
if n, err := io.Copy(client, src); n != size || err != nil {
t.Errorf("copy from source = %v, %v; want %v, nil", n, err, size)
}
}()
wg.Wait()
if _, err := dst.Seek(0, 0); err != nil {
t.Fatal(err)
}
if err := compareReaders(dst, io.LimitReader(newRandReader(), size)); err != nil {
t.Fatal(err)
}
}
func compareReaders(a, b io.Reader) error {
bufa := make([]byte, 4096)
bufb := make([]byte, 4096)
for {
na, erra := io.ReadFull(a, bufa)
if erra != nil && erra != io.EOF {
return erra
}
nb, errb := io.ReadFull(b, bufb)
if errb != nil && errb != io.EOF {
return errb
}
if !bytes.Equal(bufa[:na], bufb[:nb]) {
return errors.New("contents mismatch")
}
if erra == io.EOF && errb == io.EOF {
break
}
}
return nil
}
type randReader struct {
rand *rand.Rand
}
func newRandReader() *randReader {
return &randReader{rand.New(rand.NewPCG(0, 0))}
}
func (r *randReader) Read(p []byte) (int, error) {
var v uint64
var n int
for i := range p {
if n == 0 {
v = r.rand.Uint64()
n = 8
}
p[i] = byte(v & 0xff)
v >>= 8
n--
}
return len(p), nil
}
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
t.Helper()
if !nettest.TestableNetwork(proto) {
t.Skipf("%s does not support %q", runtime.GOOS, proto)
}
ln, err := nettest.NewLocalListener(proto)
if err != nil {
t.Fatalf("NewLocalListener error: %v", err)
}
t.Cleanup(func() {
if ln != nil {
ln.Close()
}
if client != nil {
client.Close()
}
if server != nil {
server.Close()
}
})
ch := make(chan struct{})
go func() {
var err error
server, err = ln.Accept()
if err != nil {
t.Errorf("Accept new connection error: %v", err)
}
ch <- struct{}{}
}()
client, err = net.Dial(proto, ln.Addr().String())
<-ch
if err != nil {
t.Fatalf("Dial new connection error: %v", err)
}
return client, server
}

View File

@@ -8,12 +8,17 @@
// v5.3: pidfd_open syscall, clone3 syscall;
// v5.4: P_PIDFD idtype support for waitid syscall;
// v5.6: pidfd_getfd syscall.
//
// N.B. Alternative Linux implementations may not follow this ordering. e.g.,
// QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until
// 8.0.
package os
import (
"errors"
"internal/syscall/unix"
"runtime"
"sync"
"syscall"
"unsafe"
@@ -139,14 +144,21 @@ func pidfdWorks() bool {
var checkPidfdOnce = sync.OnceValue(checkPidfd)
// checkPidfd checks whether all required pidfd-related syscalls work.
// This consists of pidfd_open and pidfd_send_signal syscalls, and waitid
// syscall with idtype of P_PIDFD.
// checkPidfd checks whether all required pidfd-related syscalls work. This
// consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with
// idtype of P_PIDFD, and clone(CLONE_PIDFD).
//
// Reasons for non-working pidfd syscalls include an older kernel and an
// execution environment in which the above system calls are restricted by
// seccomp or a similar technology.
func checkPidfd() error {
// In Android version < 12, pidfd-related system calls are not allowed
// by seccomp and trigger the SIGSYS signal. See issue #69065.
if runtime.GOOS == "android" {
ignoreSIGSYS()
defer restoreSIGSYS()
}
// Get a pidfd of the current process (opening of "/proc/self" won't
// work for waitid).
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
@@ -172,5 +184,27 @@ func checkPidfd() error {
return NewSyscallError("pidfd_send_signal", err)
}
// Verify that clone(CLONE_PIDFD) works.
//
// This shouldn't be necessary since pidfd_open was added in Linux 5.3,
// after CLONE_PIDFD in Linux 5.2, but some alternative Linux
// implementations may not adhere to this ordering.
if err := checkClonePidfd(); err != nil {
return err
}
return nil
}
// Provided by syscall.
//
//go:linkname checkClonePidfd
func checkClonePidfd() error
// Provided by runtime.
//
//go:linkname ignoreSIGSYS
func ignoreSIGSYS()
//go:linkname restoreSIGSYS
func restoreSIGSYS()

View File

@@ -14,15 +14,12 @@ import (
"net"
. "os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"testing"
"time"
"golang.org/x/net/nettest"
)
func TestCopyFileRange(t *testing.T) {
@@ -784,41 +781,3 @@ func testGetPollFDAndNetwork(t *testing.T, proto string) {
t.Fatalf("server Control error: %v", err)
}
}
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
t.Helper()
if !nettest.TestableNetwork(proto) {
t.Skipf("%s does not support %q", runtime.GOOS, proto)
}
ln, err := nettest.NewLocalListener(proto)
if err != nil {
t.Fatalf("NewLocalListener error: %v", err)
}
t.Cleanup(func() {
if ln != nil {
ln.Close()
}
if client != nil {
client.Close()
}
if server != nil {
server.Close()
}
})
ch := make(chan struct{})
go func() {
var err error
server, err = ln.Accept()
if err != nil {
t.Errorf("Accept new connection error: %v", err)
}
ch <- struct{}{}
}()
client, err = net.Dial(proto, ln.Addr().String())
<-ch
if err != nil {
t.Fatalf("Dial new connection error: %v", err)
}
return client, server
}

View File

@@ -208,6 +208,18 @@ func coroswitch_m(gp *g) {
// directly if possible.
setGNoWB(&mp.curg, gnext)
setMNoWB(&gnext.m, mp)
// Synchronize with any out-standing goroutine profile. We're about to start
// executing, and an invariant of the profiler is that we tryRecordGoroutineProfile
// whenever a goroutine is about to start running.
//
// N.B. We must do this before transitioning to _Grunning but after installing gnext
// in curg, so that we have a valid curg for allocation (tryRecordGoroutineProfile
// may allocate).
if goroutineProfile.active {
tryRecordGoroutineProfile(gnext, nil, osyield)
}
if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) {
// The CAS failed: use casgstatus, which will take care of
// coordinating with the garbage collector about the state change.

View File

@@ -1136,11 +1136,12 @@ func expandFrames(p []BlockProfileRecord) {
for i := range p {
cf := CallersFrames(p[i].Stack())
j := 0
for ; j < len(expandedStack); j++ {
for j < len(expandedStack) {
f, more := cf.Next()
// f.PC is a "call PC", but later consumers will expect
// "return PCs"
expandedStack[j] = f.PC + 1
j++
if !more {
break
}
@@ -1270,7 +1271,8 @@ func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok
// of calling ThreadCreateProfile directly.
func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
copy(p[0].Stack0[:], r.Stack)
i := copy(p[0].Stack0[:], r.Stack)
clear(p[0].Stack0[i:])
p = p[1:]
})
}
@@ -1649,7 +1651,8 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
return
}
for i, mr := range records[0:n] {
copy(p[i].Stack0[:], mr.Stack)
l := copy(p[i].Stack0[:], mr.Stack)
clear(p[i].Stack0[l:])
}
return
}

View File

@@ -879,8 +879,9 @@ func runPerThreadSyscall() {
}
const (
_SI_USER = 0
_SI_TKILL = -6
_SI_USER = 0
_SI_TKILL = -6
_SYS_SECCOMP = 1
)
// sigFromUser reports whether the signal was sent because of a call
@@ -892,6 +893,14 @@ func (c *sigctxt) sigFromUser() bool {
return code == _SI_USER || code == _SI_TKILL
}
// sigFromSeccomp reports whether the signal was sent from seccomp.
//
//go:nosplit
func (c *sigctxt) sigFromSeccomp() bool {
code := int32(c.sigcode())
return code == _SYS_SECCOMP
}
//go:nosplit
func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int32) {
r, _, err := syscall.Syscall6(syscall.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)

View File

@@ -13,3 +13,10 @@ package runtime
func (c *sigctxt) sigFromUser() bool {
return c.sigcode() == _SI_USER
}
// sigFromSeccomp reports whether the signal was sent from seccomp.
//
//go:nosplit
func (c *sigctxt) sigFromSeccomp() bool {
return false
}

View File

@@ -145,7 +145,7 @@ func TestMemoryProfiler(t *testing.T) {
}
t.Logf("Profile = %v", p)
stks := stacks(p)
stks := profileStacks(p)
for _, test := range tests {
if !containsStack(stks, test.stk) {
t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p)

View File

@@ -15,6 +15,7 @@ import (
"internal/syscall/unix"
"internal/testenv"
"io"
"iter"
"math"
"math/big"
"os"
@@ -981,7 +982,7 @@ func TestBlockProfile(t *testing.T) {
t.Fatalf("invalid profile: %v", err)
}
stks := stacks(p)
stks := profileStacks(p)
for _, test := range tests {
if !containsStack(stks, test.stk) {
t.Errorf("No matching stack entry for %v, want %+v", test.name, test.stk)
@@ -991,7 +992,7 @@ func TestBlockProfile(t *testing.T) {
}
func stacks(p *profile.Profile) (res [][]string) {
func profileStacks(p *profile.Profile) (res [][]string) {
for _, s := range p.Sample {
var stk []string
for _, l := range s.Location {
@@ -1004,6 +1005,22 @@ func stacks(p *profile.Profile) (res [][]string) {
return res
}
func blockRecordStacks(records []runtime.BlockProfileRecord) (res [][]string) {
for _, record := range records {
frames := runtime.CallersFrames(record.Stack())
var stk []string
for {
frame, more := frames.Next()
stk = append(stk, frame.Function)
if !more {
break
}
}
res = append(res, stk)
}
return res
}
func containsStack(got [][]string, want []string) bool {
for _, stk := range got {
if len(stk) < len(want) {
@@ -1288,7 +1305,7 @@ func TestMutexProfile(t *testing.T) {
t.Fatalf("invalid profile: %v", err)
}
stks := stacks(p)
stks := profileStacks(p)
for _, want := range [][]string{
{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1"},
} {
@@ -1328,6 +1345,28 @@ func TestMutexProfile(t *testing.T) {
t.Fatalf("profile samples total %v, want within range [%v, %v] (target: %v)", d, lo, hi, N*D)
}
})
t.Run("records", func(t *testing.T) {
// Record a mutex profile using the structured record API.
var records []runtime.BlockProfileRecord
for {
n, ok := runtime.MutexProfile(records)
if ok {
records = records[:n]
break
}
records = make([]runtime.BlockProfileRecord, n*2)
}
// Check that we see the same stack trace as the proto profile. For
// historical reason we expect a runtime.goexit root frame here that is
// omitted in the proto profile.
stks := blockRecordStacks(records)
want := []string{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1", "runtime.goexit"}
if !containsStack(stks, want) {
t.Errorf("No matching stack entry for %+v", want)
}
})
}
func TestMutexProfileRateAdjust(t *testing.T) {
@@ -1754,6 +1793,50 @@ func TestGoroutineProfileConcurrency(t *testing.T) {
}
}
// Regression test for #69998.
func TestGoroutineProfileCoro(t *testing.T) {
testenv.MustHaveParallelism(t)
goroutineProf := Lookup("goroutine")
// Set up a goroutine to just create and run coroutine goroutines all day.
iterFunc := func() {
p, stop := iter.Pull2(
func(yield func(int, int) bool) {
for i := 0; i < 10000; i++ {
if !yield(i, i) {
return
}
}
},
)
defer stop()
for {
_, _, ok := p()
if !ok {
break
}
}
}
var wg sync.WaitGroup
done := make(chan struct{})
wg.Add(1)
go func() {
defer wg.Done()
for {
iterFunc()
select {
case <-done:
default:
}
}
}()
// Take a goroutine profile. If the bug in #69998 is present, this will crash
// with high probability. We don't care about the output for this bug.
goroutineProf.WriteTo(io.Discard, 1)
}
func BenchmarkGoroutine(b *testing.B) {
withIdle := func(n int, fn func(b *testing.B)) func(b *testing.B) {
return func(b *testing.B) {
@@ -2441,16 +2524,7 @@ func TestTimeVDSO(t *testing.T) {
}
func TestProfilerStackDepth(t *testing.T) {
// Disable sampling, otherwise it's difficult to assert anything.
oldMemRate := runtime.MemProfileRate
runtime.MemProfileRate = 1
runtime.SetBlockProfileRate(1)
oldMutexRate := runtime.SetMutexProfileFraction(1)
t.Cleanup(func() {
runtime.MemProfileRate = oldMemRate
runtime.SetBlockProfileRate(0)
runtime.SetMutexProfileFraction(oldMutexRate)
})
t.Cleanup(disableSampling())
const depth = 128
go produceProfileEvents(t, depth)
@@ -2478,7 +2552,7 @@ func TestProfilerStackDepth(t *testing.T) {
}
t.Logf("Profile = %v", p)
stks := stacks(p)
stks := profileStacks(p)
var stk []string
for _, s := range stks {
if hasPrefix(s, test.prefix) {
@@ -2742,3 +2816,84 @@ runtime/pprof.inlineA`,
})
}
}
func TestProfileRecordNullPadding(t *testing.T) {
// Produce events for the different profile types.
t.Cleanup(disableSampling())
memSink = make([]byte, 1) // MemProfile
<-time.After(time.Millisecond) // BlockProfile
blockMutex(t) // MutexProfile
runtime.GC()
// Test that all profile records are null padded.
testProfileRecordNullPadding(t, "MutexProfile", runtime.MutexProfile)
testProfileRecordNullPadding(t, "GoroutineProfile", runtime.GoroutineProfile)
testProfileRecordNullPadding(t, "BlockProfile", runtime.BlockProfile)
testProfileRecordNullPadding(t, "MemProfile/inUseZero=true", func(p []runtime.MemProfileRecord) (int, bool) {
return runtime.MemProfile(p, true)
})
testProfileRecordNullPadding(t, "MemProfile/inUseZero=false", func(p []runtime.MemProfileRecord) (int, bool) {
return runtime.MemProfile(p, false)
})
// Not testing ThreadCreateProfile because it is broken, see issue 6104.
}
func testProfileRecordNullPadding[T runtime.StackRecord | runtime.MemProfileRecord | runtime.BlockProfileRecord](t *testing.T, name string, fn func([]T) (int, bool)) {
stack0 := func(sr *T) *[32]uintptr {
switch t := any(sr).(type) {
case *runtime.StackRecord:
return &t.Stack0
case *runtime.MemProfileRecord:
return &t.Stack0
case *runtime.BlockProfileRecord:
return &t.Stack0
default:
panic(fmt.Sprintf("unexpected type %T", sr))
}
}
t.Run(name, func(t *testing.T) {
var p []T
for {
n, ok := fn(p)
if ok {
p = p[:n]
break
}
p = make([]T, n*2)
for i := range p {
s0 := stack0(&p[i])
for j := range s0 {
// Poison the Stack0 array to identify lack of zero padding
s0[j] = ^uintptr(0)
}
}
}
if len(p) == 0 {
t.Fatal("no records found")
}
for _, sr := range p {
for i, v := range stack0(&sr) {
if v == ^uintptr(0) {
t.Fatalf("record p[%d].Stack0 is not null padded: %+v", i, sr)
}
}
}
})
}
// disableSampling configures the profilers to capture all events, otherwise
// it's difficult to assert anything.
func disableSampling() func() {
oldMemRate := runtime.MemProfileRate
runtime.MemProfileRate = 1
runtime.SetBlockProfileRate(1)
oldMutexRate := runtime.SetMutexProfileFraction(1)
return func() {
runtime.MemProfileRate = oldMemRate
runtime.SetBlockProfileRate(0)
runtime.SetMutexProfileFraction(oldMutexRate)
}
}

View File

@@ -575,15 +575,15 @@ func TestGdbAutotmpTypes(t *testing.T) {
// Check that the backtrace matches the source code.
types := []string{
"[]main.astruct;",
"bucket<string,main.astruct>;",
"hash<string,main.astruct>;",
"main.astruct;",
"hash<string,main.astruct> * map[string]main.astruct;",
"[]main.astruct",
"bucket<string,main.astruct>",
"hash<string,main.astruct>",
"main.astruct",
"hash<string,main.astruct> * map[string]main.astruct",
}
for _, name := range types {
if !strings.Contains(sgot, name) {
t.Fatalf("could not find %s in 'info typrs astruct' output", name)
t.Fatalf("could not find %q in 'info typrs astruct' output", name)
}
}
}

View File

@@ -605,6 +605,19 @@ var crashing atomic.Int32
var testSigtrap func(info *siginfo, ctxt *sigctxt, gp *g) bool
var testSigusr1 func(gp *g) bool
// sigsysIgnored is non-zero if we are currently ignoring SIGSYS. See issue #69065.
var sigsysIgnored uint32
//go:linkname ignoreSIGSYS os.ignoreSIGSYS
func ignoreSIGSYS() {
atomic.Store(&sigsysIgnored, 1)
}
//go:linkname restoreSIGSYS os.restoreSIGSYS
func restoreSIGSYS() {
atomic.Store(&sigsysIgnored, 0)
}
// sighandler is invoked when a signal occurs. The global g will be
// set to a gsignal goroutine and we will be running on the alternate
// signal stack. The parameter gp will be the value of the global g
@@ -715,6 +728,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
return
}
if sig == _SIGSYS && c.sigFromSeccomp() && atomic.Load(&sigsysIgnored) != 0 {
return
}
if flags&_SigKill != 0 {
dieFromSignal(sig)
}

View File

@@ -467,43 +467,37 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint
//go:linkname syscall_Syscall syscall.Syscall
//go:nosplit
func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3)
}
//go:linkname syscall_Syscall6 syscall.Syscall6
//go:nosplit
func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3, a4, a5, a6}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6)
}
//go:linkname syscall_Syscall9 syscall.Syscall9
//go:nosplit
func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9)
}
//go:linkname syscall_Syscall12 syscall.Syscall12
//go:nosplit
func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
}
//go:linkname syscall_Syscall15 syscall.Syscall15
//go:nosplit
func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
}
//go:linkname syscall_Syscall18 syscall.Syscall18
//go:nosplit
func syscall_Syscall18(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2, err uintptr) {
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18}
return syscall_SyscallN(fn, args[:nargs]...)
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
}
// maxArgs should be divisible by 2, as Windows stack
@@ -516,7 +510,15 @@ const maxArgs = 42
//go:linkname syscall_SyscallN syscall.SyscallN
//go:nosplit
func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
if len(args) > maxArgs {
return syscall_syscalln(fn, uintptr(len(args)), args...)
}
//go:nosplit
func syscall_syscalln(fn, n uintptr, args ...uintptr) (r1, r2, err uintptr) {
if n > uintptr(len(args)) {
panic("syscall: n > len(args)") // should not be reachable from user code
}
if n > maxArgs {
panic("runtime: SyscallN has too many arguments")
}
@@ -525,7 +527,7 @@ func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
// calls back into Go.
c := &getg().m.winsyscall
c.fn = fn
c.n = uintptr(len(args))
c.n = n
if c.n != 0 {
c.args = uintptr(noescape(unsafe.Pointer(&args[0])))
}

View File

@@ -1215,6 +1215,13 @@ func TestBigStackCallbackSyscall(t *testing.T) {
}
}
func TestSyscallStackUsage(t *testing.T) {
// Test that the stack usage of a syscall doesn't exceed the limit.
// See https://go.dev/issue/69813.
syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
}
// wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
func wantLoadLibraryEx() bool {
return testenv.Builder() != "" && (runtime.GOARCH == "amd64" || runtime.GOARCH == "386")

View File

@@ -26,10 +26,11 @@ type timer struct {
// mu protects reads and writes to all fields, with exceptions noted below.
mu mutex
astate atomic.Uint8 // atomic copy of state bits at last unlock
state uint8 // state bits
isChan bool // timer has a channel; immutable; can be read without lock
blocked uint32 // number of goroutines blocked on timer's channel
astate atomic.Uint8 // atomic copy of state bits at last unlock
state uint8 // state bits
isChan bool // timer has a channel; immutable; can be read without lock
blocked uint32 // number of goroutines blocked on timer's channel
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(arg, seq, delay) in the timer goroutine, so f must be
@@ -68,6 +69,20 @@ type timer struct {
// sendLock protects sends on the timer's channel.
// Not used for async (pre-Go 1.23) behavior when debug.asynctimerchan.Load() != 0.
sendLock mutex
// isSending is used to handle races between running a
// channel timer and stopping or resetting the timer.
// It is used only for channel timers (t.isChan == true).
// It is not used for tickers.
// The value is incremented when about to send a value on the channel,
// and decremented after sending the value.
// The stop/reset code uses this to detect whether it
// stopped the channel send.
//
// isSending is incremented only when t.mu is held.
// isSending is decremented only when t.sendLock is held.
// isSending is read only when both t.mu and t.sendLock are held.
isSending atomic.Int32
}
// init initializes a newly allocated timer t.
@@ -431,6 +446,15 @@ func (t *timer) stop() bool {
// Stop any future sends with stale values.
// See timer.unlockAndRun.
t.seq++
// If there is currently a send in progress,
// incrementing seq is going to prevent that
// send from actually happening. That means
// that we should return true: the timer was
// stopped, even though t.when may be zero.
if t.period == 0 && t.isSending.Load() > 0 {
pending = true
}
}
t.unlock()
if !async && t.isChan {
@@ -490,6 +514,7 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
t.maybeRunAsync()
}
t.trace("modify")
oldPeriod := t.period
t.period = period
if f != nil {
t.f = f
@@ -525,6 +550,15 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
// Stop any future sends with stale values.
// See timer.unlockAndRun.
t.seq++
// If there is currently a send in progress,
// incrementing seq is going to prevent that
// send from actually happening. That means
// that we should return true: the timer was
// stopped, even though t.when may be zero.
if oldPeriod == 0 && t.isSending.Load() > 0 {
pending = true
}
}
t.unlock()
if !async && t.isChan {
@@ -1013,6 +1047,15 @@ func (t *timer) unlockAndRun(now int64) {
}
t.updateHeap()
}
async := debug.asynctimerchan.Load() != 0
if !async && t.isChan && t.period == 0 {
// Tell Stop/Reset that we are sending a value.
if t.isSending.Add(1) < 0 {
throw("too many concurrent timer firings")
}
}
t.unlock()
if raceenabled {
@@ -1028,7 +1071,6 @@ func (t *timer) unlockAndRun(now int64) {
ts.unlock()
}
async := debug.asynctimerchan.Load() != 0
if !async && t.isChan {
// For a timer channel, we want to make sure that no stale sends
// happen after a t.stop or t.modify, but we cannot hold t.mu
@@ -1044,7 +1086,21 @@ func (t *timer) unlockAndRun(now int64) {
// and double-check that t.seq is still the seq value we saw above.
// If not, the timer has been updated and we should skip the send.
// We skip the send by reassigning f to a no-op function.
//
// The isSending field tells t.stop or t.modify that we have
// started to send the value. That lets them correctly return
// true meaning that no value was sent.
lock(&t.sendLock)
if t.period == 0 {
// We are committed to possibly sending a value
// based on seq, so no need to keep telling
// stop/modify that we are sending.
if t.isSending.Add(-1) < 0 {
throw("mismatched isSending updates")
}
}
if t.seq != seq {
f = func(any, uintptr, int64) {}
}

View File

@@ -7,6 +7,7 @@
package syscall
import (
errpkg "errors"
"internal/itoa"
"runtime"
"unsafe"
@@ -328,6 +329,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
if clone3 != nil {
pid, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3), 0)
} else {
// N.B. Keep in sync with doCheckClonePidfd.
flags |= uintptr(SIGCHLD)
if runtime.GOARCH == "s390x" {
// On Linux/s390, the first two arguments of clone(2) are swapped.
@@ -743,3 +745,82 @@ func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
*sys.PidFD = -1
}
}
// checkClonePidfd verifies that clone(CLONE_PIDFD) works by actually doing a
// clone.
//
//go:linkname os_checkClonePidfd os.checkClonePidfd
func os_checkClonePidfd() error {
pidfd := int32(-1)
pid, errno := doCheckClonePidfd(&pidfd)
if errno != 0 {
return errno
}
if pidfd == -1 {
// Bad: CLONE_PIDFD failed to provide a pidfd. Reap the process
// before returning.
var err error
for {
var status WaitStatus
_, err = Wait4(int(pid), &status, 0, nil)
if err != EINTR {
break
}
}
if err != nil {
return err
}
return errpkg.New("clone(CLONE_PIDFD) failed to return pidfd")
}
// Good: CLONE_PIDFD provided a pidfd. Reap the process and close the
// pidfd.
defer Close(int(pidfd))
for {
const _P_PIDFD = 3
_, _, errno = Syscall6(SYS_WAITID, _P_PIDFD, uintptr(pidfd), 0, WEXITED, 0, 0)
if errno != EINTR {
break
}
}
if errno != 0 {
return errno
}
return nil
}
// doCheckClonePidfd implements the actual clone call of os_checkClonePidfd and
// child execution. This is a separate function so we can separate the child's
// and parent's stack frames if we're using vfork.
//
// This is go:noinline because the point is to keep the stack frames of this
// and os_checkClonePidfd separate.
//
//go:noinline
func doCheckClonePidfd(pidfd *int32) (pid uintptr, errno Errno) {
flags := uintptr(CLONE_VFORK|CLONE_VM|CLONE_PIDFD|SIGCHLD)
if runtime.GOARCH == "s390x" {
// On Linux/s390, the first two arguments of clone(2) are swapped.
pid, errno = rawVforkSyscall(SYS_CLONE, 0, flags, uintptr(unsafe.Pointer(pidfd)))
} else {
pid, errno = rawVforkSyscall(SYS_CLONE, flags, 0, uintptr(unsafe.Pointer(pidfd)))
}
if errno != 0 || pid != 0 {
// If we're in the parent, we must return immediately
// so we're not in the same stack frame as the child.
// This can at most use the return PC, which the child
// will not modify, and the results of
// rawVforkSyscall, which must have been written after
// the child was replaced.
return
}
for {
RawSyscall(SYS_EXIT_GROUP, 0, 0, 0)
}
}

View File

@@ -14,7 +14,6 @@ import (
"unsafe"
)
// ForkLock is not used on Windows.
var ForkLock sync.RWMutex
// EscapeArg rewrites command line argument s as prescribed
@@ -317,6 +316,18 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
}
}
info := _OSVERSIONINFOW{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
rtlGetVersion(&info)
isWin7 := info.majorVersion < 6 || (info.majorVersion == 6 && info.minorVersion <= 1)
// NT kernel handles are divisible by 4, with the bottom 3 bits left as
// a tag. The fully set tag correlates with the types of handles we're
// concerned about here. Except, the kernel will interpret some
// special handle values, like -1, -2, and so forth, so kernelbase.dll
// checks to see that those bottom three bits are checked, but that top
// bit is not checked.
isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
p, _ := GetCurrentProcess()
parentProcess := p
if sys.ParentProcess != 0 {
@@ -325,7 +336,15 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
fd := make([]Handle, len(attr.Files))
for i := range attr.Files {
if attr.Files[i] > 0 {
err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
destinationProcessHandle := parentProcess
// On Windows 7, console handles aren't real handles, and can only be duplicated
// into the current process, not a parent one, which amounts to the same thing.
if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
destinationProcessHandle = p
}
err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
if err != nil {
return 0, 0, err
}
@@ -356,6 +375,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
fd = append(fd, sys.AdditionalInheritedHandles...)
// On Windows 7, console handles aren't real handles, so don't pass them
// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
for i := range fd {
if isLegacyWin7ConsoleHandle(fd[i]) {
fd[i] = 0
}
}
// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
// to treat the entire list as empty, so remove NULL handles.
j := 0

View File

@@ -1169,3 +1169,13 @@ const (
)
const UNIX_PATH_MAX = 108 // defined in afunix.h
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
type _OSVERSIONINFOW struct {
osVersionInfoSize uint32
majorVersion uint32
minorVersion uint32
buildNumber uint32
platformId uint32
csdVersion [128]uint16
}

View File

@@ -43,6 +43,7 @@ var (
modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll"))
modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll"))
modnetapi32 = NewLazyDLL(sysdll.Add("netapi32.dll"))
modntdll = NewLazyDLL(sysdll.Add("ntdll.dll"))
modsecur32 = NewLazyDLL(sysdll.Add("secur32.dll"))
modshell32 = NewLazyDLL(sysdll.Add("shell32.dll"))
moduserenv = NewLazyDLL(sysdll.Add("userenv.dll"))
@@ -169,6 +170,7 @@ var (
procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
procRtlGetVersion = modntdll.NewProc("RtlGetVersion")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
@@ -1228,6 +1230,11 @@ func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err er
return
}
func rtlGetVersion(info *_OSVERSIONINFOW) {
Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0)
return
}
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
if r1&0xff == 0 {

View File

@@ -785,6 +785,119 @@ func TestAdjustTimers(t *testing.T) {
}
}
func TestStopResult(t *testing.T) {
testStopResetResult(t, true)
}
func TestResetResult(t *testing.T) {
testStopResetResult(t, false)
}
// Test that when racing between running a timer and stopping a timer Stop
// consistently indicates whether a value can be read from the channel.
// Issue #69312.
func testStopResetResult(t *testing.T, testStop bool) {
for _, name := range []string{"0", "1", "2"} {
t.Run("asynctimerchan="+name, func(t *testing.T) {
testStopResetResultGODEBUG(t, testStop, name)
})
}
}
func testStopResetResultGODEBUG(t *testing.T, testStop bool, godebug string) {
t.Setenv("GODEBUG", "asynctimerchan="+godebug)
stopOrReset := func(timer *Timer) bool {
if testStop {
return timer.Stop()
} else {
return timer.Reset(1 * Hour)
}
}
start := make(chan struct{})
var wg sync.WaitGroup
const N = 1000
wg.Add(N)
for range N {
go func() {
defer wg.Done()
<-start
for j := 0; j < 100; j++ {
timer1 := NewTimer(1 * Millisecond)
timer2 := NewTimer(1 * Millisecond)
select {
case <-timer1.C:
if !stopOrReset(timer2) {
// The test fails if this
// channel read times out.
<-timer2.C
}
case <-timer2.C:
if !stopOrReset(timer1) {
// The test fails if this
// channel read times out.
<-timer1.C
}
}
}
}()
}
close(start)
wg.Wait()
}
// Test having a large number of goroutines wake up a ticker simultaneously.
// This used to trigger a crash when run under x/tools/cmd/stress.
func TestMultiWakeupTicker(t *testing.T) {
if testing.Short() {
t.Skip("-short")
}
goroutines := runtime.GOMAXPROCS(0)
timer := NewTicker(Microsecond)
var wg sync.WaitGroup
wg.Add(goroutines)
for range goroutines {
go func() {
defer wg.Done()
for range 100000 {
select {
case <-timer.C:
case <-After(Millisecond):
}
}
}()
}
wg.Wait()
}
// Test having a large number of goroutines wake up a timer simultaneously.
// This used to trigger a crash when run under x/tools/cmd/stress.
func TestMultiWakeupTimer(t *testing.T) {
if testing.Short() {
t.Skip("-short")
}
goroutines := runtime.GOMAXPROCS(0)
timer := NewTimer(Nanosecond)
var wg sync.WaitGroup
wg.Add(goroutines)
for range goroutines {
go func() {
defer wg.Done()
for range 10000 {
select {
case <-timer.C:
default:
}
timer.Reset(Nanosecond)
}
}()
}
wg.Wait()
}
// Benchmark timer latency when the thread that creates the timer is busy with
// other work and the timers must be serviced by other threads.
// https://golang.org/issue/38860

View File

@@ -12,22 +12,29 @@ import (
"bytes"
"log"
"os/exec"
"runtime"
"strings"
)
func main() {
checkLinkOutput("", "-B argument must start with 0x")
// The cannot open file error indicates that the parsing of -B flag
// succeeded and it failed at a later step.
checkLinkOutput("0", "-B argument must start with 0x")
checkLinkOutput("0x", "usage")
checkLinkOutput("0x", "cannot open file nonexistent.o")
checkLinkOutput("0x0", "-B argument must have even number of digits")
checkLinkOutput("0x00", "usage")
checkLinkOutput("0x00", "cannot open file nonexistent.o")
checkLinkOutput("0xYZ", "-B argument contains invalid hex digit")
checkLinkOutput("0x"+strings.Repeat("00", 32), "usage")
checkLinkOutput("0x"+strings.Repeat("00", 33), "-B option too long (max 32 digits)")
maxLen := 32
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
maxLen = 16
}
checkLinkOutput("0x"+strings.Repeat("00", maxLen), "cannot open file nonexistent.o")
checkLinkOutput("0x"+strings.Repeat("00", maxLen+1), "-B option too long")
}
func checkLinkOutput(buildid string, message string) {
cmd := exec.Command("go", "tool", "link", "-B", buildid)
cmd := exec.Command("go", "tool", "link", "-B", buildid, "nonexistent.o")
out, err := cmd.CombinedOutput()
if err == nil {
log.Fatalf("expected cmd/link to fail")
@@ -39,6 +46,6 @@ func checkLinkOutput(buildid string, message string) {
}
if !strings.Contains(firstLine, message) {
log.Fatalf("cmd/link output did not include expected message %q: %s", message, firstLine)
log.Fatalf("%s: cmd/link output did not include expected message %q: %s", buildid, message, firstLine)
}
}

View File

@@ -0,0 +1,173 @@
// run
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"fmt"
"io"
"iter"
"math/rand"
"os"
"strings"
"unicode"
)
// WordReader is the struct that implements io.Reader
type WordReader struct {
scanner *bufio.Scanner
}
// NewWordReader creates a new WordReader from an io.Reader
func NewWordReader(r io.Reader) *WordReader {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
return &WordReader{
scanner: scanner,
}
}
// Read reads data from the input stream and returns a single lowercase word at a time
func (wr *WordReader) Read(p []byte) (n int, err error) {
if !wr.scanner.Scan() {
if err := wr.scanner.Err(); err != nil {
return 0, err
}
return 0, io.EOF
}
word := wr.scanner.Text()
cleanedWord := removeNonAlphabetic(word)
if len(cleanedWord) == 0 {
return wr.Read(p)
}
n = copy(p, []byte(cleanedWord))
return n, nil
}
// All returns an iterator allowing the caller to iterate over the WordReader using for/range.
func (wr *WordReader) All() iter.Seq[string] {
word := make([]byte, 1024)
return func(yield func(string) bool) {
var err error
var n int
for n, err = wr.Read(word); err == nil; n, err = wr.Read(word) {
if !yield(string(word[:n])) {
return
}
}
if err != io.EOF {
fmt.Fprintf(os.Stderr, "error reading word: %v\n", err)
}
}
}
// removeNonAlphabetic removes non-alphabetic characters from a word using strings.Map
func removeNonAlphabetic(word string) string {
return strings.Map(func(r rune) rune {
if unicode.IsLetter(r) {
return unicode.ToLower(r)
}
return -1
}, word)
}
// ProbabilisticSkipper determines if an item should be retained with probability 1/(1<<n)
type ProbabilisticSkipper struct {
n int
counter uint64
bitmask uint64
}
// NewProbabilisticSkipper initializes the ProbabilisticSkipper
func NewProbabilisticSkipper(n int) *ProbabilisticSkipper {
pr := &ProbabilisticSkipper{n: n}
pr.refreshCounter()
return pr
}
// check panics if pr.n is not the expected value
func (pr *ProbabilisticSkipper) check(n int) {
if pr.n != n {
panic(fmt.Sprintf("check: pr.n != n %d != %d", pr.n, n))
}
}
// refreshCounter refreshes the counter with a new random value
func (pr *ProbabilisticSkipper) refreshCounter() {
if pr.n == 0 {
pr.bitmask = ^uint64(0) // All bits set to 1
} else {
pr.bitmask = rand.Uint64()
for i := 0; i < pr.n-1; i++ {
pr.bitmask &= rand.Uint64()
}
}
pr.counter = 64
}
// ShouldSkip returns true with probability 1/(1<<n)
func (pr *ProbabilisticSkipper) ShouldSkip() bool {
remove := pr.bitmask&1 == 0
pr.bitmask >>= 1
pr.counter--
if pr.counter == 0 {
pr.refreshCounter()
}
return remove
}
// EstimateUniqueWordsIter estimates the number of unique words using a probabilistic counting method
func EstimateUniqueWordsIter(reader io.Reader, memorySize int) int {
wordReader := NewWordReader(reader)
words := make(map[string]struct{}, memorySize)
rounds := 0
roundRemover := NewProbabilisticSkipper(1)
wordSkipper := NewProbabilisticSkipper(rounds)
wordSkipper.check(rounds)
for word := range wordReader.All() {
wordSkipper.check(rounds)
if wordSkipper.ShouldSkip() {
delete(words, word)
} else {
words[word] = struct{}{}
if len(words) >= memorySize {
rounds++
wordSkipper = NewProbabilisticSkipper(rounds)
for w := range words {
if roundRemover.ShouldSkip() {
delete(words, w)
}
}
}
}
wordSkipper.check(rounds)
}
if len(words) == 0 {
return 0
}
invProbability := 1 << rounds
estimatedUniqueWords := len(words) * invProbability
return estimatedUniqueWords
}
func main() {
input := "Hello, world! This is a test. Hello, world, hello!"
expectedUniqueWords := 6 // "hello", "world", "this", "is", "a", "test" (but "hello" and "world" are repeated)
memorySize := 6
reader := strings.NewReader(input)
estimatedUniqueWords := EstimateUniqueWordsIter(reader, memorySize)
if estimatedUniqueWords != expectedUniqueWords {
// ...
}
}

View File

@@ -0,0 +1,133 @@
// run
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
func main() {
err := run()
if err != nil {
panic(err)
}
}
func run() error {
methods := "AB"
type node struct {
tag string
choices []string
}
all := []node{
{"000", permutations(methods)},
}
next := 1
for len(all) > 0 {
cur := all[0]
k := copy(all, all[1:])
all = all[:k]
if len(cur.choices) == 1 {
continue
}
var bestM map[byte][]string
bMax := len(cur.choices) + 1
bMin := -1
for sel := range selections(methods) {
m := make(map[byte][]string)
for _, order := range cur.choices {
x := findFirstMatch(order, sel)
m[x] = append(m[x], order)
}
min := len(cur.choices) + 1
max := -1
for _, v := range m {
if len(v) < min {
min = len(v)
}
if len(v) > max {
max = len(v)
}
}
if max < bMax || (max == bMax && min > bMin) {
bestM = m
bMin = min
bMax = max
}
}
if bMax == len(cur.choices) {
continue
}
cc := Keys(bestM)
for c := range cc {
choices := bestM[c]
next++
switch c {
case 'A':
case 'B':
default:
panic("unexpected selector type " + string(c))
}
all = append(all, node{"", choices})
}
}
return nil
}
func permutations(s string) []string {
if len(s) <= 1 {
return []string{s}
}
var result []string
for i, char := range s {
rest := s[:i] + s[i+1:]
for _, perm := range permutations(rest) {
result = append(result, string(char)+perm)
}
}
return result
}
type Seq[V any] func(yield func(V) bool)
func selections(s string) Seq[string] {
return func(yield func(string) bool) {
for bits := 1; bits < 1<<len(s); bits++ {
var choice string
for j, char := range s {
if bits&(1<<j) != 0 {
choice += string(char)
}
}
if !yield(choice) {
break
}
}
}
}
func findFirstMatch(order, sel string) byte {
for _, c := range order {
return byte(c)
}
return 0
}
func Keys[Map ~map[K]V, K comparable, V any](m Map) Seq[K] {
return func(yield func(K) bool) {
for k := range m {
if !yield(k) {
return
}
}
}
}