diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2025-08-08 12:44:42 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-08-26 19:39:36 -0700 |
| commit | fa18c547cd891e526d2e01cfbc17961de45c31a5 (patch) | |
| tree | 074a6e85fa9d4cd2a57423f4333637c772b11d41 /src/syscall/exec_windows.go | |
| parent | bfd130db02336a174dab781185be369f089373ba (diff) | |
| download | go-fa18c547cd891e526d2e01cfbc17961de45c31a5.tar.xz | |
syscall: sort Windows env block in StartProcess
Fixes #29530
Change-Id: Ia28c78274b9288bfa5de9ccb142a452d291a5b66
Reviewed-on: https://go-review.googlesource.com/c/go/+/694435
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Auto-Submit: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/syscall/exec_windows.go')
| -rw-r--r-- | src/syscall/exec_windows.go | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go index 3ba2fbe0ec..96089fcd93 100644 --- a/src/syscall/exec_windows.go +++ b/src/syscall/exec_windows.go @@ -9,6 +9,7 @@ package syscall import ( "internal/bytealg" "runtime" + "slices" "sync" "unicode/utf16" "unsafe" @@ -113,6 +114,49 @@ func makeCmdLine(args []string) string { return string(b) } +func envSorted(envv []string) []string { + if len(envv) < 2 { + return envv + } + + lowerKeyCache := map[string][]byte{} // lowercased keys to avoid recomputing them in sort + lowerKey := func(kv string) []byte { + eq := bytealg.IndexByteString(kv, '=') + if eq < 0 { + return nil + } + k := kv[:eq] + v, ok := lowerKeyCache[k] + if !ok { + v = []byte(k) + for i, b := range v { + // We only normalize ASCII for now. + // In practice, all environment variables are ASCII, and the + // syscall package can't import "unicode" anyway. + // Also, per https://nullprogram.com/blog/2023/08/23/ the + // sorting of environment variables doesn't really matter. + // TODO(bradfitz): use RtlCompareUnicodeString instead, + // per that blog post? For now, ASCII is good enough. + if 'a' <= b && b <= 'z' { + v[i] -= 'a' - 'A' + } + } + lowerKeyCache[k] = v + } + return v + } + + cmpEnv := func(a, b string) int { + return bytealg.Compare(lowerKey(a), lowerKey(b)) + } + + if !slices.IsSortedFunc(envv, cmpEnv) { + envv = slices.Clone(envv) + slices.SortFunc(envv, cmpEnv) + } + return envv +} + // createEnvBlock converts an array of environment strings into // the representation required by CreateProcess: a sequence of NUL // terminated strings followed by a nil. @@ -122,6 +166,12 @@ func createEnvBlock(envv []string) ([]uint16, error) { if len(envv) == 0 { return utf16.Encode([]rune("\x00\x00")), nil } + + // https://learn.microsoft.com/en-us/windows/win32/procthread/changing-environment-variables + // says that: "All strings in the environment block must be sorted + // alphabetically by name." + envv = envSorted(envv) + var length int for _, s := range envv { if bytealg.IndexByteString(s, 0) != -1 { |
