os, syscall: add Unsetenv

Also address a TODO, making Clearenv pass through to cgo.

Based largely on Minux's earlier https://golang.org/cl/82040044

Fixes #6423

LGTM=iant, alex.brainman, r, rsc
R=rsc, iant, r, alex.brainman
CC=golang-codereviews
https://golang.org/cl/148370043
diff --git a/src/syscall/env_unix.go b/src/syscall/env_unix.go
index 01ac38a..b5ded9c 100644
--- a/src/syscall/env_unix.go
+++ b/src/syscall/env_unix.go
@@ -20,16 +20,18 @@
 	// env maps from an environment variable to its first occurrence in envs.
 	env map[string]int
 
-	// envs is provided by the runtime. elements are expected to be
-	// of the form "key=value".
+	// envs is provided by the runtime. elements are expected to
+	// be of the form "key=value". An empty string means deleted
+	// (or a duplicate to be ignored).
 	envs []string = runtime_envs()
 )
 
 func runtime_envs() []string // in package runtime
 
-// setenv_c is provided by the runtime, but is a no-op if cgo isn't
-// loaded.
+// setenv_c and unsetenv_c are provided by the runtime but are no-ops
+// if cgo isn't loaded.
 func setenv_c(k, v string)
+func unsetenv_c(k string)
 
 func copyenv() {
 	env = make(map[string]int)
@@ -38,7 +40,13 @@
 			if s[j] == '=' {
 				key := s[:j]
 				if _, ok := env[key]; !ok {
-					env[key] = i
+					env[key] = i // first mention of key
+				} else {
+					// Clear duplicate keys. This permits Unsetenv to
+					// safely delete only the first item without
+					// worrying about unshadowing a later one,
+					// which might be a security problem.
+					envs[i] = ""
 				}
 				break
 			}
@@ -46,6 +54,20 @@
 	}
 }
 
+func Unsetenv(key string) error {
+	envOnce.Do(copyenv)
+
+	envLock.Lock()
+	defer envLock.Unlock()
+
+	if i, ok := env[key]; ok {
+		envs[i] = ""
+		delete(env, key)
+	}
+	unsetenv_c(key)
+	return nil
+}
+
 func Getenv(key string) (value string, found bool) {
 	envOnce.Do(copyenv)
 	if len(key) == 0 {
@@ -106,16 +128,22 @@
 	envLock.Lock()
 	defer envLock.Unlock()
 
+	for k := range env {
+		unsetenv_c(k)
+	}
 	env = make(map[string]int)
 	envs = []string{}
-	// TODO(bradfitz): pass through to C
 }
 
 func Environ() []string {
 	envOnce.Do(copyenv)
 	envLock.RLock()
 	defer envLock.RUnlock()
-	a := make([]string, len(envs))
-	copy(a, envs)
+	a := make([]string, 0, len(envs))
+	for _, env := range envs {
+		if env != "" {
+			a = append(a, env)
+		}
+	}
 	return a
 }