From 78b4cdf24a75950da64dab9146984b448497cc28 Mon Sep 17 00:00:00 2001
From: xiadanni1 <xiadanni1@huawei.com>
Date: Wed, 15 Apr 2020 16:58:02 +0800
Subject: [PATCH] rootfs: do not permit /proc mounts to non-directories

mount(2) will blindly follow symlinks, which is a problem because it
allows a malicious container to trick runc into mounting /proc to an
entirely different location (and thus within the attacker's control for
a rename-exchange attack).

This is just a hotfix (to "stop the bleeding"), and the more complete
fix would be finish libpathrs and port runc to it (to avoid these types
of attacks entirely, and defend against a variety of other /proc-related
attacks). It can be bypased by someone having "/" be a volume controlled
by another container.

Fixes: CVE-2019-19921
Signed-off-by: Aleksa Sarai <asarai@suse.de>
Signed-off-by: xiadanni1 <xiadanni1@huawei.com>
---
 libcontainer/rootfs_linux.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
index 4c18482..67cf0bf 100644
--- a/libcontainer/rootfs_linux.go
+++ b/libcontainer/rootfs_linux.go
@@ -168,6 +168,18 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
 
 	switch m.Device {
 	case "proc", "sysfs":
+		// If the destination already exists and is not a directory, we bail
+		// out This is to avoid mounting through a symlink or similar -- which
+		// 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.
+		if fi, err := os.Lstat(dest); err != nil {
+			if !os.IsNotExist(err) {
+				return err
+			}
+		} else if fi.Mode()&os.ModeDir == 0 {
+			return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device)
+		}
 		if strings.HasPrefix(m.Destination, "/proc/sys/") {
 			return nil
 		}
-- 
1.8.3.1

