aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland Shoemaker <roland@golang.org>2021-01-15 12:14:06 -0800
committerRoland Shoemaker <bracewell@google.com>2021-01-16 00:29:16 +0000
commit07e3195293ec510171d7d43ec8ac2bcb9cf00df4 (patch)
tree3a36f93eab5c8dad04fb6f0963199b669fe5986c
parentb21052258ef27a7b267df21411dd4e8ddbdee5fe (diff)
downloadgo-07e3195293ec510171d7d43ec8ac2bcb9cf00df4.tar.gz
go-07e3195293ec510171d7d43ec8ac2bcb9cf00df4.zip
[release-branch.go1.15-security] all: introduce and use internal/execabs
Introduces a wrapper around os/exec, internal/execabs, for use in all commands. This wrapper prevents exec.LookPath and exec.Command from running executables in the current directory. All imports of os/exec in non-test files in cmd/ are replaced with imports of internal/execabs. This issue was reported by RyotaK. Fixes CVE-2021-3115 Change-Id: I0423451a6e27ec1e1d6f3fe929ab1ef69145c08f Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/955304 Reviewed-by: Russ Cox <rsc@google.com> Reviewed-by: Katie Hockman <katiehockman@google.com> (cherry picked from commit 44f09a6990ccf4db601cbf8208c89ac4e888f884) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/955308
-rw-r--r--src/cmd/api/goapi.go2
-rw-r--r--src/cmd/api/run.go2
-rw-r--r--src/cmd/cgo/out.go2
-rw-r--r--src/cmd/cgo/util.go2
-rw-r--r--src/cmd/compile/internal/ssa/html.go2
-rw-r--r--src/cmd/cover/func.go2
-rw-r--r--src/cmd/cover/testdata/toolexec.go2
-rw-r--r--src/cmd/dist/buildtool.go4
-rw-r--r--src/cmd/doc/dirs.go2
-rw-r--r--src/cmd/fix/typecheck.go2
-rw-r--r--src/cmd/go/internal/base/base.go2
-rw-r--r--src/cmd/go/internal/bug/bug.go2
-rw-r--r--src/cmd/go/internal/generate/generate.go2
-rw-r--r--src/cmd/go/internal/modfetch/codehost/codehost.go2
-rw-r--r--src/cmd/go/internal/modfetch/codehost/git.go2
-rw-r--r--src/cmd/go/internal/test/genflags.go2
-rw-r--r--src/cmd/go/internal/test/test.go2
-rw-r--r--src/cmd/go/internal/tool/tool.go2
-rw-r--r--src/cmd/go/internal/vet/vetflag.go2
-rw-r--r--src/cmd/go/internal/work/build.go2
-rw-r--r--src/cmd/go/internal/work/buildid.go2
-rw-r--r--src/cmd/go/internal/work/exec.go2
-rw-r--r--src/cmd/go/internal/work/gccgo.go2
-rw-r--r--src/cmd/go/testdata/addmod.go2
-rw-r--r--src/cmd/internal/browser/browser.go2
-rw-r--r--src/cmd/internal/diff/diff.go2
-rw-r--r--src/cmd/internal/dwarf/dwarf.go2
-rw-r--r--src/cmd/link/internal/ld/execarchive.go2
-rw-r--r--src/cmd/link/internal/ld/lib.go2
-rw-r--r--src/cmd/test2json/main.go2
-rw-r--r--src/cmd/trace/pprof.go2
-rw-r--r--src/go/build/build.go2
-rw-r--r--src/go/build/deps_test.go8
-rw-r--r--src/go/internal/gccgoimporter/gccgoinstallation.go2
-rw-r--r--src/go/internal/srcimporter/srcimporter.go2
-rw-r--r--src/internal/execabs/execabs.go70
-rw-r--r--src/internal/execabs/execabs_test.go107
-rw-r--r--src/internal/goroot/gc.go2
38 files changed, 221 insertions, 36 deletions
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 01b17b88392..6cddfc100bb 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -16,11 +16,11 @@ import (
"go/parser"
"go/token"
"go/types"
+ exec "internal/execabs"
"io"
"io/ioutil"
"log"
"os"
- "os/exec"
"path/filepath"
"regexp"
"runtime"
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
index a36f1179c16..ecb1d0f81aa 100644
--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -10,9 +10,9 @@ package main
import (
"fmt"
+ exec "internal/execabs"
"log"
"os"
- "os/exec"
"path/filepath"
"runtime"
"strings"
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index dcd69edb44b..b9043efbf78 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -13,11 +13,11 @@ import (
"go/ast"
"go/printer"
"go/token"
+ exec "internal/execabs"
"internal/xcoff"
"io"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"regexp"
"sort"
diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go
index 779f7be2259..00d931b98a0 100644
--- a/src/cmd/cgo/util.go
+++ b/src/cmd/cgo/util.go
@@ -8,9 +8,9 @@ import (
"bytes"
"fmt"
"go/token"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
)
// run runs the command argv, feeding in stdin on standard input.
diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go
index ba37a804123..99a41579375 100644
--- a/src/cmd/compile/internal/ssa/html.go
+++ b/src/cmd/compile/internal/ssa/html.go
@@ -9,9 +9,9 @@ import (
"cmd/internal/src"
"fmt"
"html"
+ exec "internal/execabs"
"io"
"os"
- "os/exec"
"path/filepath"
"strconv"
"strings"
diff --git a/src/cmd/cover/func.go b/src/cmd/cover/func.go
index 988c4caebff..ce7c771ac96 100644
--- a/src/cmd/cover/func.go
+++ b/src/cmd/cover/func.go
@@ -15,9 +15,9 @@ import (
"go/ast"
"go/parser"
"go/token"
+ exec "internal/execabs"
"io"
"os"
- "os/exec"
"path"
"path/filepath"
"runtime"
diff --git a/src/cmd/cover/testdata/toolexec.go b/src/cmd/cover/testdata/toolexec.go
index 1769efedbeb..386de79038a 100644
--- a/src/cmd/cover/testdata/toolexec.go
+++ b/src/cmd/cover/testdata/toolexec.go
@@ -16,7 +16,7 @@ package main
import (
"os"
- "os/exec"
+ exec "internal/execabs"
"strings"
)
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 9502dac4eb2..710f4bde6f0 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -302,8 +302,10 @@ func bootstrapFixImports(srcFile string) string {
continue
}
if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
- inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
+ inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"") || strings.HasPrefix(line, "\texec \"")) {
line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1)
+ // During bootstrap, must use plain os/exec.
+ line = strings.Replace(line, `exec "internal/execabs"`, `"os/exec"`, -1)
for _, dir := range bootstrapDirs {
if strings.HasPrefix(dir, "cmd/") {
continue
diff --git a/src/cmd/doc/dirs.go b/src/cmd/doc/dirs.go
index 38cbe7fa021..661624cfe4c 100644
--- a/src/cmd/doc/dirs.go
+++ b/src/cmd/doc/dirs.go
@@ -7,9 +7,9 @@ package main
import (
"bytes"
"fmt"
+ exec "internal/execabs"
"log"
"os"
- "os/exec"
"path/filepath"
"regexp"
"strings"
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
index 66e0cdcec05..8390e4446c0 100644
--- a/src/cmd/fix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -9,9 +9,9 @@ import (
"go/ast"
"go/parser"
"go/token"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"reflect"
"runtime"
diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go
index ab2f1bb4e2c..b63303afd18 100644
--- a/src/cmd/go/internal/base/base.go
+++ b/src/cmd/go/internal/base/base.go
@@ -9,9 +9,9 @@ package base
import (
"flag"
"fmt"
+ exec "internal/execabs"
"log"
"os"
- "os/exec"
"strings"
"sync"
diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go
index fe71281ef05..9434bc2b1c1 100644
--- a/src/cmd/go/internal/bug/bug.go
+++ b/src/cmd/go/internal/bug/bug.go
@@ -8,11 +8,11 @@ package bug
import (
"bytes"
"fmt"
+ exec "internal/execabs"
"io"
"io/ioutil"
urlpkg "net/url"
"os"
- "os/exec"
"path/filepath"
"regexp"
"runtime"
diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go
index 093b19817b5..3abb4ca537d 100644
--- a/src/cmd/go/internal/generate/generate.go
+++ b/src/cmd/go/internal/generate/generate.go
@@ -11,11 +11,11 @@ import (
"fmt"
"go/parser"
"go/token"
+ exec "internal/execabs"
"io"
"io/ioutil"
"log"
"os"
- "os/exec"
"path/filepath"
"regexp"
"strconv"
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index d85eddf767b..058052e3290 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -10,10 +10,10 @@ import (
"bytes"
"crypto/sha256"
"fmt"
+ exec "internal/execabs"
"io"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"strings"
"sync"
diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go
index 31921324a7d..06b2fa4fb56 100644
--- a/src/cmd/go/internal/modfetch/codehost/git.go
+++ b/src/cmd/go/internal/modfetch/codehost/git.go
@@ -8,11 +8,11 @@ import (
"bytes"
"errors"
"fmt"
+ exec "internal/execabs"
"io"
"io/ioutil"
"net/url"
"os"
- "os/exec"
"path/filepath"
"sort"
"strconv"
diff --git a/src/cmd/go/internal/test/genflags.go b/src/cmd/go/internal/test/genflags.go
index 512fa1671ef..1331287fa36 100644
--- a/src/cmd/go/internal/test/genflags.go
+++ b/src/cmd/go/internal/test/genflags.go
@@ -9,9 +9,9 @@ package main
import (
"bytes"
"flag"
+ exec "internal/execabs"
"log"
"os"
- "os/exec"
"strings"
"testing"
"text/template"
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 77bfc11fe9a..8cb902748ef 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -10,10 +10,10 @@ import (
"errors"
"fmt"
"go/build"
+ exec "internal/execabs"
"io"
"io/ioutil"
"os"
- "os/exec"
"path"
"path/filepath"
"regexp"
diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go
index 930eecb63f1..f06c9038a06 100644
--- a/src/cmd/go/internal/tool/tool.go
+++ b/src/cmd/go/internal/tool/tool.go
@@ -7,8 +7,8 @@ package tool
import (
"fmt"
+ exec "internal/execabs"
"os"
- "os/exec"
"sort"
"strings"
diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go
index ef995ef8353..5bf5cf44467 100644
--- a/src/cmd/go/internal/vet/vetflag.go
+++ b/src/cmd/go/internal/vet/vetflag.go
@@ -10,9 +10,9 @@ import (
"errors"
"flag"
"fmt"
+ exec "internal/execabs"
"log"
"os"
- "os/exec"
"path/filepath"
"strings"
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 7146c9ce00e..a2cbea870dc 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -8,8 +8,8 @@ import (
"errors"
"fmt"
"go/build"
+ exec "internal/execabs"
"os"
- "os/exec"
"path/filepath"
"runtime"
"strings"
diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go
index 6613b6fe3f2..2a79d9d7677 100644
--- a/src/cmd/go/internal/work/buildid.go
+++ b/src/cmd/go/internal/work/buildid.go
@@ -7,9 +7,9 @@ package work
import (
"bytes"
"fmt"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"strings"
"cmd/go/internal/base"
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 3c397345901..072162a26cc 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -11,13 +11,13 @@ import (
"encoding/json"
"errors"
"fmt"
+ exec "internal/execabs"
"internal/lazyregexp"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
- "os/exec"
"path/filepath"
"regexp"
"runtime"
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index 4c1f36dbd6a..2f5d5d62837 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -6,9 +6,9 @@ package work
import (
"fmt"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"strings"
diff --git a/src/cmd/go/testdata/addmod.go b/src/cmd/go/testdata/addmod.go
index d9c3aab9c49..9c74cf500b6 100644
--- a/src/cmd/go/testdata/addmod.go
+++ b/src/cmd/go/testdata/addmod.go
@@ -25,7 +25,7 @@ import (
"io/ioutil"
"log"
"os"
- "os/exec"
+ exec "internal/execabs"
"path/filepath"
"strings"
diff --git a/src/cmd/internal/browser/browser.go b/src/cmd/internal/browser/browser.go
index 6867c85d232..577d31789f1 100644
--- a/src/cmd/internal/browser/browser.go
+++ b/src/cmd/internal/browser/browser.go
@@ -6,8 +6,8 @@
package browser
import (
+ exec "internal/execabs"
"os"
- "os/exec"
"runtime"
"time"
)
diff --git a/src/cmd/internal/diff/diff.go b/src/cmd/internal/diff/diff.go
index e9d2c237800..c0ca2f31063 100644
--- a/src/cmd/internal/diff/diff.go
+++ b/src/cmd/internal/diff/diff.go
@@ -7,9 +7,9 @@
package diff
import (
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"runtime"
)
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index a17b574cdd8..1269ebc2630 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -12,7 +12,7 @@ import (
"cmd/internal/objabi"
"errors"
"fmt"
- "os/exec"
+ exec "internal/execabs"
"sort"
"strconv"
"strings"
diff --git a/src/cmd/link/internal/ld/execarchive.go b/src/cmd/link/internal/ld/execarchive.go
index fe5cc408657..4687c624de4 100644
--- a/src/cmd/link/internal/ld/execarchive.go
+++ b/src/cmd/link/internal/ld/execarchive.go
@@ -7,8 +7,8 @@
package ld
import (
+ exec "internal/execabs"
"os"
- "os/exec"
"path/filepath"
"syscall"
)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 0366bc7a6f4..fc897599970 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -50,11 +50,11 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
+ exec "internal/execabs"
"io"
"io/ioutil"
"log"
"os"
- "os/exec"
"path/filepath"
"runtime"
"sort"
diff --git a/src/cmd/test2json/main.go b/src/cmd/test2json/main.go
index 57a874193e3..14626974989 100644
--- a/src/cmd/test2json/main.go
+++ b/src/cmd/test2json/main.go
@@ -82,9 +82,9 @@ package main
import (
"flag"
"fmt"
+ exec "internal/execabs"
"io"
"os"
- "os/exec"
"cmd/internal/test2json"
)
diff --git a/src/cmd/trace/pprof.go b/src/cmd/trace/pprof.go
index a31d71b013c..e6ea1a4ab61 100644
--- a/src/cmd/trace/pprof.go
+++ b/src/cmd/trace/pprof.go
@@ -9,12 +9,12 @@ package main
import (
"bufio"
"fmt"
+ exec "internal/execabs"
"internal/trace"
"io"
"io/ioutil"
"net/http"
"os"
- "os/exec"
"path/filepath"
"runtime"
"sort"
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 4a5da308a0c..9050d070f8a 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -12,12 +12,12 @@ import (
"go/doc"
"go/parser"
"go/token"
+ exec "internal/execabs"
"internal/goroot"
"internal/goversion"
"io"
"io/ioutil"
"os"
- "os/exec"
pathpkg "path"
"path/filepath"
"runtime"
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index fa8ecf10f42..875acebf9c5 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -161,7 +161,7 @@ var depsRules = `
reflect !< OS;
OS
- < golang.org/x/sys/cpu, internal/goroot;
+ < golang.org/x/sys/cpu;
# FMT is OS (which includes string routines) plus reflect and fmt.
# It does not include package log, which should be avoided in core packages.
@@ -177,6 +177,12 @@ var depsRules = `
log !< FMT;
+ OS, FMT
+ < internal/execabs;
+
+ OS, internal/execabs
+ < internal/goroot;
+
# Misc packages needing only FMT.
FMT
< flag,
diff --git a/src/go/internal/gccgoimporter/gccgoinstallation.go b/src/go/internal/gccgoimporter/gccgoinstallation.go
index 8fc7ce32321..e90a3cc0b0a 100644
--- a/src/go/internal/gccgoimporter/gccgoinstallation.go
+++ b/src/go/internal/gccgoimporter/gccgoinstallation.go
@@ -7,8 +7,8 @@ package gccgoimporter
import (
"bufio"
"go/types"
+ exec "internal/execabs"
"os"
- "os/exec"
"path/filepath"
"strings"
)
diff --git a/src/go/internal/srcimporter/srcimporter.go b/src/go/internal/srcimporter/srcimporter.go
index 90bb3a9bc11..37d58833542 100644
--- a/src/go/internal/srcimporter/srcimporter.go
+++ b/src/go/internal/srcimporter/srcimporter.go
@@ -13,10 +13,10 @@ import (
"go/parser"
"go/token"
"go/types"
+ exec "internal/execabs"
"io"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"strings"
"sync"
diff --git a/src/internal/execabs/execabs.go b/src/internal/execabs/execabs.go
new file mode 100644
index 00000000000..547c3a50c80
--- /dev/null
+++ b/src/internal/execabs/execabs.go
@@ -0,0 +1,70 @@
+// Copyright 2020 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 execabs is a drop-in replacement for os/exec
+// that requires PATH lookups to find absolute paths.
+// That is, execabs.Command("cmd") runs the same PATH lookup
+// as exec.Command("cmd"), but if the result is a path
+// which is relative, the Run and Start methods will report
+// an error instead of running the executable.
+package execabs
+
+import (
+ "context"
+ "fmt"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "unsafe"
+)
+
+var ErrNotFound = exec.ErrNotFound
+
+type (
+ Cmd = exec.Cmd
+ Error = exec.Error
+ ExitError = exec.ExitError
+)
+
+func relError(file, path string) error {
+ return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path)
+}
+
+func LookPath(file string) (string, error) {
+ path, err := exec.LookPath(file)
+ if err != nil {
+ return "", err
+ }
+ if filepath.Base(file) == file && !filepath.IsAbs(path) {
+ return "", relError(file, path)
+ }
+ return path, nil
+}
+
+func fixCmd(name string, cmd *exec.Cmd) {
+ if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
+ // exec.Command was called with a bare binary name and
+ // exec.LookPath returned a path which is not absolute.
+ // Set cmd.lookPathErr and clear cmd.Path so that it
+ // cannot be run.
+ lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
+ if *lookPathErr == nil {
+ *lookPathErr = relError(name, cmd.Path)
+ }
+ cmd.Path = ""
+ }
+}
+
+func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
+ cmd := exec.CommandContext(ctx, name, arg...)
+ fixCmd(name, cmd)
+ return cmd
+
+}
+
+func Command(name string, arg ...string) *exec.Cmd {
+ cmd := exec.Command(name, arg...)
+ fixCmd(name, cmd)
+ return cmd
+}
diff --git a/src/internal/execabs/execabs_test.go b/src/internal/execabs/execabs_test.go
new file mode 100644
index 00000000000..a0b88dd2a0c
--- /dev/null
+++ b/src/internal/execabs/execabs_test.go
@@ -0,0 +1,107 @@
+// Copyright 2020 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 execabs
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+func TestFixCmd(t *testing.T) {
+ cmd := &exec.Cmd{Path: "hello"}
+ fixCmd("hello", cmd)
+ if cmd.Path != "" {
+ t.Errorf("fixCmd didn't clear cmd.Path")
+ }
+ expectedErr := fmt.Sprintf("hello resolves to executable in current directory (.%chello)", filepath.Separator)
+ if err := cmd.Run(); err == nil {
+ t.Fatal("Command.Run didn't fail")
+ } else if err.Error() != expectedErr {
+ t.Fatalf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+ }
+}
+
+func TestCommand(t *testing.T) {
+ for _, cmd := range []func(string) *Cmd{
+ func(s string) *Cmd { return Command(s) },
+ func(s string) *Cmd { return CommandContext(context.Background(), s) },
+ } {
+ tmpDir, err := ioutil.TempDir("", "execabs-test")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir failed: %s", err)
+ }
+ defer os.RemoveAll(tmpDir)
+ executable := "execabs-test"
+ if runtime.GOOS == "windows" {
+ executable += ".exe"
+ }
+ if err = ioutil.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+ t.Fatalf("ioutil.WriteFile failed: %s", err)
+ }
+ cwd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("os.Getwd failed: %s", err)
+ }
+ defer os.Chdir(cwd)
+ if err = os.Chdir(tmpDir); err != nil {
+ t.Fatalf("os.Chdir failed: %s", err)
+ }
+ if runtime.GOOS != "windows" {
+ // add "." to PATH so that exec.LookPath looks in the current directory on
+ // non-windows platforms as well
+ origPath := os.Getenv("PATH")
+ defer os.Setenv("PATH", origPath)
+ os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+ }
+ expectedErr := fmt.Sprintf("execabs-test resolves to executable in current directory (.%c%s)", filepath.Separator, executable)
+ if err = cmd("execabs-test").Run(); err == nil {
+ t.Fatalf("Command.Run didn't fail when exec.LookPath returned a relative path")
+ } else if err.Error() != expectedErr {
+ t.Errorf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+ }
+ }
+}
+
+func TestLookPath(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "execabs-test")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir failed: %s", err)
+ }
+ defer os.RemoveAll(tmpDir)
+ executable := "execabs-test"
+ if runtime.GOOS == "windows" {
+ executable += ".exe"
+ }
+ if err = ioutil.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+ t.Fatalf("ioutil.WriteFile failed: %s", err)
+ }
+ cwd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("os.Getwd failed: %s", err)
+ }
+ defer os.Chdir(cwd)
+ if err = os.Chdir(tmpDir); err != nil {
+ t.Fatalf("os.Chdir failed: %s", err)
+ }
+ if runtime.GOOS != "windows" {
+ // add "." to PATH so that exec.LookPath looks in the current directory on
+ // non-windows platforms as well
+ origPath := os.Getenv("PATH")
+ defer os.Setenv("PATH", origPath)
+ os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+ }
+ expectedErr := fmt.Sprintf("execabs-test resolves to executable in current directory (.%c%s)", filepath.Separator, executable)
+ if _, err := LookPath("execabs-test"); err == nil {
+ t.Fatalf("LookPath didn't fail when finding a non-relative path")
+ } else if err.Error() != expectedErr {
+ t.Errorf("LookPath returned unexpected error: want %q, got %q", expectedErr, err.Error())
+ }
+}
diff --git a/src/internal/goroot/gc.go b/src/internal/goroot/gc.go
index 0f541d734b0..ce72bc38965 100644
--- a/src/internal/goroot/gc.go
+++ b/src/internal/goroot/gc.go
@@ -7,8 +7,8 @@
package goroot
import (
+ exec "internal/execabs"
"os"
- "os/exec"
"path/filepath"
"strings"
"sync"