aboutsummaryrefslogtreecommitdiff
path: root/src/syscall/exec_windows.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2025-08-08 12:44:42 -0700
committerGopher Robot <gobot@golang.org>2025-08-26 19:39:36 -0700
commitfa18c547cd891e526d2e01cfbc17961de45c31a5 (patch)
tree074a6e85fa9d4cd2a57423f4333637c772b11d41 /src/syscall/exec_windows.go
parentbfd130db02336a174dab781185be369f089373ba (diff)
downloadgo-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.go50
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 {