[RFC,RH7] fs: autofs4: find_autofs_mount overmounted parent support

Submitted by Alexander Mikhalitsyn on March 1, 2021, 1:05 p.m.

Details

Message ID 20210301130510.507431-1-alexander.mikhalitsyn@virtuozzo.com
State New
Series "fs: autofs4: find_autofs_mount overmounted parent support"
Headers show

Commit Message

Alexander Mikhalitsyn March 1, 2021, 1:05 p.m.
It was discovered that find_autofs_mount() function
in autofs4 not support cases when autofs mount
parent is overmounted. In this case this function will
always return -ENOENT.

Real-life reproducer is fairly simple.
Consider the following mounts on root mntns:
--
35 24 0:36 / /proc/sys/fs/binfmt_misc rw,relatime shared:16 - autofs systemd-1 ...
654 35 0:57 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime shared:322 - binfmt_misc ...
--
and some process "some_process" which calls ioctl(AUTOFS_DEV_IOCTL_OPENMOUNT)

Due to "mount-proc" /proc will be overmounted and ioctl() will fail with -ENOENT

https://jira.sw.ru/browse/PSBM-125911

Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
---
 fs/autofs4/dev-ioctl.c | 68 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 59 insertions(+), 9 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index 86be02910116..5a387acd0705 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -24,6 +24,8 @@ 
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 
+#include "../mount.h"
+
 #include "autofs_i.h"
 
 /*
@@ -198,26 +200,74 @@  static int find_autofs_mount(const char *pathname,
 			     int test(const struct path *path, void *data),
 			     void *data)
 {
+	struct mnt_namespace *mnt_ns = current->nsproxy->mnt_ns;
 	struct path path;
+	char *fpathname;
+	char *fpathbuf = NULL, *pathbuf = NULL;
+	struct mount *mnt;
 	int err;
 
 	err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
 	if (err)
 		return err;
+
+	fpathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (!fpathbuf) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	/*
+	 * We have pathname from user but it may be relative, we need to
+	 * have full path because we want to compare it with mountpoints
+	 * paths later.
+	 */
+	fpathname = d_path(&path, fpathbuf, PATH_MAX);
+	if (IS_ERR(fpathname)) {
+		err = PTR_ERR(fpathname);
+		goto err;
+	}
+
+	pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (!pathbuf) {
+		err = -ENOMEM;
+		goto err;
+	}
+
 	err = -ENOENT;
-	while (path.dentry == path.mnt->mnt_root) {
-		if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
-			if (test(&path, data)) {
-				path_get(&path);
-				*res = path;
-				err = 0;
-				break;
-			}
+	down_read(&namespace_sem);
+	list_for_each_entry(mnt, &mnt_ns->list, mnt_list) {
+		char *name;
+		struct path tmppath;
+
+		if (mnt->mnt.mnt_sb->s_magic != AUTOFS_SUPER_MAGIC)
+			continue;
+
+		tmppath.dentry = mnt->mnt.mnt_root;
+		tmppath.mnt = &mnt->mnt;
+
+		name = d_path(&tmppath, pathbuf, PATH_MAX);
+		if (IS_ERR(name)) {
+			err = PTR_ERR(name);
+			break;
 		}
-		if (!follow_up(&path))
+ 
+		if (strncmp(fpathname, name, PATH_MAX))
+			continue;
+
+		if (test(&tmppath, data)) {
+			path_get(&tmppath);
+			*res = tmppath;
+			err = 0;
 			break;
+		}
 	}
+	up_read(&namespace_sem);
+
+err:
 	path_put(&path);
+	kfree(fpathbuf);
+	kfree(pathbuf);
 	return err;
 }