Skip to content

Commit

Permalink
libcontainer/cgroups/fscommon: add openat2 support
Browse files Browse the repository at this point in the history
In case openat2 is available, it will be used to guarantee
that we're not accessing anything other than cgroupfs[2] files.

In cases when openat2 is not available, or when cgroup has a
non-standard prefix (not "/sys/fs/cgroup", which might theoretically
be the case on some very old installs and/or very custom systems),
fall back to using securejoin + os.Open like we did before.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
  • Loading branch information
kolyshkin committed Oct 29, 2020
1 parent 8d860c6 commit d8cc4af
Showing 1 changed file with 63 additions and 0 deletions.
63 changes: 63 additions & 0 deletions libcontainer/cgroups/fscommon/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,56 @@ package fscommon

import (
"os"
"strings"
"sync"

securejoin "github.com/cyphar/filepath-securejoin"
"github.com/pkg/errors"
"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}
return
}
var st unix.Statfs_t
if err = unix.Fstatfs(fd, &st); err != nil {
// XXX: make unix.ENOSYS a special case?
prepErr = &os.PathError{Op: "statfs", Path: cgroupfsDir, Err: err}
return
}

cgroupFd = fd

if st.Type == unix.CGROUP2_SUPER_MAGIC {
resolveFlags = unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_SYMLINKS
} else {
// cgroupfs v1 is a set of separate mounts
resolveFlags = unix.RESOLVE_BENEATH
}
})

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) {
Expand All @@ -24,6 +64,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
Expand Down

0 comments on commit d8cc4af

Please sign in to comment.