bump pkg/init containerd, runc and image-spec to fix CVEs

Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
Avi Deitcher
2024-07-16 10:55:01 +03:00
parent 7b114bfa36
commit 1fe9d0ea76
1143 changed files with 121716 additions and 87692 deletions

View File

@@ -7,6 +7,34 @@ import (
"golang.org/x/sys/unix"
)
// MountedFast is a method of detecting a mount point without reading
// mountinfo from procfs. A caller can only trust the result if no error
// and sure == true are returned. Otherwise, other methods (e.g. parsing
// /proc/mounts) have to be used. If unsure, use Mounted instead (which
// uses MountedFast, but falls back to parsing mountinfo if needed).
//
// If a non-existent path is specified, an appropriate error is returned.
// In case the caller is not interested in this particular error, it should
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
//
// This function is only available on Linux. When available (since kernel
// v5.6), openat2(2) syscall is used to reliably detect all mounts. Otherwise,
// the implementation falls back to using stat(2), which can reliably detect
// normal (but not bind) mounts.
func MountedFast(path string) (mounted, sure bool, err error) {
// Root is always mounted.
if path == string(os.PathSeparator) {
return true, true, nil
}
path, err = normalizePath(path)
if err != nil {
return false, false, err
}
mounted, sure, err = mountedFast(path)
return
}
// mountedByOpenat2 is a method of detecting a mount that works for all kinds
// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel.
func mountedByOpenat2(path string) (bool, error) {
@@ -16,9 +44,6 @@ func mountedByOpenat2(path string) (bool, error) {
Flags: unix.O_PATH | unix.O_CLOEXEC,
})
if err != nil {
if err == unix.ENOENT { // not a mount
return false, nil
}
return false, &os.PathError{Op: "openat2", Path: dir, Err: err}
}
fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{
@@ -26,33 +51,51 @@ func mountedByOpenat2(path string) (bool, error) {
Resolve: unix.RESOLVE_NO_XDEV,
})
_ = unix.Close(dirfd)
switch err {
switch err { //nolint:errorlint // unix errors are bare
case nil: // definitely not a mount
_ = unix.Close(fd)
return false, nil
case unix.EXDEV: // definitely a mount
return true, nil
case unix.ENOENT: // not a mount
return false, nil
}
// not sure
return false, &os.PathError{Op: "openat2", Path: path, Err: err}
}
func mounted(path string) (bool, error) {
// Try a fast path, using openat2() with RESOLVE_NO_XDEV.
mounted, err := mountedByOpenat2(path)
if err == nil {
return mounted, nil
// mountedFast is similar to MountedFast, except it expects a normalized path.
func mountedFast(path string) (mounted, sure bool, err error) {
// Root is always mounted.
if path == string(os.PathSeparator) {
return true, true, nil
}
// Try a fast path, using openat2() with RESOLVE_NO_XDEV.
mounted, err = mountedByOpenat2(path)
if err == nil {
return mounted, true, nil
}
// Another fast path: compare st.st_dev fields.
mounted, err = mountedByStat(path)
// This does not work for bind mounts, so false negative
// is possible, therefore only trust if return is true.
if mounted && err == nil {
return true, true, nil
}
return
}
func mounted(path string) (bool, error) {
path, err := normalizePath(path)
if err != nil {
return false, err
}
mounted, sure, err := mountedFast(path)
if sure && err == nil {
return mounted, nil
}
// Fallback to parsing mountinfo
// Fallback to parsing mountinfo.
return mountedByMountinfo(path)
}

View File

@@ -1,10 +1,9 @@
// +build linux freebsd,cgo openbsd,cgo
//go:build linux || freebsd || openbsd || darwin
// +build linux freebsd openbsd darwin
package mountinfo
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -15,10 +14,6 @@ func mountedByStat(path string) (bool, error) {
var st unix.Stat_t
if err := unix.Lstat(path, &st); err != nil {
if err == unix.ENOENT {
// Treat ENOENT as "not mounted".
return false, nil
}
return false, &os.PathError{Op: "stat", Path: path, Err: err}
}
dev := st.Dev
@@ -37,26 +32,18 @@ func mountedByStat(path string) (bool, error) {
func normalizePath(path string) (realPath string, err error) {
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err)
return "", err
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err)
return "", err
}
if _, err := os.Stat(realPath); err != nil {
return "", fmt.Errorf("failed to stat target of %q: %w", path, err)
return "", err
}
return realPath, nil
}
func mountedByMountinfo(path string) (bool, error) {
path, err := normalizePath(path)
if err != nil {
if errors.Is(err, unix.ENOENT) {
// treat ENOENT as "not mounted"
return false, nil
}
return false, err
}
entries, err := GetMounts(SingleEntryFilter(path))
if err != nil {
return false, err

View File

@@ -10,11 +10,12 @@ func GetMounts(f FilterFunc) ([]*Info, error) {
return parseMountTable(f)
}
// Mounted determines if a specified path is a mount point.
// Mounted determines if a specified path is a mount point. In case of any
// error, false (and an error) is returned.
//
// The argument must be an absolute path, with all symlinks resolved, and clean.
// One way to ensure it is to process the path using filepath.Abs followed by
// filepath.EvalSymlinks before calling this function.
// If a non-existent path is specified, an appropriate error is returned.
// In case the caller is not interested in this particular error, it should
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
func Mounted(path string) (bool, error) {
// root is always mounted
if path == string(os.PathSeparator) {
@@ -29,35 +30,38 @@ type Info struct {
// ID is a unique identifier of the mount (may be reused after umount).
ID int
// Parent indicates the ID of the mount parent (or of self for the top of the
// mount tree).
// Parent is the ID of the parent mount (or of self for the root
// of this mount namespace's mount tree).
Parent int
// Major indicates one half of the device ID which identifies the device class.
Major int
// Major and Minor are the major and the minor components of the Dev
// field of unix.Stat_t structure returned by unix.*Stat calls for
// files on this filesystem.
Major, Minor int
// Minor indicates one half of the device ID which identifies a specific
// instance of device.
Minor int
// Root of the mount within the filesystem.
// Root is the pathname of the directory in the filesystem which forms
// the root of this mount.
Root string
// Mountpoint indicates the mount point relative to the process's root.
// Mountpoint is the pathname of the mount point relative to the
// process's root directory.
Mountpoint string
// Options represents mount-specific options.
// Options is a comma-separated list of mount options.
Options string
// Optional represents optional fields.
// Optional are zero or more fields of the form "tag[:value]",
// separated by a space. Currently, the possible optional fields are
// "shared", "master", "propagate_from", and "unbindable". For more
// information, see mount_namespaces(7) Linux man page.
Optional string
// FSType indicates the type of filesystem, such as EXT3.
// FSType is the filesystem type in the form "type[.subtype]".
FSType string
// Source indicates filesystem specific information or "none".
// Source is filesystem-specific information, or "none".
Source string
// VFSOptions represents per super block options.
// VFSOptions is a comma-separated list of superblock options.
VFSOptions string
}

View File

@@ -1,52 +1,37 @@
// +build freebsd,cgo openbsd,cgo
//go:build freebsd || openbsd || darwin
// +build freebsd openbsd darwin
package mountinfo
/*
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
*/
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
import "golang.org/x/sys/unix"
// parseMountTable returns information about mounted filesystems
func parseMountTable(filter FilterFunc) ([]*Info, error) {
var rawEntries *C.struct_statfs
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
if count == 0 {
return nil, fmt.Errorf("Failed to call getmntinfo")
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
if err != nil {
return nil, err
}
var entries []C.struct_statfs
header := (*reflect.SliceHeader)(unsafe.Pointer(&entries))
header.Cap = count
header.Len = count
header.Data = uintptr(unsafe.Pointer(rawEntries))
entries := make([]unix.Statfs_t, count)
_, err = unix.Getfsstat(entries, unix.MNT_WAIT)
if err != nil {
return nil, err
}
var out []*Info
for _, entry := range entries {
var mountinfo Info
var skip, stop bool
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
mountinfo.FSType = C.GoString(&entry.f_fstypename[0])
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
mountinfo := getMountinfo(&entry)
if filter != nil {
// filter out entries we're not interested in
skip, stop = filter(&mountinfo)
skip, stop = filter(mountinfo)
if skip {
continue
}
}
out = append(out, &mountinfo)
out = append(out, mountinfo)
if stop {
break
}
@@ -55,6 +40,10 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) {
}
func mounted(path string) (bool, error) {
path, err := normalizePath(path)
if err != nil {
return false, err
}
// Fast path: compare st.st_dev fields.
// This should always work for FreeBSD and OpenBSD.
mounted, err := mountedByStat(path)

View File

@@ -14,11 +14,16 @@ import "strings"
// stop: true if parsing should be stopped after the entry.
type FilterFunc func(*Info) (skip, stop bool)
// PrefixFilter discards all entries whose mount points
// do not start with a specific prefix.
// PrefixFilter discards all entries whose mount points do not start with, or
// are equal to the path specified in prefix. The prefix path must be absolute,
// have all symlinks resolved, and cleaned (i.e. no extra slashes or dots).
//
// PrefixFilter treats prefix as a path, not a partial prefix, which means that
// given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns
// "/foo" and "/foo/bar", and discards "/foobar".
func PrefixFilter(prefix string) FilterFunc {
return func(m *Info) (bool, bool) {
skip := !strings.HasPrefix(m.Mountpoint, prefix)
skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/")
return skip, false
}
}

View File

@@ -0,0 +1,14 @@
//go:build freebsd || darwin
// +build freebsd darwin
package mountinfo
import "golang.org/x/sys/unix"
func getMountinfo(entry *unix.Statfs_t) *Info {
return &Info{
Mountpoint: unix.ByteSliceToString(entry.Mntonname[:]),
FSType: unix.ByteSliceToString(entry.Fstypename[:]),
Source: unix.ByteSliceToString(entry.Mntfromname[:]),
}
}

View File

@@ -5,14 +5,19 @@ import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
"sync"
"golang.org/x/sys/unix"
)
// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide a fake mountinfo data.
// that provide fake mountinfo data, or when a source other
// than /proc/thread-self/mountinfo needs to be read from.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
@@ -51,7 +56,7 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
numFields := len(fields)
if numFields < 10 {
// should be at least 10 fields
return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields)
return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
}
// separator field
@@ -66,7 +71,7 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
for fields[sepIdx] != "-" {
sepIdx--
if sepIdx == 5 {
return nil, fmt.Errorf("Parsing '%s' failed: missing - separator", text)
return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text)
}
}
@@ -74,46 +79,39 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
p.Mountpoint, err = unescape(fields[4])
if err != nil {
return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err)
return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err)
}
p.FSType, err = unescape(fields[sepIdx+1])
if err != nil {
return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
}
p.Source, err = unescape(fields[sepIdx+2])
if err != nil {
return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err)
return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err)
}
p.VFSOptions = fields[sepIdx+3]
// ignore any numbers parsing errors, as there should not be any
p.ID, _ = strconv.Atoi(fields[0])
p.Parent, _ = strconv.Atoi(fields[1])
mm := strings.Split(fields[2], ":")
mm := strings.SplitN(fields[2], ":", 3)
if len(mm) != 2 {
return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm)
return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm)
}
p.Major, _ = strconv.Atoi(mm[0])
p.Minor, _ = strconv.Atoi(mm[1])
p.Root, err = unescape(fields[3])
if err != nil {
return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err)
return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err)
}
p.Options = fields[5]
// zero or more optional fields
switch {
case sepIdx == 6:
// zero, do nothing
case sepIdx == 7:
p.Optional = fields[6]
default:
p.Optional = strings.Join(fields[6:sepIdx-1], " ")
}
p.Optional = strings.Join(fields[6:sepIdx], " ")
// Run the filter after parsing all of the fields.
// Run the filter after parsing all fields.
var skip, stop bool
if filter != nil {
skip, stop = filter(p)
@@ -133,10 +131,40 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
return out, nil
}
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
func parseMountTable(filter FilterFunc) ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
var (
haveProcThreadSelf bool
haveProcThreadSelfOnce sync.Once
)
func parseMountTable(filter FilterFunc) (_ []*Info, err error) {
haveProcThreadSelfOnce.Do(func() {
_, err := os.Stat("/proc/thread-self/mountinfo")
haveProcThreadSelf = err == nil
})
// We need to lock ourselves to the current OS thread in order to make sure
// that the thread referenced by /proc/thread-self stays alive until we
// finish parsing the file.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var f *os.File
if haveProcThreadSelf {
f, err = os.Open("/proc/thread-self/mountinfo")
} else {
// On pre-3.17 kernels (such as CentOS 7), we don't have
// /proc/thread-self/ so we need to manually construct
// /proc/self/task/<tid>/ as a fallback.
f, err = os.Open("/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/mountinfo")
if os.IsNotExist(err) {
// If /proc/self/task/... failed, it means that our active pid
// namespace doesn't match the pid namespace of the /proc mount. In
// this case we just have to make do with /proc/self, since there
// is no other way of figuring out our tid in a parent pid
// namespace on pre-3.17 kernels.
f, err = os.Open("/proc/self/mountinfo")
}
}
if err != nil {
return nil, err
}
@@ -166,10 +194,10 @@ func PidMountInfo(pid int) ([]*Info, error) {
// A few specific characters in mountinfo path entries (root and mountpoint)
// are escaped using a backslash followed by a character's ascii code in octal.
//
// space -- as \040
// tab (aka \t) -- as \011
// newline (aka \n) -- as \012
// backslash (aka \\) -- as \134
// space -- as \040
// tab (aka \t) -- as \011
// newline (aka \n) -- as \012
// backslash (aka \\) -- as \134
//
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
func unescape(path string) (string, error) {

View File

@@ -0,0 +1,11 @@
package mountinfo
import "golang.org/x/sys/unix"
func getMountinfo(entry *unix.Statfs_t) *Info {
return &Info{
Mountpoint: unix.ByteSliceToString(entry.F_mntonname[:]),
FSType: unix.ByteSliceToString(entry.F_fstypename[:]),
Source: unix.ByteSliceToString(entry.F_mntfromname[:]),
}
}

View File

@@ -1,4 +1,5 @@
// +build !windows,!linux,!freebsd,!openbsd freebsd,!cgo openbsd,!cgo
//go:build !windows && !linux && !freebsd && !openbsd && !darwin
// +build !windows,!linux,!freebsd,!openbsd,!darwin
package mountinfo