diff --git a/libcontainer/cgroups/fscommon/open.go b/libcontainer/cgroups/fscommon/open.go index 90560f548d3..0a7e3d95282 100644 --- a/libcontainer/cgroups/fscommon/open.go +++ b/libcontainer/cgroups/fscommon/open.go @@ -2,16 +2,63 @@ package fscommon import ( "os" + "strings" + "sync" securejoin "github.com/cyphar/filepath-securejoin" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +const ( + cgroupfsDir = "/sys/fs/cgroup" + cgroupfsPrefix = cgroupfsDir + "/" ) var ( // Set to true by fs unit tests TestMode bool + + cgroupFd int = -1 + prepOnce sync.Once + prepErr error + resolveFlags uint64 ) +func prepareOpenat2() error { + prepOnce.Do(func() { + fd, err := unix.Openat2(-1, cgroupfsDir, &unix.OpenHow{ + Flags: unix.O_DIRECTORY | unix.O_PATH}) + if err != nil { + prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err} + if err != unix.ENOSYS { + logrus.Warnf("falling back to securejoin: %s", prepErr) + } else { + logrus.Debug("openat2 not available, falling back to securejoin") + } + return + } + var st unix.Statfs_t + if err = unix.Fstatfs(fd, &st); err != nil { + prepErr = &os.PathError{Op: "statfs", Path: cgroupfsDir, Err: err} + logrus.Warnf("falling back to securejoin: %s", prepErr) + return + } + + cgroupFd = fd + + resolveFlags = unix.RESOLVE_BENEATH | unix.RESOLVE_NO_MAGICLINKS + if st.Type == unix.CGROUP2_SUPER_MAGIC { + // cgroupv2 has a single mountpoint and no "cpu,cpuacct" symlinks + resolveFlags |= unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_SYMLINKS + } + + }) + + return prepErr +} + // OpenFile opens a cgroup file in a given dir with given flags. // It is supposed to be used for cgroup files only. func OpenFile(dir, file string, flags int) (*os.File, error) { @@ -24,6 +71,29 @@ func OpenFile(dir, file string, flags int) (*os.File, error) { flags |= os.O_TRUNC | os.O_CREATE mode = 0o600 } + reldir := strings.TrimPrefix(dir, cgroupfsPrefix) + if len(reldir) == len(dir) { // non-standard path, old system? + return openWithSecureJoin(dir, file, flags, mode) + } + if prepareOpenat2() != nil { + return openWithSecureJoin(dir, file, flags, mode) + } + + relname := reldir + "/" + file + fd, err := unix.Openat2(cgroupFd, relname, + &unix.OpenHow{ + Resolve: resolveFlags, + Flags: uint64(flags) | unix.O_CLOEXEC, + Mode: uint64(mode), + }) + if err != nil { + return nil, &os.PathError{Op: "openat2", Path: dir + "/" + file, Err: err} + } + + return os.NewFile(uintptr(fd), cgroupfsPrefix+relname), nil +} + +func openWithSecureJoin(dir, file string, flags int, mode os.FileMode) (*os.File, error) { path, err := securejoin.SecureJoin(dir, file) if err != nil { return nil, err