Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package runc for openSUSE:Factory checked in at 2021-05-20 19:23:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/runc (Old) and /work/SRC/openSUSE:Factory/.runc.new.2988 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "runc" Thu May 20 19:23:42 2021 rev:38 rq:894286 version:1.0.0~rc95 Changes: -------- --- /work/SRC/openSUSE:Factory/runc/runc.changes 2021-05-15 23:16:34.828644016 +0200 +++ /work/SRC/openSUSE:Factory/.runc.new.2988/runc.changes 2021-05-20 19:24:08.678169355 +0200 @@ -1,0 +2,10 @@ +Wed May 19 10:00:00 UTC 2021 - Aleksa Sarai <asa...@suse.com> + +- Update to runc v1.0.0~rc95. Upstream changelog is available from + https://github.com/opencontainers/runc/releases/tag/v1.0.0-rc95 + + This release of runc contains a fix for CVE-2021-30465, and users are + strongly recommended to update (especially if you are providing + semi-limited access to spawn containers to untrusted users). bsc#1185405 + +------------------------------------------------------------------- Old: ---- runc-1.0.0-rc94.tar.xz runc-1.0.0-rc94.tar.xz.asc New: ---- runc-1.0.0-rc95.tar.xz runc-1.0.0-rc95.tar.xz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ runc.spec ++++++ --- /var/tmp/diff_new_pack.RcspMh/_old 2021-05-20 19:24:09.114167510 +0200 +++ /var/tmp/diff_new_pack.RcspMh/_new 2021-05-20 19:24:09.118167493 +0200 @@ -25,8 +25,8 @@ %define project github.com/opencontainers/runc Name: runc -Version: 1.0.0~rc94 -%define _version 1.0.0-rc94 +Version: 1.0.0~rc95 +%define _version 1.0.0-rc95 Release: 0 Summary: Tool for spawning and running OCI containers License: Apache-2.0 ++++++ runc-1.0.0-rc94.tar.xz -> runc-1.0.0-rc95.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/.github/workflows/test.yml new/runc-1.0.0-rc95/.github/workflows/test.yml --- old/runc-1.0.0-rc94/.github/workflows/test.yml 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/.github/workflows/test.yml 2021-05-19 08:59:00.000000000 +0200 @@ -48,7 +48,7 @@ - name: install bats uses: mig4/setup-bats@v1 with: - bats-version: 1.2.1 + bats-version: 1.3.0 - name: unit test if: matrix.rootless != 'rootless' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/.github/workflows/validate.yml new/runc-1.0.0-rc95/.github/workflows/validate.yml --- old/runc-1.0.0-rc94/.github/workflows/validate.yml 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/.github/workflows/validate.yml 2021-05-19 08:59:00.000000000 +0200 @@ -30,7 +30,7 @@ - uses: actions/checkout@v2 - name: vars run: | - echo "VERSION=3.2.0" >> $GITHUB_ENV + echo "VERSION=3.2.4" >> $GITHUB_ENV echo "$(go env GOPATH)/bin" >> $GITHUB_PATH - name: cache go mod and $GOCACHE uses: actions/cache@v2 @@ -53,9 +53,9 @@ - uses: actions/checkout@v2 - name: vars run: | - echo 'VERSION=v0.7.1' >> $GITHUB_ENV + echo 'VERSION=v0.7.2' >> $GITHUB_ENV echo 'BASEURL=https://github.com/koalaman/shellcheck/releases/download' >> $GITHUB_ENV - echo 'SHA256SUM=1e8499d6f90481bfa2af7a4dce86dd9bcba5c0fbd88bf4793b34ec679c8bb98a' >> $GITHUB_ENV + echo 'SHA256SUM=12ee2e0b90a3d1e9cae24ac9b2838be66b48573cb2c8e8f3c566b959df6f050c' >> $GITHUB_ENV echo ~/bin >> $GITHUB_PATH - name: install shellcheck run: | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/Dockerfile new/runc-1.0.0-rc95/Dockerfile --- old/runc-1.0.0-rc94/Dockerfile 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/Dockerfile 2021-05-19 08:59:00.000000000 +0200 @@ -1,5 +1,5 @@ -ARG GO_VERSION=1.15 -ARG BATS_VERSION=v1.2.1 +ARG GO_VERSION=1.16 +ARG BATS_VERSION=v1.3.0 FROM golang:${GO_VERSION}-buster ARG DEBIAN_FRONTEND=noninteractive diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/EMERITUS.md new/runc-1.0.0-rc95/EMERITUS.md --- old/runc-1.0.0-rc94/EMERITUS.md 1970-01-01 01:00:00.000000000 +0100 +++ new/runc-1.0.0-rc95/EMERITUS.md 2021-05-19 08:59:00.000000000 +0200 @@ -0,0 +1,11 @@ +## Emeritus ## + +We would like to acknowledge previous runc maintainers and their huge +contributions to our collective success: + + * Alexander Morozov (@lk4d4) + * Andrei Vagin (@avagin) + * Rohit Jnagal (@rjnagal) + * Victor Marmol (@vmarmol) + +We thank these members for their service to the OCI community. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/VERSION new/runc-1.0.0-rc95/VERSION --- old/runc-1.0.0-rc94/VERSION 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/VERSION 2021-05-19 08:59:00.000000000 +0200 @@ -1 +1 @@ -1.0.0-rc94 +1.0.0-rc95 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/Vagrantfile.centos7 new/runc-1.0.0-rc95/Vagrantfile.centos7 --- old/runc-1.0.0-rc94/Vagrantfile.centos7 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/Vagrantfile.centos7 2021-05-19 08:59:00.000000000 +0200 @@ -15,8 +15,8 @@ set -e -u -o pipefail # configuration - GO_VERSION="1.15" - BATS_VERSION="v1.2.1" + GO_VERSION="1.16.4" + BATS_VERSION="v1.3.0" # install yum packages yum install -y -q epel-release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/configs/config.go new/runc-1.0.0-rc95/libcontainer/configs/config.go --- old/runc-1.0.0-rc94/libcontainer/configs/config.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/configs/config.go 2021-05-19 08:59:00.000000000 +0200 @@ -31,9 +31,10 @@ // for syscalls. Additional architectures can be added by specifying them in // Architectures. type Seccomp struct { - DefaultAction Action `json:"default_action"` - Architectures []string `json:"architectures"` - Syscalls []*Syscall `json:"syscalls"` + DefaultAction Action `json:"default_action"` + Architectures []string `json:"architectures"` + Syscalls []*Syscall `json:"syscalls"` + DefaultErrnoRet *uint `json:"default_errno_ret"` } // Action is taken upon rule match in Seccomp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/container_linux.go new/runc-1.0.0-rc95/libcontainer/container_linux.go --- old/runc-1.0.0-rc94/libcontainer/container_linux.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/container_linux.go 2021-05-19 08:59:00.000000000 +0200 @@ -1217,7 +1217,6 @@ if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil { return err } - m.Destination = dest if err := os.MkdirAll(dest, 0755); err != nil { return err } @@ -1257,13 +1256,16 @@ umounts := []string{} defer func() { for _, u := range umounts { - if e := unix.Unmount(u, unix.MNT_DETACH); e != nil { - if e != unix.EINVAL { - // Ignore EINVAL as it means 'target is not a mount point.' - // It probably has already been unmounted. - logrus.Warnf("Error during cleanup unmounting of %q (%v)", u, e) + _ = utils.WithProcfd(c.config.Rootfs, u, func(procfd string) error { + if e := unix.Unmount(procfd, unix.MNT_DETACH); e != nil { + if e != unix.EINVAL { + // Ignore EINVAL as it means 'target is not a mount point.' + // It probably has already been unmounted. + logrus.Warnf("Error during cleanup unmounting of %s (%s): %v", procfd, u, e) + } } - } + return nil + }) } }() for _, m := range mounts { @@ -1281,8 +1283,13 @@ // because during initial container creation mounts are // set up in the order they are configured. if m.Device == "bind" { - if err := unix.Mount(m.Source, m.Destination, "", unix.MS_BIND|unix.MS_REC, ""); err != nil { - return errorsf.Wrapf(err, "unable to bind mount %q to %q", m.Source, m.Destination) + if err := utils.WithProcfd(c.config.Rootfs, m.Destination, func(procfd string) error { + if err := unix.Mount(m.Source, procfd, "", unix.MS_BIND|unix.MS_REC, ""); err != nil { + return errorsf.Wrapf(err, "unable to bind mount %q to %q (through %q)", m.Source, m.Destination, procfd) + } + return nil + }); err != nil { + return err } umounts = append(umounts, m.Destination) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/integration/checkpoint_test.go new/runc-1.0.0-rc95/libcontainer/integration/checkpoint_test.go --- old/runc-1.0.0-rc94/libcontainer/integration/checkpoint_test.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/integration/checkpoint_test.go 2021-05-19 08:59:00.000000000 +0200 @@ -11,9 +11,11 @@ "testing" "github.com/opencontainers/runc/libcontainer" + "golang.org/x/sys/unix" ) func showFile(t *testing.T, fname string) error { + t.Helper() t.Logf("=== %s ===\n", fname) f, err := os.Open(fname) @@ -188,19 +190,14 @@ pid, err = restoreProcessConfig.Pid() ok(t, err) - _, err = os.FindProcess(pid) + err = unix.Kill(pid, 0) ok(t, err) _, err = restoreStdinW.WriteString("Hello!") ok(t, err) restoreStdinW.Close() - s, err := restoreProcessConfig.Wait() - ok(t, err) - - if !s.Success() { - t.Fatal(s.String(), pid) - } + waitProcess(restoreProcessConfig, t) output := restoreStdout.String() if !strings.Contains(output, "Hello!") { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/integration/utils_test.go new/runc-1.0.0-rc95/libcontainer/integration/utils_test.go --- old/runc-1.0.0-rc94/libcontainer/integration/utils_test.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/integration/utils_test.go 2021-05-19 08:59:00.000000000 +0200 @@ -80,22 +80,21 @@ // ok fails the test if an err is not nil. func ok(t testing.TB, err error) { + t.Helper() if err != nil { - _, file, line, _ := runtime.Caller(1) - t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error()) + t.Fatalf("unexpected error: %v", err) } } func waitProcess(p *libcontainer.Process, t *testing.T) { - _, file, line, _ := runtime.Caller(1) + t.Helper() status, err := p.Wait() - if err != nil { - t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error()) + t.Fatalf("unexpected error: %v", err) } if !status.Success() { - t.Fatalf("%s:%d: unexpected status: %s\n\n", filepath.Base(file), line, status.String()) + t.Fatalf("unexpected status: %v", status) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/rootfs_linux.go new/runc-1.0.0-rc95/libcontainer/rootfs_linux.go --- old/runc-1.0.0-rc94/libcontainer/rootfs_linux.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/rootfs_linux.go 2021-05-19 08:59:00.000000000 +0200 @@ -25,6 +25,7 @@ libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -228,8 +229,6 @@ if err := checkProcMount(rootfs, dest, m.Source); err != nil { return err } - // update the mount with the correct dest after symlinks are resolved. - m.Destination = dest if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } @@ -266,18 +265,21 @@ if err := os.MkdirAll(subsystemPath, 0755); err != nil { return err } - flags := defaultMountFlags - if m.Flags&unix.MS_RDONLY != 0 { - flags = flags | unix.MS_RDONLY - } - cgroupmount := &configs.Mount{ - Source: "cgroup", - Device: "cgroup", // this is actually fstype - Destination: subsystemPath, - Flags: flags, - Data: filepath.Base(subsystemPath), - } - if err := mountNewCgroup(cgroupmount); err != nil { + if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error { + flags := defaultMountFlags + if m.Flags&unix.MS_RDONLY != 0 { + flags = flags | unix.MS_RDONLY + } + var ( + source = "cgroup" + data = filepath.Base(subsystemPath) + ) + if data == "systemd" { + data = cgroups.CgroupNamePrefix + data + source = "systemd" + } + return unix.Mount(source, procfd, "cgroup", uintptr(flags), data) + }); err != nil { return err } } else { @@ -307,33 +309,79 @@ if err := os.MkdirAll(dest, 0755); err != nil { return err } - if err := unix.Mount(m.Source, dest, "cgroup2", uintptr(m.Flags), m.Data); err != nil { - // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) - if err == unix.EPERM || 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 = unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "") - if err == unix.ENOENT && c.rootlessCgroups { - err = nil + return utils.WithProcfd(c.root, m.Destination, func(procfd string) error { + if err := unix.Mount(m.Source, 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 err == unix.EPERM || 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 = unix.Mount(src, procfd, "", uintptr(m.Flags)|unix.MS_BIND, "") + if err == unix.ENOENT && c.rootlessCgroups { + err = nil + } } return err } + return nil + }) +} + +func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) { + // Set up a scratch dir for the tmpfs on the host. + tmpdir, err := prepareTmp("/tmp") + if err != nil { + return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir") + } + defer cleanupTmp(tmpdir) + tmpDir, err := ioutil.TempDir(tmpdir, "runctmpdir") + if err != nil { + return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") + } + defer os.RemoveAll(tmpDir) + + // Configure the *host* tmpdir as if it's the container mount. We change + // m.Destination since we are going to mount *on the host*. + oldDest := m.Destination + m.Destination = tmpDir + err = mountPropagate(m, "/", mountLabel) + m.Destination = oldDest + if err != nil { return err } - return nil + defer func() { + if Err != nil { + if err := unix.Unmount(tmpDir, unix.MNT_DETACH); err != nil { + logrus.Warnf("tmpcopyup: failed to unmount tmpdir on error: %v", err) + } + } + }() + + return utils.WithProcfd(rootfs, m.Destination, func(procfd string) (Err error) { + // Copy the container data to the host tmpdir. We append "/" to force + // CopyDirectory to resolve the symlink rather than trying to copy the + // symlink itself. + if err := fileutils.CopyDirectory(procfd+"/", tmpDir); err != nil { + return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, procfd, tmpDir, err) + } + // Now move the mount into the container. + if err := unix.Mount(tmpDir, procfd, "", unix.MS_MOVE, ""); err != nil { + return fmt.Errorf("tmpcopyup: failed to move mount %s to %s (%s): %w", tmpDir, procfd, m.Destination, err) + } + return nil + }) } func mountToRootfs(m *configs.Mount, c *mountConfig) error { rootfs := c.root mountLabel := c.label - dest := m.Destination - if !strings.HasPrefix(dest, rootfs) { - dest = filepath.Join(rootfs, dest) + dest, err := securejoin.SecureJoin(rootfs, m.Destination) + if err != nil { + return err } switch m.Device { @@ -364,53 +412,21 @@ } return label.SetFileLabel(dest, mountLabel) case "tmpfs": - copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP - tmpDir := "" - // dest might be an absolute symlink, so it needs - // to be resolved under rootfs. - dest, err := securejoin.SecureJoin(rootfs, m.Destination) - if err != nil { - return err - } - m.Destination = dest stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil { return err } } - if copyUp { - tmpdir, err := prepareTmp("/tmp") - if err != nil { - return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir") - } - defer cleanupTmp(tmpdir) - tmpDir, err = ioutil.TempDir(tmpdir, "runctmpdir") - if err != nil { - return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") - } - defer os.RemoveAll(tmpDir) - m.Destination = tmpDir + + if m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP { + err = doTmpfsCopyUp(m, rootfs, mountLabel) + } else { + err = mountPropagate(m, rootfs, mountLabel) } - if err := mountPropagate(m, rootfs, mountLabel); err != nil { + if err != nil { return err } - if copyUp { - if err := fileutils.CopyDirectory(dest, tmpDir); err != nil { - errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err) - if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil { - return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) - } - return errMsg - } - if err := unix.Mount(tmpDir, dest, "", unix.MS_MOVE, ""); err != nil { - errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) - if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil { - return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) - } - return errMsg - } - } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err @@ -454,19 +470,9 @@ } return mountCgroupV1(m, c) default: - // ensure that the destination of the mount is resolved of symlinks at mount time because - // any previous mounts can invalidate the next mount's destination. - // this can happen when a user specifies mounts within other mounts to cause breakouts or other - // evil stuff to try to escape the container's rootfs. - var err error - if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { - return err - } if err := checkProcMount(rootfs, dest, m.Source); err != nil { return err } - // update the mount with the correct dest after symlinks are resolved. - m.Destination = dest if err := os.MkdirAll(dest, 0755); err != nil { return err } @@ -649,7 +655,7 @@ return nil } -func bindMountDeviceNode(dest string, node *devices.Device) error { +func bindMountDeviceNode(rootfs, dest string, node *devices.Device) error { f, err := os.Create(dest) if err != nil && !os.IsExist(err) { return err @@ -657,7 +663,9 @@ if f != nil { f.Close() } - return unix.Mount(node.Path, dest, "bind", unix.MS_BIND, "") + return utils.WithProcfd(rootfs, dest, func(procfd string) error { + return unix.Mount(node.Path, procfd, "bind", unix.MS_BIND, "") + }) } // Creates the device node in the rootfs of the container. @@ -666,18 +674,21 @@ // The node only exists for cgroup reasons, ignore it here. return nil } - dest := filepath.Join(rootfs, node.Path) + dest, err := securejoin.SecureJoin(rootfs, node.Path) + if err != nil { + return err + } if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { return err } if bind { - return bindMountDeviceNode(dest, node) + return bindMountDeviceNode(rootfs, dest, node) } if err := mknodDevice(dest, node); err != nil { if os.IsExist(err) { return nil } else if os.IsPermission(err) { - return bindMountDeviceNode(dest, node) + return bindMountDeviceNode(rootfs, dest, node) } return err } @@ -1024,61 +1035,47 @@ } func remount(m *configs.Mount, rootfs string) error { - var ( - dest = m.Destination - ) - if !strings.HasPrefix(dest, rootfs) { - dest = filepath.Join(rootfs, dest) - } - return unix.Mount(m.Source, dest, m.Device, uintptr(m.Flags|unix.MS_REMOUNT), "") + return utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { + return unix.Mount(m.Source, procfd, m.Device, uintptr(m.Flags|unix.MS_REMOUNT), "") + }) } // Do the mount operation followed by additional mounts required to take care -// of propagation flags. +// of propagation flags. This will always be scoped inside the container rootfs. func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error { var ( - dest = m.Destination data = label.FormatMountLabel(m.Data, mountLabel) flags = m.Flags ) - if libcontainerUtils.CleanPath(dest) == "/dev" { - flags &= ^unix.MS_RDONLY - } - - // Mount it rw to allow chmod operation. A remount will be performed - // later to make it ro if set. - if m.Device == "tmpfs" { + // Delay mounting the filesystem read-only if we need to do further + // operations on it. We need to set up files in "/dev" and tmpfs mounts may + // need to be chmod-ed after mounting. The mount will be remounted ro later + // in finalizeRootfs() if necessary. + if libcontainerUtils.CleanPath(m.Destination) == "/dev" || m.Device == "tmpfs" { flags &= ^unix.MS_RDONLY } - copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP - if !(copyUp || strings.HasPrefix(dest, rootfs)) { - dest = filepath.Join(rootfs, dest) - } - - if err := unix.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { - return err - } - - for _, pflag := range m.PropagationFlags { - if err := unix.Mount("", dest, "", uintptr(pflag), ""); err != nil { - return err + // Because the destination is inside a container path which might be + // mutating underneath us, we verify that we are actually going to mount + // inside the container with WithProcfd() -- mounting through a procfd + // mounts on the target. + if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { + return unix.Mount(m.Source, procfd, m.Device, uintptr(flags), data) + }); err != nil { + return fmt.Errorf("mount through procfd: %w", err) + } + // We have to apply mount propagation flags in a separate WithProcfd() call + // because the previous call invalidates the passed procfd -- the mount + // target needs to be re-opened. + if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { + for _, pflag := range m.PropagationFlags { + if err := unix.Mount("", procfd, "", uintptr(pflag), ""); err != nil { + return err + } } - } - return nil -} - -func mountNewCgroup(m *configs.Mount) error { - var ( - data = m.Data - source = m.Source - ) - if data == "systemd" { - data = cgroups.CgroupNamePrefix + data - source = "systemd" - } - if err := unix.Mount(source, m.Destination, m.Device, uintptr(m.Flags), data); err != nil { - return err + return nil + }); err != nil { + return fmt.Errorf("change mount propagation through procfd: %w", err) } return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/seccomp/patchbpf/enosys_linux.go new/runc-1.0.0-rc95/libcontainer/seccomp/patchbpf/enosys_linux.go --- old/runc-1.0.0-rc94/libcontainer/seccomp/patchbpf/enosys_linux.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/seccomp/patchbpf/enosys_linux.go 2021-05-19 08:59:00.000000000 +0200 @@ -523,6 +523,11 @@ } func generatePatch(config *configs.Seccomp) ([]bpf.Instruction, error) { + // Patch the generated cBPF only when there is not a defaultErrnoRet set + // and it is different from ENOSYS + if config.DefaultErrnoRet != nil && *config.DefaultErrnoRet == uint(retErrnoEnosys) { + return nil, nil + } // We only add the stub if the default action is not permissive. if isAllowAction(config.DefaultAction) { logrus.Debugf("seccomp: skipping -ENOSYS stub filter generation") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/seccomp/seccomp_linux.go new/runc-1.0.0-rc95/libcontainer/seccomp/seccomp_linux.go --- old/runc-1.0.0-rc94/libcontainer/seccomp/seccomp_linux.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/seccomp/seccomp_linux.go 2021-05-19 08:59:00.000000000 +0200 @@ -36,7 +36,7 @@ return errors.New("cannot initialize Seccomp - nil config passed") } - defaultAction, err := getAction(config.DefaultAction, nil) + defaultAction, err := getAction(config.DefaultAction, config.DefaultErrnoRet) if err != nil { return errors.New("error initializing seccomp - invalid default action") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/specconv/spec_linux.go new/runc-1.0.0-rc95/libcontainer/specconv/spec_linux.go --- old/runc-1.0.0-rc94/libcontainer/specconv/spec_linux.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/specconv/spec_linux.go 2021-05-19 08:59:00.000000000 +0200 @@ -878,6 +878,7 @@ return nil, err } newConfig.DefaultAction = newDefaultAction + newConfig.DefaultErrnoRet = config.DefaultErrnoRet // Loop through all syscall blocks and convert them to libcontainer format for _, call := range config.Syscalls { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/utils/utils.go new/runc-1.0.0-rc95/libcontainer/utils/utils.go --- old/runc-1.0.0-rc94/libcontainer/utils/utils.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/utils/utils.go 2021-05-19 08:59:00.000000000 +0200 @@ -3,12 +3,15 @@ import ( "encoding/binary" "encoding/json" + "fmt" "io" "os" "path/filepath" + "strconv" "strings" "unsafe" + "github.com/cyphar/filepath-securejoin" "golang.org/x/sys/unix" ) @@ -88,6 +91,57 @@ return filepath.Clean(path) } +// stripRoot returns the passed path, stripping the root path if it was +// (lexicially) inside it. Note that both passed paths will always be treated +// as absolute, and the returned path will also always be absolute. In +// addition, the paths are cleaned before stripping the root. +func stripRoot(root, path string) string { + // Make the paths clean and absolute. + root, path = CleanPath("/"+root), CleanPath("/"+path) + switch { + case path == root: + path = "/" + case root == "/": + // do nothing + case strings.HasPrefix(path, root+"/"): + path = strings.TrimPrefix(path, root+"/") + } + return CleanPath("/" + path) +} + +// WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) +// corresponding to the unsafePath resolved within the root. Before passing the +// fd, this path is verified to have been inside the root -- so operating on it +// through the passed fdpath should be safe. Do not access this path through +// the original path strings, and do not attempt to use the pathname outside of +// the passed closure (the file handle will be freed once the closure returns). +func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { + // Remove the root then forcefully resolve inside the root. + unsafePath = stripRoot(root, unsafePath) + path, err := securejoin.SecureJoin(root, unsafePath) + if err != nil { + return fmt.Errorf("resolving path inside rootfs failed: %v", err) + } + + // Open the target path. + fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) + if err != nil { + return fmt.Errorf("open o_path procfd: %w", err) + } + defer fh.Close() + + // Double-check the path is the one we expected. + procfd := "/proc/self/fd/" + strconv.Itoa(int(fh.Fd())) + if realpath, err := os.Readlink(procfd); err != nil { + return fmt.Errorf("procfd verification failed: %w", err) + } else if realpath != path { + return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath) + } + + // Run the closure. + return fn(procfd) +} + // SearchLabels searches a list of key-value pairs for the provided key and // returns the corresponding value. The pairs must be separated with '='. func SearchLabels(labels []string, query string) string { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/libcontainer/utils/utils_test.go new/runc-1.0.0-rc95/libcontainer/utils/utils_test.go --- old/runc-1.0.0-rc94/libcontainer/utils/utils_test.go 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/libcontainer/utils/utils_test.go 2021-05-19 08:59:00.000000000 +0200 @@ -143,3 +143,38 @@ t.Errorf("expected to receive '/foo' and received %s", path) } } + +func TestStripRoot(t *testing.T) { + for _, test := range []struct { + root, path, out string + }{ + // Works with multiple components. + {"/a/b", "/a/b/c", "/c"}, + {"/hello/world", "/hello/world/the/quick-brown/fox", "/the/quick-brown/fox"}, + // '/' must be a no-op. + {"/", "/a/b/c", "/a/b/c"}, + // Must be the correct order. + {"/a/b", "/a/c/b", "/a/c/b"}, + // Must be at start. + {"/abc/def", "/foo/abc/def/bar", "/foo/abc/def/bar"}, + // Must be a lexical parent. + {"/foo/bar", "/foo/barSAMECOMPONENT", "/foo/barSAMECOMPONENT"}, + // Must only strip the root once. + {"/foo/bar", "/foo/bar/foo/bar/baz", "/foo/bar/baz"}, + // Deal with .. in a fairly sane way. + {"/foo/bar", "/foo/bar/../baz", "/foo/baz"}, + {"/foo/bar", "../../../../../../foo/bar/baz", "/baz"}, + {"/foo/bar", "/../../../../../../foo/bar/baz", "/baz"}, + {"/foo/bar/../baz", "/foo/baz/bar", "/bar"}, + {"/foo/bar/../baz", "/foo/baz/../bar/../baz/./foo", "/foo"}, + // All paths are made absolute before stripping. + {"foo/bar", "/foo/bar/baz/bee", "/baz/bee"}, + {"/foo/bar", "foo/bar/baz/beef", "/baz/beef"}, + {"foo/bar", "foo/bar/baz/beets", "/baz/beets"}, + } { + got := stripRoot(test.root, test.path) + if got != test.out { + t.Errorf("stripRoot(%q, %q) -- got %q, expected %q", test.root, test.path, got, test.out) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/tests/integration/seccomp.bats new/runc-1.0.0-rc95/tests/integration/seccomp.bats --- old/runc-1.0.0-rc94/tests/integration/seccomp.bats 2021-05-10 03:17:34.000000000 +0200 +++ new/runc-1.0.0-rc95/tests/integration/seccomp.bats 2021-05-19 08:59:00.000000000 +0200 @@ -21,3 +21,15 @@ runc run test_busybox [ "$status" -eq 0 ] } + +@test "runc run [seccomp defaultErrnoRet=ENXIO]" { + TEST_NAME="seccomp_syscall_test2" + + # Compile the test binary and update the config to run it. + gcc -static -o rootfs/seccomp_test2 "${TESTDATA}/${TEST_NAME}.c" + update_config ".linux.seccomp = $(<"${TESTDATA}/${TEST_NAME}.json")" + update_config '.process.args = ["/seccomp_test2"]' + + runc run test_busybox + [ "$status" -eq 0 ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/tests/integration/testdata/seccomp_syscall_test2.c new/runc-1.0.0-rc95/tests/integration/testdata/seccomp_syscall_test2.c --- old/runc-1.0.0-rc94/tests/integration/testdata/seccomp_syscall_test2.c 1970-01-01 01:00:00.000000000 +0100 +++ new/runc-1.0.0-rc95/tests/integration/testdata/seccomp_syscall_test2.c 2021-05-19 08:59:00.000000000 +0200 @@ -0,0 +1,12 @@ +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +int main() +{ + if (chdir("/") < 0 && errno == ENXIO) + exit(EXIT_SUCCESS); + fprintf(stderr, "got errno=%m\n"); + exit(EXIT_FAILURE); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/runc-1.0.0-rc94/tests/integration/testdata/seccomp_syscall_test2.json new/runc-1.0.0-rc95/tests/integration/testdata/seccomp_syscall_test2.json --- old/runc-1.0.0-rc94/tests/integration/testdata/seccomp_syscall_test2.json 1970-01-01 01:00:00.000000000 +0100 +++ new/runc-1.0.0-rc95/tests/integration/testdata/seccomp_syscall_test2.json 2021-05-19 08:59:00.000000000 +0200 @@ -0,0 +1,356 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 6, + "architectures": [ + "SCMP_ARCH_X86", + "SCMP_ARCH_X32", + "SCMP_ARCH_X86_64", + "SCMP_ARCH_AARCH64", + "SCMP_ARCH_ARM" + ], + "syscalls": [ + { + "action": "SCMP_ACT_ALLOW", + "names": [ + "accept", + "accept4", + "access", + "adjtimex", + "alarm", + "arch_prctl", + "bind", + "brk", + "capget", + "capset", + "chmod", + "chown", + "chown32", + "clock_adjtime", + "clock_adjtime64", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "close", + "connect", + "copy_file_range", + "creat", + "dup", + "dup2", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsetxattr", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_time64", + "futimesat", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "get_robust_list", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "get_thread_area", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "ioctl", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "ioprio_get", + "ioprio_set", + "io_setup", + "io_submit", + "io_uring_enter", + "io_uring_register", + "io_uring_setup", + "ipc", + "kill", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "_llseek", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "membarrier", + "memfd_create", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "modify_ldt", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "nanosleep", + "newfstatat", + "_newselect", + "open", + "openat", + "openat2", + "pause", + "pidfd_open", + "pidfd_send_signal", + "pipe", + "pipe2", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rseq", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "set_robust_list", + "setsid", + "setsockopt", + "set_thread_area", + "set_tid_address", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "syncfs", + "sysinfo", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev" + ] + } + ] +} +