Change:
opencontainers/runc has fixed three CVEs: Latest runc is pulled by k/k Check runc version in file No response n/a n/a <details</details<details</details<details</details<details</details
/assign /sig security architecture release
/triage accepted

Discussion:
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/onsi/ginkgo/v2 v2.9.1 github.com/onsi/gomega v1.27.4 <del> github.com/opencontainers/runc v1.1.4 </del> <ins> github.com/opencontainers/runc v1.1.5 </ins> github.com/opencontainers/selinux v1.10.0 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0

github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= <del> github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= </del> github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= <ins> github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= </ins> github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=

// rules separately to systemd) we can safely skip entries that don't // have a corresponding path. if _, err := os.Stat(entry.Path); err != nil { <del> logrus.Debugf("skipping device %s for systemd: %s", entry.Path, err) continue </del> <ins> // Also check /sys/dev so that we don't depend on /dev/{block,char} // being populated. (/dev/{block,char} is populated by udev, which // isn't strictly required for systemd). Ironically, this happens most // easily when starting containerd within a runc created container // itself. // We don't bother with securejoin here because we create entry.Path // right above here, so we know it's safe. if _, err := os.Stat("/sys" + entry.Path); err != nil { logrus.Warnf("skipping device %s for systemd: %s", entry.Path, err) continue } </ins> } } deviceAllowList = append(deviceAllowList, entry)

return &os.PathError{Op: "fstat", Path: file.Name(), Err: err} } <del> // Skip chown if uid is already the one we want. if int(s.Uid) == u.Uid { </del> <ins> // Skip chown if uid is already the one we want or any of the STDIO descriptors // were redirected to /dev/null. if int(s.Uid) == u.Uid || s.Rdev == null.Rdev { </ins> continue }

if err := os.MkdirAll(dest, 0o755); err != nil { return err } <del> return utils.WithProcfd(c.root, m.Destination, func(procfd string) error { if err := mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data); err != nil { // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY) { src := fs2.UnifiedMountpoint if c.cgroupns && c.cgroup2Path != "" { // Emulate cgroupns by bind-mounting // the container cgroup path rather than // the whole /sys/fs/cgroup. src = c.cgroup2Path } err = mount(src, m.Destination, procfd, "", uintptr(m.Flags)|unix.MS_BIND, "") if c.rootlessCgroups && errors.Is(err, unix.ENOENT) { err = nil } } return err } return nil </del> <ins> err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error { return mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data) </ins> }) <ins> if err == nil || !(errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY)) { return err } // When we are in UserNS but CgroupNS is not unshared, we cannot mount // cgroup2 (#2158), so fall back to bind mount. bindM := &configs.Mount{ Device:           "bind", Source:           fs2.UnifiedMountpoint, Destination:      m.Destination, Flags:            unix.MS_BIND | m.Flags, PropagationFlags: m.PropagationFlags, } if c.cgroupns && c.cgroup2Path != "" { // Emulate cgroupns by bind-mounting the container cgroup path // rather than the whole /sys/fs/cgroup. bindM.Source = c.cgroup2Path } // mountToRootfs() handles remounting for MS_RDONLY. // No need to set c.fd here, because mountToRootfs() calls utils.WithProcfd() by itself in mountPropagate(). err = mountToRootfs(bindM, c) if c.rootlessCgroups && errors.Is(err, unix.ENOENT) { // ENOENT (for `src = c.cgroup2Path`) happens when rootless runc is being executed // outside the userns+mountns. // // Mask `/sys/fs/cgroup` to ensure it is read-only, even when `/sys` is mounted // with `rbind,ro` (`runc spec --rootless` produces `rbind,ro` for `/sys`). err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error { return maskPath(procfd, c.label) }) } return err </ins> } func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) {

func mountToRootfs(m *configs.Mount, c *mountConfig) error { rootfs := c.root <del> mountLabel := c.label mountFd := c.fd dest, err := securejoin.SecureJoin(rootfs, m.Destination) if err != nil { return err } </del> <ins> // procfs and sysfs are special because we need to ensure they are actually // mounted on a specific path in a container without any funny business. </ins> switch m.Device { case "proc", "sysfs": // If the destination already exists and is not a directory, we bail <del> // out This is to avoid mounting through a symlink or similar -- which </del> <ins> // out. This is to avoid mounting through a symlink or similar -- which </ins> // has been a "fun" attack scenario in the past. // TODO: This won't be necessary once we switch to libpathrs and we can //       stop all of these symlink-exchange attacks. <ins> dest := filepath.Clean(m.Destination) if !strings.HasPrefix(dest, rootfs) { // Do not use securejoin as it resolves symlinks. dest = filepath.Join(rootfs, dest) } </ins> if fi, err := os.Lstat(dest); err != nil { if !os.IsNotExist(err) { return err } <del> } else if fi.Mode()&os.ModeDir == 0 { </del> <ins> } else if !fi.IsDir() { </ins> return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device) } if err := os.MkdirAll(dest, 0o755); err != nil { return err } <del> // Selinux kernels do not support labeling of /proc or /sys </del> <ins> // Selinux kernels do not support labeling of /proc or /sys. </ins> return mountPropagate(m, rootfs, "", nil) <ins> } mountLabel := c.label mountFd := c.fd dest, err := securejoin.SecureJoin(rootfs, m.Destination) if err != nil { return err } switch m.Device { </ins> case "mqueue": if err := os.MkdirAll(dest, 0o755); err != nil { return err

# github.com/opencontainers/go-digest v1.0.0 ## explicit; go 1.13 github.com/opencontainers/go-digest <del> # github.com/opencontainers/runc v1.1.4 </del> <ins> # github.com/opencontainers/runc v1.1.5 </ins> ## explicit; go 1.16 github.com/opencontainers/runc/libcontainer github.com/opencontainers/runc/libcontainer/apparmor