diff --git a/.gitignore b/.gitignore
index ac15d58..7668d0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/v1.0.0-rc92.tar.gz
+SOURCES/v1.0.0-rc93.tar.gz
diff --git a/.runc.metadata b/.runc.metadata
index 85883d8..0a7c604 100644
--- a/.runc.metadata
+++ b/.runc.metadata
@@ -1 +1 @@
-b5571f41bcc85be33a56122a30cb1a241476a8d1 SOURCES/v1.0.0-rc92.tar.gz
+e8693109441696536710e5751e0fee6e6fa32590 SOURCES/v1.0.0-rc93.tar.gz
diff --git a/SOURCES/rc93-0001-libct-newInitConfig-nit.patch b/SOURCES/rc93-0001-libct-newInitConfig-nit.patch
new file mode 100644
index 0000000..2040586
--- /dev/null
+++ b/SOURCES/rc93-0001-libct-newInitConfig-nit.patch
@@ -0,0 +1,40 @@
+From 28daf0653d324fad545a7031e64b6891f399969b Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Tue, 23 Feb 2021 17:58:07 -0800
+Subject: [PATCH 1/5] libct/newInitConfig: nit
+
+Move the initialization of Console* fields as they are unconditional.
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ libcontainer/container_linux.go | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
+index 3dca29e4c3f2..b6100aae9d5a 100644
+--- a/libcontainer/container_linux.go
++++ b/libcontainer/container_linux.go
+@@ -594,6 +594,9 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
+ 		AppArmorProfile:  c.config.AppArmorProfile,
+ 		ProcessLabel:     c.config.ProcessLabel,
+ 		Rlimits:          c.config.Rlimits,
++		CreateConsole:    process.ConsoleSocket != nil,
++		ConsoleWidth:     process.ConsoleWidth,
++		ConsoleHeight:    process.ConsoleHeight,
+ 	}
+ 	if process.NoNewPrivileges != nil {
+ 		cfg.NoNewPrivileges = *process.NoNewPrivileges
+@@ -607,9 +610,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
+ 	if len(process.Rlimits) > 0 {
+ 		cfg.Rlimits = process.Rlimits
+ 	}
+-	cfg.CreateConsole = process.ConsoleSocket != nil
+-	cfg.ConsoleWidth = process.ConsoleWidth
+-	cfg.ConsoleHeight = process.ConsoleHeight
++
+ 	return cfg
+ }
+ 
+-- 
+2.31.1
+
diff --git a/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch b/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch
new file mode 100644
index 0000000..3df9b15
--- /dev/null
+++ b/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch
@@ -0,0 +1,139 @@
+From 46ec7b5a94d370c4963ca361e9d96cb78d75d118 Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Tue, 23 Feb 2021 18:14:37 -0800
+Subject: [PATCH 2/5] libct/rootfs: introduce and use mountConfig
+
+The code is already passing three parameters around from
+mountToRootfs to mountCgroupV* to mountToRootfs again.
+
+I am about to add another parameter, so let's introduce and
+use struct mountConfig to pass around.
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ libcontainer/rootfs_linux.go | 42 ++++++++++++++++++++++--------------
+ 1 file changed, 26 insertions(+), 16 deletions(-)
+
+diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
+index 411496ab7c6d..a384abb7e8a5 100644
+--- a/libcontainer/rootfs_linux.go
++++ b/libcontainer/rootfs_linux.go
+@@ -29,6 +29,12 @@ import (
+ 
+ const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
+ 
++type mountConfig struct {
++	root     string
++	label    string
++	cgroupns bool
++}
++
+ // needsSetupDev returns true if /dev needs to be set up.
+ func needsSetupDev(config *configs.Config) bool {
+ 	for _, m := range config.Mounts {
+@@ -48,7 +54,11 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
+ 		return newSystemErrorWithCause(err, "preparing rootfs")
+ 	}
+ 
+-	hasCgroupns := config.Namespaces.Contains(configs.NEWCGROUP)
++	mountConfig := &mountConfig{
++		root:     config.Rootfs,
++		label:    config.MountLabel,
++		cgroupns: config.Namespaces.Contains(configs.NEWCGROUP),
++	}
+ 	setupDev := needsSetupDev(config)
+ 	for _, m := range config.Mounts {
+ 		for _, precmd := range m.PremountCmds {
+@@ -56,7 +66,7 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
+ 				return newSystemErrorWithCause(err, "running premount command")
+ 			}
+ 		}
+-		if err := mountToRootfs(m, config.Rootfs, config.MountLabel, hasCgroupns); err != nil {
++		if err := mountToRootfs(m, mountConfig); err != nil {
+ 			return newSystemErrorWithCausef(err, "mounting %q to rootfs at %q", m.Source, m.Destination)
+ 		}
+ 
+@@ -222,7 +232,7 @@ func prepareBindMount(m *configs.Mount, rootfs string) error {
+ 	return nil
+ }
+ 
+-func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error {
++func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
+ 	binds, err := getCgroupMounts(m)
+ 	if err != nil {
+ 		return err
+@@ -242,12 +252,12 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 		Data:             "mode=755",
+ 		PropagationFlags: m.PropagationFlags,
+ 	}
+-	if err := mountToRootfs(tmpfs, rootfs, mountLabel, enableCgroupns); err != nil {
++	if err := mountToRootfs(tmpfs, c); err != nil {
+ 		return err
+ 	}
+ 	for _, b := range binds {
+-		if enableCgroupns {
+-			subsystemPath := filepath.Join(rootfs, b.Destination)
++		if c.cgroupns {
++			subsystemPath := filepath.Join(c.root, b.Destination)
+ 			if err := os.MkdirAll(subsystemPath, 0755); err != nil {
+ 				return err
+ 			}
+@@ -266,7 +276,7 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 				return err
+ 			}
+ 		} else {
+-			if err := mountToRootfs(b, rootfs, mountLabel, enableCgroupns); err != nil {
++			if err := mountToRootfs(b, c); err != nil {
+ 				return err
+ 			}
+ 		}
+@@ -276,7 +286,7 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 			// symlink(2) is very dumb, it will just shove the path into
+ 			// the link and doesn't do any checks or relative path
+ 			// conversion. Also, don't error out if the cgroup already exists.
+-			if err := os.Symlink(mc, filepath.Join(rootfs, m.Destination, ss)); err != nil && !os.IsExist(err) {
++			if err := os.Symlink(mc, filepath.Join(c.root, m.Destination, ss)); err != nil && !os.IsExist(err) {
+ 				return err
+ 			}
+ 		}
+@@ -284,8 +294,8 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 	return nil
+ }
+ 
+-func mountCgroupV2(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error {
+-	cgroupPath, err := securejoin.SecureJoin(rootfs, m.Destination)
++func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
++	cgroupPath, err := securejoin.SecureJoin(c.root, m.Destination)
+ 	if err != nil {
+ 		return err
+ 	}
+@@ -302,10 +312,10 @@ func mountCgroupV2(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 	return nil
+ }
+ 
+-func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error {
+-	var (
+-		dest = m.Destination
+-	)
++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)
+ 	}
+@@ -424,9 +434,9 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
+ 		}
+ 	case "cgroup":
+ 		if cgroups.IsCgroup2UnifiedMode() {
+-			return mountCgroupV2(m, rootfs, mountLabel, enableCgroupns)
++			return mountCgroupV2(m, c)
+ 		}
+-		return mountCgroupV1(m, rootfs, mountLabel, enableCgroupns)
++		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.
+-- 
+2.31.1
+
diff --git a/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch b/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch
new file mode 100644
index 0000000..2ded660
--- /dev/null
+++ b/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch
@@ -0,0 +1,52 @@
+From 198a2806b0b5522cff1c53bf4671cfee85e45608 Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Tue, 23 Feb 2021 18:25:56 -0800
+Subject: [PATCH 3/5] libct/rootfs/mountCgroupV2: minor refactor
+
+1. s/cgroupPath/dest/
+
+2. don't hardcode /sys/fs/cgroup
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ libcontainer/rootfs_linux.go | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
+index a384abb7e8a5..0f0495b93b3e 100644
+--- a/libcontainer/rootfs_linux.go
++++ b/libcontainer/rootfs_linux.go
+@@ -17,6 +17,7 @@ import (
+ 	"github.com/moby/sys/mountinfo"
+ 	"github.com/mrunalp/fileutils"
+ 	"github.com/opencontainers/runc/libcontainer/cgroups"
++	"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
+ 	"github.com/opencontainers/runc/libcontainer/configs"
+ 	"github.com/opencontainers/runc/libcontainer/devices"
+ 	"github.com/opencontainers/runc/libcontainer/system"
+@@ -295,17 +296,18 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
+ }
+ 
+ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
+-	cgroupPath, err := securejoin.SecureJoin(c.root, m.Destination)
++	dest, err := securejoin.SecureJoin(c.root, m.Destination)
+ 	if err != nil {
+ 		return err
+ 	}
+-	if err := os.MkdirAll(cgroupPath, 0755); err != nil {
++	if err := os.MkdirAll(dest, 0755); err != nil {
+ 		return err
+ 	}
+-	if err := unix.Mount(m.Source, cgroupPath, "cgroup2", uintptr(m.Flags), m.Data); err != nil {
++	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 {
+-			return unix.Mount("/sys/fs/cgroup", cgroupPath, "", uintptr(m.Flags)|unix.MS_BIND, "")
++			src := fs2.UnifiedMountpoint
++			return unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "")
+ 		}
+ 		return err
+ 	}
+-- 
+2.31.1
+
diff --git a/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch b/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch
new file mode 100644
index 0000000..971ded9
--- /dev/null
+++ b/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch
@@ -0,0 +1,180 @@
+From ce352accdfb07a91b5527e70ec8bce658a8b68de Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Tue, 23 Feb 2021 18:27:42 -0800
+Subject: [PATCH 4/5] Fix cgroup2 mount for rootless case
+
+In case of rootless, cgroup2 mount is not possible (see [1] for more
+details), so since commit 9c81440fb5a7 runc bind-mounts the whole
+/sys/fs/cgroup into container.
+
+Problem is, if cgroupns is enabled, /sys/fs/cgroup inside the container
+is supposed to show the cgroup files for this cgroup, not the root one.
+
+The fix is to pass through and use the cgroup path in case cgroup2
+mount failed, cgroupns is enabled, and the path is non-empty.
+
+Surely this requires the /sys/fs/cgroup mount in the spec, so modify
+runc spec --rootless to keep it.
+
+Before:
+
+	$ ./runc run aaa
+	# find /sys/fs/cgroup/ -type d
+	/sys/fs/cgroup
+	/sys/fs/cgroup/user.slice
+	/sys/fs/cgroup/user.slice/user-1000.slice
+	/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service
+	...
+	# ls -l /sys/fs/cgroup/cgroup.controllers
+	-r--r--r--    1 nobody   nogroup          0 Feb 24 02:22 /sys/fs/cgroup/cgroup.controllers
+	# wc -w /sys/fs/cgroup/cgroup.procs
+	142 /sys/fs/cgroup/cgroup.procs
+	# cat /sys/fs/cgroup/memory.current
+	cat: can't open '/sys/fs/cgroup/memory.current': No such file or directory
+
+After:
+
+	# find /sys/fs/cgroup/ -type d
+	/sys/fs/cgroup/
+	# ls -l /sys/fs/cgroup/cgroup.controllers
+	-r--r--r--    1 root     root             0 Feb 24 02:43 /sys/fs/cgroup/cgroup.controllers
+	# wc -w /sys/fs/cgroup/cgroup.procs
+	2 /sys/fs/cgroup/cgroup.procs
+	# cat /sys/fs/cgroup/memory.current
+	577536
+
+[1] https://github.com/opencontainers/runc/issues/2158
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ libcontainer/container_linux.go  |  3 +++
+ libcontainer/init_linux.go       |  1 +
+ libcontainer/rootfs_linux.go     | 28 +++++++++++++++++++++-------
+ libcontainer/specconv/example.go | 18 +++++++++---------
+ 4 files changed, 34 insertions(+), 16 deletions(-)
+
+diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
+index b6100aae9d5a..1cbc734172d0 100644
+--- a/libcontainer/container_linux.go
++++ b/libcontainer/container_linux.go
+@@ -610,6 +610,9 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
+ 	if len(process.Rlimits) > 0 {
+ 		cfg.Rlimits = process.Rlimits
+ 	}
++	if cgroups.IsCgroup2UnifiedMode() {
++		cfg.Cgroup2Path = c.cgroupManager.Path("")
++	}
+ 
+ 	return cfg
+ }
+diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go
+index c57af0eebb8b..681797099f46 100644
+--- a/libcontainer/init_linux.go
++++ b/libcontainer/init_linux.go
+@@ -70,6 +70,7 @@ type initConfig struct {
+ 	RootlessEUID     bool                  `json:"rootless_euid,omitempty"`
+ 	RootlessCgroups  bool                  `json:"rootless_cgroups,omitempty"`
+ 	SpecState        *specs.State          `json:"spec_state,omitempty"`
++	Cgroup2Path      string                `json:"cgroup2_path,omitempty"`
+ }
+ 
+ type initer interface {
+diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
+index 0f0495b93b3e..5d2d74cf924b 100644
+--- a/libcontainer/rootfs_linux.go
++++ b/libcontainer/rootfs_linux.go
+@@ -31,9 +31,11 @@ import (
+ const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
+ 
+ type mountConfig struct {
+-	root     string
+-	label    string
+-	cgroupns bool
++	root            string
++	label           string
++	cgroup2Path     string
++	rootlessCgroups bool
++	cgroupns        bool
+ }
+ 
+ // needsSetupDev returns true if /dev needs to be set up.
+@@ -56,9 +58,11 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
+ 	}
+ 
+ 	mountConfig := &mountConfig{
+-		root:     config.Rootfs,
+-		label:    config.MountLabel,
+-		cgroupns: config.Namespaces.Contains(configs.NEWCGROUP),
++		root:            config.Rootfs,
++		label:           config.MountLabel,
++		cgroup2Path:     iConfig.Cgroup2Path,
++		rootlessCgroups: iConfig.RootlessCgroups,
++		cgroupns:        config.Namespaces.Contains(configs.NEWCGROUP),
+ 	}
+ 	setupDev := needsSetupDev(config)
+ 	for _, m := range config.Mounts {
+@@ -307,7 +311,17 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
+ 		// 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
+-			return unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "")
++			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 err
+ 		}
+ 		return err
+ 	}
+diff --git a/libcontainer/specconv/example.go b/libcontainer/specconv/example.go
+index 8a201bc78dd9..56bab3bfbfa5 100644
+--- a/libcontainer/specconv/example.go
++++ b/libcontainer/specconv/example.go
+@@ -2,6 +2,7 @@ package specconv
+ 
+ import (
+ 	"os"
++	"path/filepath"
+ 	"strings"
+ 
+ 	"github.com/opencontainers/runc/libcontainer/cgroups"
+@@ -200,8 +201,14 @@ func ToRootless(spec *specs.Spec) {
+ 	// Fix up mounts.
+ 	var mounts []specs.Mount
+ 	for _, mount := range spec.Mounts {
+-		// Ignore all mounts that are under /sys.
+-		if strings.HasPrefix(mount.Destination, "/sys") {
++		// Replace the /sys mount with an rbind.
++		if filepath.Clean(mount.Destination) == "/sys" {
++			mounts = append(mounts, specs.Mount{
++				Source:      "/sys",
++				Destination: "/sys",
++				Type:        "none",
++				Options:     []string{"rbind", "nosuid", "noexec", "nodev", "ro"},
++			})
+ 			continue
+ 		}
+ 
+@@ -216,13 +223,6 @@ func ToRootless(spec *specs.Spec) {
+ 		mount.Options = options
+ 		mounts = append(mounts, mount)
+ 	}
+-	// Add the sysfs mount as an rbind.
+-	mounts = append(mounts, specs.Mount{
+-		Source:      "/sys",
+-		Destination: "/sys",
+-		Type:        "none",
+-		Options:     []string{"rbind", "nosuid", "noexec", "nodev", "ro"},
+-	})
+ 	spec.Mounts = mounts
+ 
+ 	// Remove cgroup settings.
+-- 
+2.31.1
+
diff --git a/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch b/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch
new file mode 100644
index 0000000..6236232
--- /dev/null
+++ b/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch
@@ -0,0 +1,562 @@
+From 14faf1c20948688a48edb9b41367ab07ac11ca91 Mon Sep 17 00:00:00 2001
+From: Aleksa Sarai <cyphar@cyphar.com>
+Date: Thu, 1 Apr 2021 12:00:31 -0700
+Subject: [PATCH 5/5] rootfs: add mount destination validation
+
+Because the target of a mount is inside a container (which may be a
+volume that is shared with another container), there exists a race
+condition where the target of the mount may change to a path containing
+a symlink after we have sanitised the path -- resulting in us
+inadvertently mounting the path outside of the container.
+
+This is not immediately useful because we are in a mount namespace with
+MS_SLAVE mount propagation applied to "/", so we cannot mount on top of
+host paths in the host namespace. However, if any subsequent mountpoints
+in the configuration use a subdirectory of that host path as a source,
+those subsequent mounts will use an attacker-controlled source path
+(resolved within the host rootfs) -- allowing the bind-mounting of "/"
+into the container.
+
+While arguably configuration issues like this are not entirely within
+runc's threat model, within the context of Kubernetes (and possibly
+other container managers that provide semi-arbitrary container creation
+privileges to untrusted users) this is a legitimate issue. Since we
+cannot block mounting from the host into the container, we need to block
+the first stage of this attack (mounting onto a path outside the
+container).
+
+The long-term plan to solve this would be to migrate to libpathrs, but
+as a stop-gap we implement libpathrs-like path verification through
+readlink(/proc/self/fd/$n) and then do mount operations through the
+procfd once it's been verified to be inside the container. The target
+could move after we've checked it, but if it is inside the container
+then we can assume that it is safe for the same reason that libpathrs
+operations would be safe.
+
+A slight wrinkle is the "copyup" functionality we provide for tmpfs,
+which is the only case where we want to do a mount on the host
+filesystem. To facilitate this, I split out the copy-up functionality
+entirely so that the logic isn't interspersed with the regular tmpfs
+logic. In addition, all dependencies on m.Destination being overwritten
+have been removed since that pattern was just begging to be a source of
+more mount-target bugs (we do still have to modify m.Destination for
+tmpfs-copyup but we only do it temporarily).
+
+Fixes: CVE-2021-30465
+Reported-by: Etienne Champetier <champetier.etienne@gmail.com>
+Co-authored-by: Noah Meyerhans <nmeyerha@amazon.com>
+Reviewed-by: Samuel Karp <skarp@amazon.com>
+Reviewed-by: Kir Kolyshkin <kolyshkin@gmail.com> (@kolyshkin)
+Reviewed-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
+Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
+---
+ libcontainer/container_linux.go  |   1 -
+ libcontainer/rootfs_linux.go     | 251 +++++++++++++++----------------
+ libcontainer/utils/utils.go      |  54 +++++++
+ libcontainer/utils/utils_test.go |  35 +++++
+ 4 files changed, 213 insertions(+), 128 deletions(-)
+
+diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
+index 1cbc734172d0..70b388b1252e 100644
+--- a/libcontainer/container_linux.go
++++ b/libcontainer/container_linux.go
+@@ -1202,7 +1202,6 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error {
+ 		if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil {
+ 			return err
+ 		}
+-		m.Destination = dest
+ 		if err := os.MkdirAll(dest, 0755); err != nil {
+ 			return err
+ 		}
+diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
+index 5d2d74cf924b..96be669c365e 100644
+--- a/libcontainer/rootfs_linux.go
++++ b/libcontainer/rootfs_linux.go
+@@ -25,6 +25,7 @@ import (
+ 	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 @@ func prepareBindMount(m *configs.Mount, rootfs string) error {
+ 	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 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {
+ 			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 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
+ 	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 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error {
+ 		}
+ 		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 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error {
+ 		}
+ 		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 @@ func createDevices(config *configs.Config) error {
+ 	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 @@ func bindMountDeviceNode(dest string, node *devices.Device) error {
+ 	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 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
+ 		// 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
+ 	}
+@@ -1013,61 +1024,47 @@ func writeSystemProperty(key, value string) error {
+ }
+ 
+ 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 --git a/libcontainer/utils/utils.go b/libcontainer/utils/utils.go
+index 1b72b7a1c1ba..cd78f23e1bd0 100644
+--- a/libcontainer/utils/utils.go
++++ b/libcontainer/utils/utils.go
+@@ -3,12 +3,15 @@ package utils
+ 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 @@ func CleanPath(path string) string {
+ 	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 --git a/libcontainer/utils/utils_test.go b/libcontainer/utils/utils_test.go
+index 7f38ed169a6b..d33662238d36 100644
+--- a/libcontainer/utils/utils_test.go
++++ b/libcontainer/utils/utils_test.go
+@@ -143,3 +143,38 @@ func TestCleanPath(t *testing.T) {
+ 		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)
++		}
++	}
++}
+-- 
+2.31.1
+
diff --git a/SOURCES/runc-1947432.patch b/SOURCES/runc-1947432.patch
new file mode 100644
index 0000000..79ae82e
--- /dev/null
+++ b/SOURCES/runc-1947432.patch
@@ -0,0 +1,102 @@
+From a2050ea471b0eb3c7240282219773c0f1d7ec554 Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Wed, 7 Apr 2021 16:45:39 -0700
+Subject: [PATCH 1/2] runc run: fix start for rootless + host pidns
+
+Currently, runc fails like this when used from rootless podman
+with host PID namespace:
+
+> $ podman --runtime=runc run --pid=host --rm -it busybox sh
+> WARN[0000] additional gid=10 is not present in the user namespace, skip setting it
+> Error: container_linux.go:380: starting container process caused:
+> process_linux.go:545: container init caused: readonly path /proc/asound:
+> operation not permitted: OCI permission denied
+
+(Here /proc/asound is the first path from OCI spec's readonlyPaths).
+
+The code uses MS_BIND|MS_REMOUNT flags that have a special meaning in
+the kernel ("keep the flags like nodev, nosuid, noexec as is").
+For some reason, this "special meaning" trick is not working for the
+above use case (rootless podman + no PID namespace), and I don't know
+how to reproduce this without podman.
+
+Instead of relying on the kernel feature, let's just get the current
+mount flags using fstatfs(2) and add those that needs to be preserved.
+
+While at it, wrap errors from unix.Mount into os.PathError to make
+errors a bit less cryptic.
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ libcontainer/rootfs_linux.go | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
+index ed38e77219..3d67287926 100644
+--- a/libcontainer/rootfs_linux.go
++++ b/libcontainer/rootfs_linux.go
+@@ -931,9 +931,20 @@ func readonlyPath(path string) error {
+ 		if os.IsNotExist(err) {
+ 			return nil
+ 		}
+-		return err
++		return &os.PathError{Op: "bind-mount", Path: path, Err: err}
++	}
++
++	var s unix.Statfs_t
++	if err := unix.Statfs(path, &s); err != nil {
++		return &os.PathError{Op: "statfs", Path: path, Err: err}
+ 	}
+-	return unix.Mount(path, path, "", unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY|unix.MS_REC, "")
++	flags := uintptr(s.Flags) & (unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC)
++
++	if err := unix.Mount(path, path, "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil {
++		return &os.PathError{Op: "bind-mount-ro", Path: path, Err: err}
++	}
++
++	return nil
+ }
+ 
+ // remountReadonly will remount an existing mount point and ensure that it is read-only.
+
+From 31dd1e499b2f590cd3bcf59153491967ea2a8e1f Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kolyshkin@gmail.com>
+Date: Wed, 14 Apr 2021 10:58:42 -0700
+Subject: [PATCH 2/2] tests/int: add rootless + host pidns test case
+
+For the fix, see previous commit. Without the fix, this test case fails:
+
+> container_linux.go:380: starting container process caused:
+> process_linux.go:545: container init caused: readonly path /proc/bus:
+> operation not permitted
+
+Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
+---
+ tests/integration/start_hello.bats | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/tests/integration/start_hello.bats b/tests/integration/start_hello.bats
+index 5c2a66fdb1..858847032c 100644
+--- a/tests/integration/start_hello.bats
++++ b/tests/integration/start_hello.bats
+@@ -59,3 +59,20 @@ function teardown() {
+ 
+ 	[[ "$(cat pid.txt)" =~ [0-9]+ ]]
+ }
++
++# https://github.com/opencontainers/runc/pull/2897
++@test "runc run [rootless with host pidns]" {
++	requires rootless_no_features
++
++	# Remove pid namespace, and replace /proc mount
++	# with a bind mount from the host.
++	update_config '	  .linux.namespaces -= [{"type": "pid"}]
++			| .mounts |= map((select(.type == "proc")
++				| .type = "none"
++				| .source = "/proc"
++				| .options = ["rbind", "nosuid", "nodev", "noexec"]
++			  ) // .)'
++
++	runc run test_hello
++	[ "$status" -eq 0 ]
++}
diff --git a/SPECS/runc.spec b/SPECS/runc.spec
index 6e5f001..5f6e4f7 100644
--- a/SPECS/runc.spec
+++ b/SPECS/runc.spec
@@ -19,11 +19,11 @@ go build -buildmode pie -compiler gc -tags="rpm_crashtraceback libtrust_openssl 
 # https://github.com/opencontainers/runc
 %global import_path %{provider}.%{provider_tld}/%{project}/%{repo}
 %global git0 https://%{import_path}
-%global release_candidate rc92
+%global release_candidate rc93
 
 Name: %{repo}
 Version: 1.0.0
-Release: 70.%{release_candidate}%{?dist}
+Release: 73.%{release_candidate}%{?dist}
 Summary: CLI for running Open Containers
 # https://fedoraproject.org/wiki/PackagingDrafts/Go#Go_Language_Architectures
 #ExclusiveArch: %%{go_arches}
@@ -33,7 +33,14 @@ ExcludeArch: %{ix86}
 License: ASL 2.0
 URL: %{git0}
 Source0: %{git0}/archive/v1.0.0-%{release_candidate}.tar.gz
-#Patch0: 1807.patch
+Patch1: rc93-0001-libct-newInitConfig-nit.patch
+Patch2: rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch
+Patch3: rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch
+Patch4: rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch
+Patch5: rc93-0005-rootfs-add-mount-destination-validation.patch
+# related bug: https://bugzilla.redhat.com/show_bug.cgi?id=1947432
+# patch:       https://github.com/opencontainers/runc/pull/2897.patch
+Patch6: runc-1947432.patch
 Provides: oci-runtime = 1
 BuildRequires: golang >= 1.12.12-4
 BuildRequires: git
@@ -91,6 +98,18 @@ install -p -m 0644 contrib/completions/bash/%{name} %{buildroot}%{_datadir}/bash
 %{_datadir}/bash-completion/completions/%{name}
 
 %changelog
+* Thu May 13 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-73.rc93
+- fix "podman run --pid=host command causes OCI permission error"
+- Related: #1954702
+
+* Tue May 11 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-72.rc93
+- fix CVE-2021-30465
+- Related: #1954702
+
+* Thu Apr 29 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-71.rc93
+- upload rc93 tarball
+- Related: #1954702
+
 * Fri Jan 29 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-70.rc92
 - add missing Provides: oci-runtime = 1
 - Related: #1883490