diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 0b17168c2bb..6e7c0860f1b 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -619,6 +619,33 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) { } if safeExe != nil { + // Because we want to add safeExe to the set of ExtraFiles, if the fd of safeExe is + // too small, go stdlib will dup3 it to another fd, or dup3 a other fd to this fd, + // then it will cause the fd type cmd.Path refers to a random path. (#4294) + + // +1 means safeExe. + minFd := stdioFdCount + len(cmd.ExtraFiles) + 1 + if p.Init { + // This refers to fifo. + minFd++ + } + if int(safeExe.Fd()) <= minFd { + maxFd, err := utils.GetMaxFds() + if err != nil { + return nil, fmt.Errorf("unable to get the max opened fd of current process: %w", err) + } + maxFd++ + if err := unix.Dup3(int(safeExe.Fd()), maxFd, unix.O_CLOEXEC); err != nil { + return nil, fmt.Errorf("unable to dup3 the fd from %d to %d: %w", safeExe.Fd(), maxFd, err) + } + cmd.Path = "/proc/self/fd/" + strconv.Itoa(maxFd) + newSafeExe := os.NewFile(uintptr(maxFd), safeExe.Name()) + if err := safeExe.Close(); err != nil { + return nil, fmt.Errorf("unable to close old safe exe(%d): %w", safeExe.Fd(), err) + } + safeExe = newSafeExe + } + // Due to a Go stdlib bug, we need to add safeExe to the set of // ExtraFiles otherwise it is possible for the stdlib to clobber the fd // during forkAndExecInChild1 and replace it with some other file that diff --git a/libcontainer/utils/utils_unix.go b/libcontainer/utils/utils_unix.go index cc84597a7ce..b791e36545c 100644 --- a/libcontainer/utils/utils_unix.go +++ b/libcontainer/utils/utils_unix.go @@ -97,6 +97,17 @@ func fdRangeFrom(minFd int, fn fdFunc) error { return nil } +// GetMaxFds returns the max opened fd of current process. +func GetMaxFds() (int, error) { + maxFd := -1 + err := fdRangeFrom(-1, func(fd int) { + if fd > maxFd { + maxFd = fd + } + }) + return maxFd, err +} + // CloseExecFrom sets the O_CLOEXEC flag on all file descriptors greater or // equal to minFd in the current process. func CloseExecFrom(minFd int) error {