From a4c03d279b2216179063e3e9b1b46caf7b1ce6d1 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 22 Feb 2022 11:07:05 -0800 Subject: [PATCH 1/3] src/os: make Environ() return a copy of the environment Fixes #2646 --- src/os/env.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/os/env.go b/src/os/env.go index 330297b36a..9f574b536d 100644 --- a/src/os/env.go +++ b/src/os/env.go @@ -8,6 +8,7 @@ package os import ( "internal/testlog" + "strings" "syscall" ) @@ -137,5 +138,13 @@ func Clearenv() { // Environ returns a copy of strings representing the environment, // in the form "key=value". func Environ() []string { - return syscall.Environ() + orig := syscall.Environ() + single := strings.Join(orig, "") + env := make([]string, len(orig)) + for i, v := range orig { + s := single[:len(v)] + env[i] = s + single = single[len(v):] + } + return env } From 438386918d408cf02b892e9f778bdceeffa83c98 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 22 Feb 2022 12:17:50 -0800 Subject: [PATCH 2/3] src/os: add back TestClearenv Removed as flaky in #2603 --- src/os/env_test.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/os/env_test.go b/src/os/env_test.go index 40721b63ec..8489a976c5 100644 --- a/src/os/env_test.go +++ b/src/os/env_test.go @@ -122,7 +122,33 @@ func TestUnsetenv(t *testing.T) { } } -// TODO: add back TestClearenv() and fix the errors it finds +func TestClearenv(t *testing.T) { + const testKey = "GO_TEST_CLEARENV" + const testValue = "1" + + // reset env + defer func(origEnv []string) { + for _, pair := range origEnv { + // Environment variables on Windows can begin with = + // https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + i := strings.Index(pair[1:], "=") + 1 + if err := Setenv(pair[:i], pair[i+1:]); err != nil { + t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err) + } + } + }(Environ()) + + if err := Setenv(testKey, testValue); err != nil { + t.Fatalf("Setenv(%q, %q) failed: %v", testKey, testValue, err) + } + if _, ok := LookupEnv(testKey); !ok { + t.Errorf("Setenv(%q, %q) didn't set $%s", testKey, testValue, testKey) + } + Clearenv() + if val, ok := LookupEnv(testKey); ok { + t.Errorf("Clearenv() didn't clear $%s, remained with value %q", testKey, val) + } +} func TestLookupEnv(t *testing.T) { const smallpox = "SMALLPOX" // No one has smallpox. From 72ca05f5b63e2d07118615b3cad560989713de65 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 23 Feb 2022 10:11:42 -0800 Subject: [PATCH 3/3] src/os,src/syscall: move env copy code to syscall.Environ() --- src/os/env.go | 11 +---------- src/syscall/syscall_libc.go | 35 ++++++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/os/env.go b/src/os/env.go index 9f574b536d..330297b36a 100644 --- a/src/os/env.go +++ b/src/os/env.go @@ -8,7 +8,6 @@ package os import ( "internal/testlog" - "strings" "syscall" ) @@ -138,13 +137,5 @@ func Clearenv() { // Environ returns a copy of strings representing the environment, // in the form "key=value". func Environ() []string { - orig := syscall.Environ() - single := strings.Join(orig, "") - env := make([]string, len(orig)) - for i, v := range orig { - s := single[:len(v)] - env[i] = s - single = single[len(v):] - } - return env + return syscall.Environ() } diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index da46b9ffb4..0ef64984a2 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -235,11 +235,24 @@ func Mprotect(b []byte, prot int) (err error) { } func Environ() []string { - environ := libc_environ - var envs []string - for *environ != nil { - // Convert the C string to a Go string. - length := libc_strlen(*environ) + // calculate total memory required + var length uintptr + var vars int + for environ := libc_environ; *environ != nil; { + length += libc_strlen(*environ) + vars++ + environ = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(environ)) + unsafe.Sizeof(environ))) + } + + // allocate our backing slice for the strings + b := make([]byte, length) + // and the slice we're going to return + envs := make([]string, 0, vars) + + // loop over the environment again, this time copying over the data to the backing slice + for environ := libc_environ; *environ != nil; { + length = libc_strlen(*environ) + // construct a Go string pointing at the libc-allocated environment variable data var envVar string rawEnvVar := (*struct { ptr unsafe.Pointer @@ -247,8 +260,16 @@ func Environ() []string { })(unsafe.Pointer(&envVar)) rawEnvVar.ptr = *environ rawEnvVar.length = length - envs = append(envs, envVar) - // This is the Go equivalent of "environ++" in C. + // pull off the number of bytes we need for this environment variable + var bs []byte + bs, b = b[:length], b[length:] + // copy over the bytes to the Go heap + copy(bs, envVar) + // convert trimmed slice to string + s := *(*string)(unsafe.Pointer(&bs)) + // add s to our list of environment variables + envs = append(envs, s) + // environ++ environ = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(environ)) + unsafe.Sizeof(environ))) } return envs