From e213d65be6d7ec608198c2e6825d0f966e6b2764 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 22 Oct 2024 17:24:51 -0700 Subject: [PATCH 1/2] deps: switch to moby/sys/userns runc/libcontainer/userns package moved to a new home; the old package will be made obsoleted. Switch to the new package. Signed-off-by: Kir Kolyshkin --- add.go | 5 +++-- add_common.go | 7 ------- add_linux.go | 9 --------- go.mod | 2 +- 4 files changed, 4 insertions(+), 19 deletions(-) delete mode 100644 add_common.go delete mode 100644 add_linux.go diff --git a/add.go b/add.go index 036a15958e1..2e884ad89c2 100644 --- a/add.go +++ b/add.go @@ -30,6 +30,7 @@ import ( "github.com/containers/storage/pkg/regexp" "github.com/docker/go-connections/tlsconfig" "github.com/hashicorp/go-multierror" + "github.com/moby/sys/userns" digest "github.com/opencontainers/go-digest" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" @@ -545,7 +546,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption ChmodDirs: nil, ChownFiles: nil, ChmodFiles: nil, - IgnoreDevices: runningInUserNS(), + IgnoreDevices: userns.RunningInUserNS(), } putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) } @@ -686,7 +687,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption ChmodDirs: nil, ChownFiles: nil, ChmodFiles: nil, - IgnoreDevices: runningInUserNS(), + IgnoreDevices: userns.RunningInUserNS(), } putErr = copier.Put(extractDirectory, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) } diff --git a/add_common.go b/add_common.go deleted file mode 100644 index ddcc9d285db..00000000000 --- a/add_common.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !linux - -package buildah - -func runningInUserNS() bool { - return false -} diff --git a/add_linux.go b/add_linux.go deleted file mode 100644 index 78b74249627..00000000000 --- a/add_linux.go +++ /dev/null @@ -1,9 +0,0 @@ -package buildah - -import ( - "github.com/opencontainers/runc/libcontainer/userns" -) - -func runningInUserNS() bool { - return userns.RunningInUserNS() -} diff --git a/go.mod b/go.mod index a540c1fdd6e..9ddef2935d8 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/mattn/go-shellwords v1.0.12 github.com/moby/buildkit v0.16.0 github.com/moby/sys/capability v0.3.0 + github.com/moby/sys/userns v0.1.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/runc v1.2.0-rc.3 @@ -111,7 +112,6 @@ require ( github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/sys/user v0.3.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect From aa70771648792e675e8308e856453edbbfc84fdc Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 22 Oct 2024 17:27:49 -0700 Subject: [PATCH 2/2] deps: bump runc to v1.2.0 Signed-off-by: Kir Kolyshkin --- go.mod | 2 +- go.sum | 4 +- .../runc/libcontainer/cgroups/cgroups.go | 5 + .../runc/libcontainer/cgroups/fs/cpuacct.go | 2 +- .../runc/libcontainer/cgroups/fs/fs.go | 5 +- .../runc/libcontainer/cgroups/fs/memory.go | 5 +- .../runc/libcontainer/cgroups/fs2/fs2.go | 2 +- .../runc/libcontainer/cgroups/utils.go | 2 +- .../runc/libcontainer/system/linux.go | 257 ------------------ .../runc/libcontainer/system/proc.go | 127 --------- .../runc/libcontainer/system/rlimit_linux.go | 15 - .../libcontainer/system/rlimit_linux_go122.go | 27 -- .../runc/libcontainer/userns/userns.go | 8 - .../runc/libcontainer/userns/userns_linux.go | 49 ---- .../userns/userns_linux_fuzzer.go | 8 - .../libcontainer/userns/userns_unsupported.go | 6 - .../runc/libcontainer/utils/utils.go | 26 +- .../runc/libcontainer/utils/utils_unix.go | 104 +++---- vendor/modules.txt | 4 +- 19 files changed, 61 insertions(+), 597 deletions(-) delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/system/linux.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/system/proc.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux_fuzzer.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go diff --git a/go.mod b/go.mod index 9ddef2935d8..8312c8b16f2 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/moby/sys/userns v0.1.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 - github.com/opencontainers/runc v1.2.0-rc.3 + github.com/opencontainers/runc v1.2.0 github.com/opencontainers/runtime-spec v1.2.0 github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc github.com/opencontainers/selinux v1.11.1 diff --git a/go.sum b/go.sum index 234ab4be035..d8351f9ced0 100644 --- a/go.sum +++ b/go.sum @@ -269,8 +269,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opencontainers/runc v1.2.0-rc.3 h1:5vQhejBp4S5w1DwFZ7L3CSOQX9cmcc8JKFy/mOBTJlo= -github.com/opencontainers/runc v1.2.0-rc.3/go.mod h1:HADgqJU4nqAmOpe+uYBTJ4ZRvjks3ptCjKXp1pHqmCc= +github.com/opencontainers/runc v1.2.0 h1:qke7ZVCmJcKrJVY2iHJVC+0kql9uYdkusOPsQOOeBw4= +github.com/opencontainers/runc v1.2.0/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc h1:d2hUh5O6MRBvStV55MQ8we08t42zSTqBbscoQccWmMc= diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go index 811f2d26e00..53e194c74dc 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go @@ -11,6 +11,11 @@ var ( // is not configured to set device rules. ErrDevicesUnsupported = errors.New("cgroup manager is not configured to set device rules") + // ErrRootless is returned by [Manager.Apply] when there is an error + // creating cgroup directory, and cgroup.Rootless is set. In general, + // this error is to be ignored. + ErrRootless = errors.New("cgroup manager can not access cgroup (rootless container)") + // DevicesSetV1 and DevicesSetV2 are functions to set devices for // cgroup v1 and v2, respectively. Unless // [github.com/opencontainers/runc/libcontainer/cgroups/devices] diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go index d3bd7e111c7..69f8f9d8cdd 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go @@ -91,7 +91,7 @@ func getCpuUsageBreakdown(path string) (uint64, uint64, error) { if err != nil { return 0, 0, err } - // TODO: use strings.SplitN instead. + fields := strings.Fields(data) if len(fields) < 4 || fields[0] != userField || fields[2] != systemField { return 0, 0, malformedLine(path, file, data) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go index d2decb127ca..ba15bfc4077 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go @@ -105,7 +105,7 @@ func isIgnorableError(rootless bool, err error) bool { return false } -func (m *Manager) Apply(pid int) (err error) { +func (m *Manager) Apply(pid int) (retErr error) { m.mu.Lock() defer m.mu.Unlock() @@ -129,6 +129,7 @@ func (m *Manager) Apply(pid int) (err error) { // later by Set, which fails with a friendly error (see // if path == "" in Set). if isIgnorableError(c.Rootless, err) && c.Path == "" { + retErr = cgroups.ErrRootless delete(m.paths, name) continue } @@ -136,7 +137,7 @@ func (m *Manager) Apply(pid int) (err error) { } } - return nil + return retErr } func (m *Manager) Destroy() error { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go index 783566d68f0..0abea63f92a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go @@ -282,11 +282,11 @@ func getPageUsageByNUMA(path string) (cgroups.PageUsageByNUMA, error) { line := scanner.Text() columns := strings.SplitN(line, " ", maxColumns) for i, column := range columns { - byNode := strings.SplitN(column, "=", 2) + key, val, ok := strings.Cut(column, "=") // Some custom kernels have non-standard fields, like // numa_locality 0 0 0 0 0 0 0 0 0 0 // numa_exectime 0 - if len(byNode) < 2 { + if !ok { if i == 0 { // Ignore/skip those. break @@ -296,7 +296,6 @@ func getPageUsageByNUMA(path string) (cgroups.PageUsageByNUMA, error) { return stats, malformedLine(path, file, line) } } - key, val := byNode[0], byNode[1] if i == 0 { // First column: key is name, val is total. field = getNUMAField(&stats, key) if field == nil { // unknown field (new kernel?) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go index b1be7df5ccc..93f81bf8dae 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go @@ -71,7 +71,7 @@ func (m *Manager) Apply(pid int) error { if m.config.Rootless { if m.config.Path == "" { if blNeed, nErr := needAnyControllers(m.config.Resources); nErr == nil && !blNeed { - return nil + return cgroups.ErrRootless } return fmt.Errorf("rootless needs no limits + no cgrouppath when no permission is granted for cgroups: %w", err) } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index d303cf204c9..67341e690d2 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -12,7 +12,7 @@ import ( "sync" "time" - "github.com/opencontainers/runc/libcontainer/userns" + "github.com/moby/sys/userns" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go deleted file mode 100644 index 27e89d635ca..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +++ /dev/null @@ -1,257 +0,0 @@ -//go:build linux - -package system - -import ( - "fmt" - "io" - "os" - "runtime" - "strconv" - "strings" - "syscall" - "unsafe" - - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -type ParentDeathSignal int - -func (p ParentDeathSignal) Restore() error { - if p == 0 { - return nil - } - current, err := GetParentDeathSignal() - if err != nil { - return err - } - if p == current { - return nil - } - return p.Set() -} - -func (p ParentDeathSignal) Set() error { - return SetParentDeathSignal(uintptr(p)) -} - -func Exec(cmd string, args []string, env []string) error { - for { - err := unix.Exec(cmd, args, env) - if err != unix.EINTR { - return &os.PathError{Op: "exec", Path: cmd, Err: err} - } - } -} - -func execveat(fd uintptr, pathname string, args []string, env []string, flags int) error { - pathnamep, err := syscall.BytePtrFromString(pathname) - if err != nil { - return err - } - - argvp, err := syscall.SlicePtrFromStrings(args) - if err != nil { - return err - } - - envp, err := syscall.SlicePtrFromStrings(env) - if err != nil { - return err - } - - _, _, errno := syscall.Syscall6( - unix.SYS_EXECVEAT, - fd, - uintptr(unsafe.Pointer(pathnamep)), - uintptr(unsafe.Pointer(&argvp[0])), - uintptr(unsafe.Pointer(&envp[0])), - uintptr(flags), - 0, - ) - return errno -} - -func Fexecve(fd uintptr, args []string, env []string) error { - var err error - for { - err = execveat(fd, "", args, env, unix.AT_EMPTY_PATH) - if err != unix.EINTR { // nolint:errorlint // unix errors are bare - break - } - } - if err == unix.ENOSYS { // nolint:errorlint // unix errors are bare - // Fallback to classic /proc/self/fd/... exec. - return Exec("/proc/self/fd/"+strconv.Itoa(int(fd)), args, env) - } - return os.NewSyscallError("execveat", err) -} - -func SetParentDeathSignal(sig uintptr) error { - if err := unix.Prctl(unix.PR_SET_PDEATHSIG, sig, 0, 0, 0); err != nil { - return err - } - return nil -} - -func GetParentDeathSignal() (ParentDeathSignal, error) { - var sig int - if err := unix.Prctl(unix.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0, 0, 0); err != nil { - return -1, err - } - return ParentDeathSignal(sig), nil -} - -func SetKeepCaps() error { - if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil { - return err - } - - return nil -} - -func ClearKeepCaps() error { - if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 0, 0, 0, 0); err != nil { - return err - } - - return nil -} - -func Setctty() error { - if err := unix.IoctlSetInt(0, unix.TIOCSCTTY, 0); err != nil { - return err - } - return nil -} - -// SetSubreaper sets the value i as the subreaper setting for the calling process -func SetSubreaper(i int) error { - return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) -} - -// GetSubreaper returns the subreaper setting for the calling process -func GetSubreaper() (int, error) { - var i uintptr - - if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { - return -1, err - } - - return int(i), nil -} - -func ExecutableMemfd(comment string, flags int) (*os.File, error) { - // Try to use MFD_EXEC first. On pre-6.3 kernels we get -EINVAL for this - // flag. On post-6.3 kernels, with vm.memfd_noexec=1 this ensures we get an - // executable memfd. For vm.memfd_noexec=2 this is a bit more complicated. - // The original vm.memfd_noexec=2 implementation incorrectly silently - // allowed MFD_EXEC[1] -- this should be fixed in 6.6. On 6.6 and newer - // kernels, we will get -EACCES if we try to use MFD_EXEC with - // vm.memfd_noexec=2 (for 6.3-6.5, -EINVAL was the intended return value). - // - // The upshot is we only need to retry without MFD_EXEC on -EINVAL because - // it just so happens that passing MFD_EXEC bypasses vm.memfd_noexec=2 on - // kernels where -EINVAL is actually a security denial. - memfd, err := unix.MemfdCreate(comment, flags|unix.MFD_EXEC) - if err == unix.EINVAL { - memfd, err = unix.MemfdCreate(comment, flags) - } - if err != nil { - if err == unix.EACCES { - logrus.Info("memfd_create(MFD_EXEC) failed, possibly due to vm.memfd_noexec=2 -- falling back to less secure O_TMPFILE") - } - err := os.NewSyscallError("memfd_create", err) - return nil, fmt.Errorf("failed to create executable memfd: %w", err) - } - return os.NewFile(uintptr(memfd), "/memfd:"+comment), nil -} - -// Copy is like io.Copy except it uses sendfile(2) if the source and sink are -// both (*os.File) as an optimisation to make copies faster. -func Copy(dst io.Writer, src io.Reader) (copied int64, err error) { - dstFile, _ := dst.(*os.File) - srcFile, _ := src.(*os.File) - - if dstFile != nil && srcFile != nil { - fi, err := srcFile.Stat() - if err != nil { - goto fallback - } - size := fi.Size() - for size > 0 { - n, err := unix.Sendfile(int(dstFile.Fd()), int(srcFile.Fd()), nil, int(size)) - if n > 0 { - size -= int64(n) - copied += int64(n) - } - if err == unix.EINTR { - continue - } - if err != nil { - if copied == 0 { - // If we haven't copied anything so far, we can safely just - // fallback to io.Copy. We could always do the fallback but - // it's safer to error out in the case of a partial copy - // followed by an error (which should never happen). - goto fallback - } - return copied, fmt.Errorf("partial sendfile copy: %w", err) - } - } - return copied, nil - } - -fallback: - return io.Copy(dst, src) -} - -// SetLinuxPersonality sets the Linux execution personality. For more information see the personality syscall documentation. -// checkout getLinuxPersonalityFromStr() from libcontainer/specconv/spec_linux.go for type conversion. -func SetLinuxPersonality(personality int) error { - _, _, errno := unix.Syscall(unix.SYS_PERSONALITY, uintptr(personality), 0, 0) - if errno != 0 { - return &os.SyscallError{Syscall: "set_personality", Err: errno} - } - return nil -} - -func prepareAt(dir *os.File, path string) (int, string) { - if dir == nil { - return unix.AT_FDCWD, path - } - - // Rather than just filepath.Join-ing path here, do it manually so the - // error and handle correctly indicate cases like path=".." as being - // relative to the correct directory. The handle.Name() might end up being - // wrong but because this is (currently) only used in MkdirAllInRoot, that - // isn't a problem. - dirName := dir.Name() - if !strings.HasSuffix(dirName, "/") { - dirName += "/" - } - fullPath := dirName + path - - return int(dir.Fd()), fullPath -} - -func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { - dirFd, fullPath := prepareAt(dir, path) - fd, err := unix.Openat(dirFd, path, flags, mode) - if err != nil { - return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err} - } - runtime.KeepAlive(dir) - return os.NewFile(uintptr(fd), fullPath), nil -} - -func Mkdirat(dir *os.File, path string, mode uint32) error { - dirFd, fullPath := prepareAt(dir, path) - err := unix.Mkdirat(dirFd, path, mode) - if err != nil { - err = &os.PathError{Op: "mkdirat", Path: fullPath, Err: err} - } - runtime.KeepAlive(dir) - return err -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go b/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go deleted file mode 100644 index 774443ec9d2..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go +++ /dev/null @@ -1,127 +0,0 @@ -package system - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" -) - -// State is the status of a process. -type State rune - -const ( // Only values for Linux 3.14 and later are listed here - Dead State = 'X' - DiskSleep State = 'D' - Running State = 'R' - Sleeping State = 'S' - Stopped State = 'T' - TracingStop State = 't' - Zombie State = 'Z' - Parked State = 'P' - Idle State = 'I' -) - -// String forms of the state from proc(5)'s documentation for -// /proc/[pid]/status' "State" field. -func (s State) String() string { - switch s { - case Dead: - return "dead" - case DiskSleep: - return "disk sleep" - case Running: - return "running" - case Sleeping: - return "sleeping" - case Stopped: - return "stopped" - case TracingStop: - return "tracing stop" - case Zombie: - return "zombie" - case Parked: - return "parked" - case Idle: - return "idle" // kernel thread - default: - return fmt.Sprintf("unknown (%c)", s) - } -} - -// Stat_t represents the information from /proc/[pid]/stat, as -// described in proc(5) with names based on the /proc/[pid]/status -// fields. -type Stat_t struct { - // Name is the command run by the process. - Name string - - // State is the state of the process. - State State - - // StartTime is the number of clock ticks after system boot (since - // Linux 2.6). - StartTime uint64 -} - -// Stat returns a Stat_t instance for the specified process. -func Stat(pid int) (stat Stat_t, err error) { - bytes, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat")) - if err != nil { - return stat, err - } - return parseStat(string(bytes)) -} - -func parseStat(data string) (stat Stat_t, err error) { - // Example: - // 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 - // The fields are space-separated, see full description in proc(5). - // - // We are only interested in: - // * field 2: process name. It is the only field enclosed into - // parenthesis, as it can contain spaces (and parenthesis) inside. - // * field 3: process state, a single character (%c) - // * field 22: process start time, a long unsigned integer (%llu). - - // 1. Look for the first '(' and the last ')' first, what's in between is Name. - // We expect at least 20 fields and a space after the last one. - - const minAfterName = 20*2 + 1 // the min field is '0 '. - - first := strings.IndexByte(data, '(') - if first < 0 || first+minAfterName >= len(data) { - return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data) - } - - last := strings.LastIndexByte(data, ')') - if last <= first || last+minAfterName >= len(data) { - return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data) - } - - stat.Name = data[first+1 : last] - - // 2. Remove fields 1 and 2 and a space after. State is right after. - data = data[last+2:] - stat.State = State(data[0]) - - // 3. StartTime is field 22, data is at field 3 now, so we need to skip 19 spaces. - skipSpaces := 22 - 3 - for first = 0; skipSpaces > 0 && first < len(data); first++ { - if data[first] == ' ' { - skipSpaces-- - } - } - // Now first points to StartTime; look for space right after. - i := strings.IndexByte(data[first:], ' ') - if i < 0 { - return stat, fmt.Errorf("invalid stat data (too short): %q", data) - } - stat.StartTime, err = strconv.ParseUint(data[first:first+i], 10, 64) - if err != nil { - return stat, fmt.Errorf("invalid stat data (bad start time): %w", err) - } - - return stat, nil -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go deleted file mode 100644 index 4595fa82aa1..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build go1.23 - -package system - -import ( - "syscall" -) - -// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. The argument -// is process RLIMIT_NOFILE values. Relies on go.dev/cl/588076. -func ClearRlimitNofileCache(lim *syscall.Rlimit) { - // Ignore the return values since we only need to clean the cache, - // the limit is going to be set via unix.Prlimit elsewhere. - _ = syscall.Setrlimit(syscall.RLIMIT_NOFILE, lim) -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go b/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go deleted file mode 100644 index 865d1802214..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build !go1.23 - -// TODO: remove this file once go 1.22 is no longer supported. - -package system - -import ( - "sync/atomic" - "syscall" - _ "unsafe" // Needed for go:linkname to work. -) - -//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile -var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit] - -// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. -// The argument is process RLIMIT_NOFILE values. -func ClearRlimitNofileCache(_ *syscall.Rlimit) { - // As reported in issue #4195, the new version of go runtime(since 1.19) - // will cache rlimit-nofile. Before executing execve, the rlimit-nofile - // of the process will be restored with the cache. In runc, this will - // cause the rlimit-nofile setting by the parent process for the container - // to become invalid. It can be solved by clearing this cache. But - // unfortunately, go stdlib doesn't provide such function, so we need to - // link to the private var `origRlimitNofile` in package syscall to hack. - syscallOrigRlimitNofile.Store(nil) -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go deleted file mode 100644 index a07afe07bc8..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go +++ /dev/null @@ -1,8 +0,0 @@ -package userns - -// RunningInUserNS detects whether we are currently running in a Linux -// user namespace and memoizes the result. It returns false on non-Linux -// platforms. -func RunningInUserNS() bool { - return inUserNS() -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go deleted file mode 100644 index a1462e13bc0..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go +++ /dev/null @@ -1,49 +0,0 @@ -package userns - -import ( - "bufio" - "fmt" - "os" - "sync" -) - -var inUserNS = sync.OnceValue(runningInUserNS) - -// runningInUserNS detects whether we are currently running in a user namespace. -// -// Originally copied from https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700. -func runningInUserNS() bool { - file, err := os.Open("/proc/self/uid_map") - if err != nil { - // This kernel-provided file only exists if user namespaces are supported. - return false - } - defer file.Close() - - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return false - } - - return uidMapInUserNS(string(l)) -} - -func uidMapInUserNS(uidMap string) bool { - if uidMap == "" { - // File exist but empty (the initial state when userns is created, - // see user_namespaces(7)). - return true - } - - var a, b, c int64 - if _, err := fmt.Sscanf(uidMap, "%d %d %d", &a, &b, &c); err != nil { - // Assume we are in a regular, non user namespace. - return false - } - - // As per user_namespaces(7), /proc/self/uid_map of - // the initial user namespace shows 0 0 4294967295. - initNS := a == 0 && b == 0 && c == 4294967295 - return !initNS -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux_fuzzer.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux_fuzzer.go deleted file mode 100644 index 26ba2e16ec4..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux_fuzzer.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build linux && gofuzz - -package userns - -func FuzzUIDMap(uidmap []byte) int { - _ = uidMapInUserNS(string(uidmap)) - return 1 -} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go deleted file mode 100644 index 8ed83072c23..00000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !linux - -package userns - -// inUserNS is a stub for non-Linux systems. Always returns false. -func inUserNS() bool { return false } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go index 1b523d8ac54..db420ea688d 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go @@ -1,13 +1,11 @@ package utils import ( - "encoding/binary" "encoding/json" "io" "os" "path/filepath" "strings" - "unsafe" "golang.org/x/sys/unix" ) @@ -16,20 +14,6 @@ const ( exitSignalOffset = 128 ) -// NativeEndian is the native byte order of the host system. -var NativeEndian binary.ByteOrder - -func init() { - // Copied from . - i := uint32(1) - b := (*[4]byte)(unsafe.Pointer(&i)) - if b[0] == 1 { - NativeEndian = binary.LittleEndian - } else { - NativeEndian = binary.BigEndian - } -} - // ExitStatus returns the correct exit status for a process based on if it // was signaled or exited cleanly func ExitStatus(status unix.WaitStatus) int { @@ -117,14 +101,14 @@ func SearchLabels(labels []string, key string) (string, bool) { func Annotations(labels []string) (bundle string, userAnnotations map[string]string) { userAnnotations = make(map[string]string) for _, l := range labels { - parts := strings.SplitN(l, "=", 2) - if len(parts) < 2 { + name, value, ok := strings.Cut(l, "=") + if !ok { continue } - if parts[0] == "bundle" { - bundle = parts[1] + if name == "bundle" { + bundle = value } else { - userAnnotations[parts[0]] = parts[1] + userAnnotations[name] = value } } return diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go index 1f3439b78fb..c8ad559d931 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go @@ -3,7 +3,6 @@ package utils import ( - "errors" "fmt" "math" "os" @@ -14,8 +13,6 @@ import ( "sync" _ "unsafe" // for go:linkname - "github.com/opencontainers/runc/libcontainer/system" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -299,83 +296,45 @@ func IsLexicallyInRoot(root, path string) bool { // This means that the path also must not contain ".." elements, otherwise an // error will occur. // -// This is a somewhat less safe alternative to -// , but it should -// detect attempts to trick us into creating directories outside of the root. -// We should migrate to securejoin.MkdirAll once it is merged. +// This uses securejoin.MkdirAllHandle under the hood, but it has special +// handling if unsafePath has already been scoped within the rootfs (this is +// needed for a lot of runc callers and fixing this would require reworking a +// lot of path logic). func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) { - // If the path is already "within" the root, use it verbatim. - fullPath := unsafePath - if !IsLexicallyInRoot(root, unsafePath) { - var err error - fullPath, err = securejoin.SecureJoin(root, unsafePath) + // If the path is already "within" the root, get the path relative to the + // root and use that as the unsafe path. This is necessary because a lot of + // MkdirAllInRootOpen callers have already done SecureJoin, and refactoring + // all of them to stop using these SecureJoin'd paths would require a fair + // amount of work. + // TODO(cyphar): Do the refactor to libpathrs once it's ready. + if IsLexicallyInRoot(root, unsafePath) { + subPath, err := filepath.Rel(root, unsafePath) if err != nil { return nil, err } - } - subPath, err := filepath.Rel(root, fullPath) - if err != nil { - return nil, err + unsafePath = subPath } // Check for any silly mode bits. if mode&^0o7777 != 0 { return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode) } + // Linux (and thus os.MkdirAll) silently ignores the suid and sgid bits if + // passed. While it would make sense to return an error in that case (since + // the user has asked for a mode that won't be applied), for compatibility + // reasons we have to ignore these bits. + if ignoredBits := mode &^ 0o1777; ignoredBits != 0 { + logrus.Warnf("MkdirAll called with no-op mode bits that are ignored by Linux: 0o%.3o", ignoredBits) + mode &= 0o1777 + } - currentDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + rootDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) if err != nil { return nil, fmt.Errorf("open root handle: %w", err) } - defer func() { - if Err != nil { - currentDir.Close() - } - }() - - for _, part := range strings.Split(subPath, string(filepath.Separator)) { - switch part { - case "", ".": - // Skip over no-op components. - continue - case "..": - return nil, fmt.Errorf("possible breakout detected: found %q component in SecureJoin subpath %s", part, subPath) - } - - nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - switch { - case err == nil: - // Update the currentDir. - _ = currentDir.Close() - currentDir = nextDir - - case errors.Is(err, unix.ENOTDIR): - // This might be a symlink or some other random file. Either way, - // error out. - return nil, fmt.Errorf("cannot mkdir in %s/%s: %w", currentDir.Name(), part, unix.ENOTDIR) - - case errors.Is(err, os.ErrNotExist): - // Luckily, mkdirat will not follow trailing symlinks, so this is - // safe to do as-is. - if err := system.Mkdirat(currentDir, part, mode); err != nil { - return nil, err - } - // Open the new directory. There is a race here where an attacker - // could swap the directory with a different directory, but - // MkdirAll's fuzzy semantics mean we don't care about that. - nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - if err != nil { - return nil, fmt.Errorf("open newly created directory: %w", err) - } - // Update the currentDir. - _ = currentDir.Close() - currentDir = nextDir + defer rootDir.Close() - default: - return nil, err - } - } - return currentDir, nil + return securejoin.MkdirAllHandle(rootDir, unsafePath, int(mode)) } // MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the @@ -387,3 +346,18 @@ func MkdirAllInRoot(root, unsafePath string, mode uint32) error { } return err } + +// Openat is a Go-friendly openat(2) wrapper. +func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { + dirFd := unix.AT_FDCWD + if dir != nil { + dirFd = int(dir.Fd()) + } + flags |= unix.O_CLOEXEC + + fd, err := unix.Openat(dirFd, path, flags, mode) + if err != nil { + return nil, &os.PathError{Op: "openat", Path: path, Err: err} + } + return os.NewFile(uintptr(fd), dir.Name()+"/"+path), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e06e5487d47..7275e6eaa62 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -553,7 +553,7 @@ github.com/opencontainers/go-digest ## explicit; go 1.18 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/opencontainers/runc v1.2.0-rc.3 +# github.com/opencontainers/runc v1.2.0 ## explicit; go 1.22 github.com/opencontainers/runc/libcontainer/apparmor github.com/opencontainers/runc/libcontainer/cgroups @@ -562,8 +562,6 @@ github.com/opencontainers/runc/libcontainer/cgroups/fs2 github.com/opencontainers/runc/libcontainer/cgroups/fscommon github.com/opencontainers/runc/libcontainer/configs github.com/opencontainers/runc/libcontainer/devices -github.com/opencontainers/runc/libcontainer/system -github.com/opencontainers/runc/libcontainer/userns github.com/opencontainers/runc/libcontainer/utils # github.com/opencontainers/runtime-spec v1.2.0 ## explicit