Skip to content

os/exec: unexpected ErrDot returned if there is an empty path in PATH #61493

@LawyZheng

Description

@LawyZheng

What version of Go are you using (go version)?

$ go version

go1.20.2

Does this issue reproduce with the latest release?

no tested yet

What operating system and processor architecture are you using (go env)?

go env Output
$ go env

GO111MODULE="on"
GOARCH="arm64"
GOBIN=""
GOCACHE="~/Library/Caches/go-build"
GOENV="~/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="~/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="~/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="~/.g/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="~/.g/go/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.2"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="~/Library/Application Support/JetBrains/GoLand2023.1/scratches/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/bz/g8kmk1fn0xj0rnprc0v0h33r0000gn/T/go-build1655805848=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

As an apology, although my build env is Mac M1, I cross-compiled the application into the amd64 windows.
So this is a description of the Windows system.

I missed to set my PATH with a empty path, like path1;;path2;
So when I wanted to use exec.Command to open the cmd.exe (my working directory is C:/Windows/System32, just the same as cmd.exe lies), I got aErrDot.

In this situation, I got 2 ways to find the cmd.exe in PATH. The one is to find in the absolute path C:/Windows/System32, and the other one is the empty path which I entered by mistake. And the empty one comes first.

if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found {
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
if execerrdot.Value() == "0" {
execerrdot.IncNonDefault()
return f, nil
}
dotf, dotErr = f, &Error{file, ErrDot}
}
}
path := os.Getenv("path")
for _, dir := range filepath.SplitList(path) {
if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
if dotErr != nil {
// https://go.dev/issue/53536: if we resolved a relative path implicitly,
// and it is the same executable that would be resolved from the explicit %PATH%,
// prefer the explicit name for the executable (and, likely, no error) instead
// of the equivalent implicit name with ErrDot.
//
// Otherwise, return the ErrDot for the implicit path as soon as we find
// out that the explicit one doesn't match.
dotfi, dotfiErr := os.Lstat(dotf)
fi, fiErr := os.Lstat(f)
if dotfiErr != nil || fiErr != nil || !os.SameFile(dotfi, fi) {
return dotf, dotErr
}
}
if !filepath.IsAbs(f) {
if execerrdot.Value() != "0" {
return f, &Error{file, ErrDot}
}
execerrdot.IncNonDefault()
}
return f, nil
}
}

In line 114, it enters the empty path which is considered as the . path, and finds cmd.exe in this path.
Without a doubt, it is treated as a RELATIVE PATH and returned with an ErrDot.

I know the relative path is forbidden now.
And I think this is similar to the problem mentioned in the #53536, but not exactly the same.

What did you expect to see?

I have to admit that this is my fault to set an empty path by mistake.
But I still wonder whether the logic in this code should be optimized or not.
Provided with my two opinions as below:

  1. In the PATH list, skip the empty path or anything like the relative path which will function as the . path eventually. After all, there is no need to deal with the . path in this code, because it has been dealt with before.

  2. We can sort the list before range it. Make the absolute paths come before the relative paths.

Metadata

Metadata

Assignees

Labels

FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.OS-Windows

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions