From 70f4e46e68d6795863b13743ed6d3895fd721318 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 7 Jul 2023 15:11:41 +1000 Subject: [PATCH] utils: use close_range(2) to close leftover file descriptors close_range(2) is far more efficient than a readdir of /proc/self/fd and then doing a syscall for each file descriptor. Signed-off-by: Aleksa Sarai --- libcontainer/utils/utils_unix.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libcontainer/utils/utils_unix.go b/libcontainer/utils/utils_unix.go index 220d0b439..6b9a7be03 100644 --- a/libcontainer/utils/utils_unix.go +++ b/libcontainer/utils/utils_unix.go @@ -5,8 +5,10 @@ package utils import ( "fmt" + "math" "os" "strconv" + "sync" "golang.org/x/sys/unix" ) @@ -23,9 +25,38 @@ func EnsureProcHandle(fh *os.File) error { return nil } +var ( + haveCloseRangeCloexecBool bool + haveCloseRangeCloexecOnce sync.Once +) + +func haveCloseRangeCloexec() bool { + haveCloseRangeCloexecOnce.Do(func() { + // Make sure we're not closing a random file descriptor. + tmpFd, err := unix.FcntlInt(0, unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return + } + defer unix.Close(tmpFd) + + err = unix.CloseRange(uint(tmpFd), uint(tmpFd), unix.CLOSE_RANGE_CLOEXEC) + // Any error means we cannot use close_range(CLOSE_RANGE_CLOEXEC). + // -ENOSYS and -EINVAL ultimately mean we don't have support, but any + // other potential error would imply that even the most basic close + // operation wouldn't work. + haveCloseRangeCloexecBool = err == nil + }) + return haveCloseRangeCloexecBool +} + // CloseExecFrom applies O_CLOEXEC to all file descriptors currently open for // the process (except for those below the given fd value). func CloseExecFrom(minFd int) error { + if haveCloseRangeCloexec() { + err := unix.CloseRange(uint(minFd), math.MaxUint, unix.CLOSE_RANGE_CLOEXEC) + return os.NewSyscallError("close_range", err) + } + fdDir, err := os.Open("/proc/self/fd") if err != nil { return err