From 53ca847c8d21b1e422745a221d49ddf61679d4dd Mon Sep 17 00:00:00 2001
From: lifeng68 <lifeng68@huawei.com>
Date: Fri, 27 Nov 2020 16:02:25 +0800
Subject: [PATCH] mount: make possible to bind mount /proc and /sys/fs

1. add check whether have /proc mounts entry, if has, skip the auto
mount proc
2. mount cgroup before do mount entrys
3. pass if the mount on top of /proc and the source of the mount is a proc filesystem

Signed-off-by: lifeng68 <lifeng68@huawei.com>
---
 src/lxc/conf.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++---
 src/lxc/path.c |  2 +-
 2 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index ce5bab9c5..c3610ae33 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2581,7 +2581,7 @@ retry:
 
 /* isulad: checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
  * dest is required to be an abs path and have any symlinks resolved before calling this function. */
-static int check_mount_destination(const char *rootfs, const char *dest)
+static int check_mount_destination(const char *rootfs, const char *dest, const char *src)
 {
 	const char *invalid_destinations[] = {
 		"/proc",
@@ -2641,10 +2641,28 @@ static int check_mount_destination(const char *rootfs, const char *dest)
 			return -1;
 		}
 		relpath = path_relative(fullpath, dest);
+		DEBUG("dst path %s get relative path %s with full path %s,src:%s", dest, relpath, fullpath, src);
 		free(fullpath);
-		if (!relpath)
+		if (!relpath) {
+			ERROR("Failed to get relpath for %s related to %s", dest, fullpath);
 			return -1;
-		if (!strcmp(relpath, ".") || strncmp(relpath, "..", 2)) {
+		}
+		// pass if the mount path is outside of invalid proc
+		if (strncmp(relpath, "..", 2) == 0) {
+			free(relpath);
+			continue;
+		}
+		if (strcmp(relpath, ".") == 0) {
+			if (src == NULL) {
+				free(relpath);
+				continue;
+			}
+			// pass if the mount on top of /proc and the source of the mount is a proc filesystem
+			if (has_fs_type(src, PROC_SUPER_MAGIC)) {
+				WARN("src %s is proc allow mount on-top of %s", src, *invalid);
+				free(relpath);
+				continue;
+			}
 			ERROR("%s cannot be mounted because it is located inside %s", dest, *invalid);
 			free(relpath);
 			return -1;
@@ -2706,7 +2724,7 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
 		}
 		dest = rpath;
 
-		ret = check_mount_destination(rootfs_path, dest);
+		ret = check_mount_destination(rootfs_path, dest, mntent->mnt_fsname);
 		if (ret) {
 			ERROR("Mount destination is invalid: '%s'", dest);
 			lxc_write_error_message(rootfs->errfd, "%s:%d: mount destination is invalid: '%s'.",
@@ -3119,6 +3137,52 @@ static bool need_setup_dev(const struct lxc_conf *conf, struct lxc_list *mount)
 		return true;
 	}
 }
+
+static bool have_proc_bind_mount_entry(FILE *file)
+{
+	bool have_bind_proc = false;
+	char buf[PATH_MAX] = { 0 };
+	struct mntent mntent;
+
+	while (getmntent_r(file, &mntent, buf, sizeof(buf))) {
+		mntent.mnt_dir = lxc_string_replace(SPACE_MAGIC_STR, " ", mntent.mnt_dir);
+		if(mntent.mnt_dir == NULL) {
+			SYSERROR("memory allocation error");
+			continue;
+		}
+
+		DEBUG("parsed mnt %s, %s, %s", mntent.mnt_fsname, mntent.mnt_dir, mntent.mnt_type);
+
+		if (strcmp(mntent.mnt_dir, "proc") == 0 && strcmp(mntent.mnt_type, "bind") == 0) {
+			have_bind_proc = true;
+		}
+
+		free(mntent.mnt_dir);
+		mntent.mnt_dir = NULL;
+
+		if (have_bind_proc)
+			return true;
+	}
+
+	return false;
+}
+
+// returns true if /proc needs to be set up.
+static bool need_setup_proc(const struct lxc_conf *conf, struct lxc_list *mount)
+{
+	__do_fclose FILE *f = NULL;
+
+	f = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
+	if (f == NULL)
+		return true;
+
+	if (have_proc_bind_mount_entry(f)) {
+		return false;
+	} else {
+		return true;
+	}
+}
+
 #endif
 
 static int parse_cap(const char *cap)
@@ -4870,6 +4934,7 @@ int lxc_setup(struct lxc_handler *handler)
 	char *keyring_context = NULL;
 #ifdef HAVE_ISULAD
 	bool setup_dev = true;
+	bool setup_proc = true;
 #endif
 
 	ret = lxc_setup_rootfs_prepare_root(lxc_conf, name, lxcpath);
@@ -4930,6 +4995,17 @@ int lxc_setup(struct lxc_handler *handler)
 	ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler);
 	if (ret < 0)
 		return log_error(-1, "Failed to setup first automatic mounts");
+
+#ifdef HAVE_ISULAD
+	/* Now mount only cgroups, if wanted. Before, /sys could not have been
+	 * mounted. It is guaranteed to be mounted now either through
+	 * automatically or via fstab entries.
+	 */
+	ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler);
+	if (ret < 0)
+		return log_error(-1, "Failed to setup remaining automatic mounts");
+#endif
+
 #ifdef HAVE_ISULAD
 	ret = setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath, lxc_conf->lsm_se_mount_context);
 #else
@@ -4950,6 +5026,7 @@ int lxc_setup(struct lxc_handler *handler)
 			return log_error(-1, "Failed to setup mount entries");
 #ifdef HAVE_ISULAD
 		setup_dev = need_setup_dev(lxc_conf, &lxc_conf->mount_list);
+		setup_proc = need_setup_proc(lxc_conf, &lxc_conf->mount_list);
 #endif
 	}
 
@@ -4975,6 +5052,7 @@ int lxc_setup(struct lxc_handler *handler)
 		}
 	}
 
+#ifndef HAVE_ISULAD
 	/* Now mount only cgroups, if wanted. Before, /sys could not have been
 	 * mounted. It is guaranteed to be mounted now either through
 	 * automatically or via fstab entries.
@@ -4982,6 +5060,7 @@ int lxc_setup(struct lxc_handler *handler)
 	ret = lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler);
 	if (ret < 0)
 		return log_error(-1, "Failed to setup remaining automatic mounts");
+#endif
 
 	ret = run_lxc_hooks(name, "mount", lxc_conf, NULL);
 	if (ret < 0)
@@ -5026,9 +5105,17 @@ int lxc_setup(struct lxc_handler *handler)
 	if (ret < 0)
 		return log_error(-1, "Failed to setup \"/dev\" symlinks");
 
+#ifdef HAVE_ISULAD
+	if (setup_proc) {
+		ret = lxc_create_tmp_proc_mount(lxc_conf);
+		if (ret < 0)
+			return log_error(-1, "Failed to \"/proc\" LSMs");
+	}
+#else
 	ret = lxc_create_tmp_proc_mount(lxc_conf);
 	if (ret < 0)
 		return log_error(-1, "Failed to \"/proc\" LSMs");
+#endif
 
 #ifdef HAVE_ISULAD
 	/* Ask father to run oci prestart hooks and wait for him to finish. */
diff --git a/src/lxc/path.c b/src/lxc/path.c
index 65b8aadbf..46256cb26 100644
--- a/src/lxc/path.c
+++ b/src/lxc/path.c
@@ -652,4 +652,4 @@ char *path_relative(const char *basepath, const char *targpath)
 	}
 
 	return safe_strdup(targ + t0);
-}
+}
\ No newline at end of file
-- 
2.25.1

