diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 0b17168c2bb..6d0ecdb2177 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -628,6 +628,21 @@ func (c *Container) newParentProcess(p *Process) (parentProcess, error) { // // See . cmd.ExtraFiles = append(cmd.ExtraFiles, safeExe) + + // But because of we have added safeExe to the set of ExtraFiles, if the + // fd of safeExe is too small, go stdlib will dup3 it to another fd, then + // it will cause the original fd closed. (#4294) + if int(safeExe.Fd()) <= stdioFdCount+len(cmd.ExtraFiles) { + maxFd, err := utils.GetMaxFds() + if err != nil { + return nil, fmt.Errorf("unable to get the max opened fd of current process: %w", err) + } + maxFd = maxFd + 1 + 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) + } } // NOTE: when running a container with no PID namespace and the parent 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 {