os/exec: add GODEBUG setting to opt out of ErrDot changes

The changes are likely to break users, and we need
to make it easy to unbreak without code changes.

For #43724.
Fixes #53962.

Change-Id: I105c5d6c801d354467e0cefd268189c18846858e
Reviewed-on: https://go-review.googlesource.com/c/go/+/419794
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
This commit is contained in:
Russ Cox 2022-07-27 14:36:05 -04:00
parent 462b78fe70
commit 027855e8d8
6 changed files with 68 additions and 37 deletions

View file

@ -177,7 +177,11 @@ var depsRules = `
os/signal, STR
< path/filepath
< io/ioutil, os/exec;
< io/ioutil;
os < internal/godebug;
path/filepath, internal/godebug < os/exec;
io/ioutil, os/exec, os/signal
< OS;
@ -187,8 +191,6 @@ var depsRules = `
OS
< golang.org/x/sys/cpu;
os < internal/godebug;
# FMT is OS (which includes string routines) plus reflect and fmt.
# It does not include package log, which should be avoided in core packages.
strconv, unicode

View file

@ -56,40 +56,58 @@ func TestLookPath(t *testing.T) {
// Add "." to PATH so that exec.LookPath looks in the current directory on all systems.
// And try to trick it with "../testdir" too.
for _, dir := range []string{".", "../testdir"} {
t.Run(pathVar+"="+dir, func(t *testing.T) {
t.Setenv(pathVar, dir+string(filepath.ListSeparator)+origPath)
good := dir + "/execabs-test"
if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good)
}
if runtime.GOOS == "windows" {
good = dir + `\execabs-test`
if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good)
}
}
for _, errdot := range []string{"1", "0"} {
t.Run("GODEBUG=execerrdot="+errdot, func(t *testing.T) {
t.Setenv("GODEBUG", "execerrdot="+errdot)
for _, dir := range []string{".", "../testdir"} {
t.Run(pathVar+"="+dir, func(t *testing.T) {
t.Setenv(pathVar, dir+string(filepath.ListSeparator)+origPath)
good := dir + "/execabs-test"
if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good)
}
if runtime.GOOS == "windows" {
good = dir + `\execabs-test`
if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good)
}
}
if _, err := LookPath("execabs-test"); err == nil {
t.Fatalf("LookPath didn't fail when finding a non-relative path")
} else if !errors.Is(err, ErrDot) {
t.Fatalf("LookPath returned unexpected error: want Is ErrDot, got %q", err)
}
_, err := LookPath("execabs-test")
if errdot == "1" {
if err == nil {
t.Fatalf("LookPath didn't fail when finding a non-relative path")
} else if !errors.Is(err, ErrDot) {
t.Fatalf("LookPath returned unexpected error: want Is ErrDot, got %q", err)
}
} else {
if err != nil {
t.Fatalf("LookPath failed unexpectedly: %v", err)
}
}
cmd := Command("execabs-test")
if cmd.Err == nil {
t.Fatalf("Command didn't fail when finding a non-relative path")
} else if !errors.Is(cmd.Err, ErrDot) {
t.Fatalf("Command returned unexpected error: want Is ErrDot, got %q", cmd.Err)
}
cmd.Err = nil
cmd := Command("execabs-test")
if errdot == "1" {
if cmd.Err == nil {
t.Fatalf("Command didn't fail when finding a non-relative path")
} else if !errors.Is(cmd.Err, ErrDot) {
t.Fatalf("Command returned unexpected error: want Is ErrDot, got %q", cmd.Err)
}
cmd.Err = nil
} else {
if cmd.Err != nil {
t.Fatalf("Command failed unexpectedly: %v", err)
}
}
// Clearing cmd.Err should let the execution proceed,
// and it should fail because it's not a valid binary.
if err := cmd.Run(); err == nil {
t.Fatalf("Run did not fail: expected exec error")
} else if errors.Is(err, ErrDot) {
t.Fatalf("Run returned unexpected error ErrDot: want error like ENOEXEC: %q", err)
// Clearing cmd.Err should let the execution proceed,
// and it should fail because it's not a valid binary.
if err := cmd.Run(); err == nil {
t.Fatalf("Run did not fail: expected exec error")
} else if errors.Is(err, ErrDot) {
t.Fatalf("Run returned unexpected error ErrDot: want error like ENOEXEC: %q", err)
}
})
}
})
}

View file

@ -80,6 +80,11 @@
// log.Fatal(err)
// }
//
// Setting the environment variable GODEBUG=execerrdot=0
// disables generation of ErrDot entirely, temporarily restoring the pre-Go 1.19
// behavior for programs that are unable to apply more targeted fixes.
// A future version of Go may remove support for this variable.
//
// Before adding such overrides, make sure you understand the
// security implications of doing so.
// See https://go.dev/blog/path-security for more information.

View file

@ -6,6 +6,7 @@ package exec
import (
"errors"
"internal/godebug"
"io/fs"
"os"
"path/filepath"
@ -53,7 +54,7 @@ func LookPath(file string) (string, error) {
for _, dir := range filepath.SplitList(path) {
path := filepath.Join(dir, file)
if err := findExecutable(path); err == nil {
if !filepath.IsAbs(path) {
if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" {
return path, &Error{file, ErrDot}
}
return path, nil

View file

@ -8,6 +8,7 @@ package exec
import (
"errors"
"internal/godebug"
"io/fs"
"os"
"path/filepath"
@ -56,7 +57,7 @@ func LookPath(file string) (string, error) {
}
path := filepath.Join(dir, file)
if err := findExecutable(path); err == nil {
if !filepath.IsAbs(path) {
if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" {
return path, &Error{file, ErrDot}
}
return path, nil

View file

@ -6,6 +6,7 @@ package exec
import (
"errors"
"internal/godebug"
"io/fs"
"os"
"path/filepath"
@ -102,6 +103,9 @@ func LookPath(file string) (string, error) {
)
if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found {
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
if godebug.Get("execerrdot") == "0" {
return f, nil
}
dotf, dotErr = f, &Error{file, ErrDot}
}
}
@ -124,7 +128,7 @@ func LookPath(file string) (string, error) {
}
}
if !filepath.IsAbs(f) {
if !filepath.IsAbs(f) && godebug.Get("execerrdot") != "0" {
return f, &Error{file, ErrDot}
}
return f, nil