[LTP-FAIL][02/21] fs: refactor ksys_umount

Started by Vikas Kumar on Aug 6, 2020 · 4 messages from 3 authors · Last reply Aug 6, 2020

Subsystems (2)

Maintainers (3)

open the first message on its own page

Message details and related content
Hi Christoph,

We have seen LTP test(utime06 and umount03) failure in Next Master with commit Id 41525f56e256("fs: refactor ksys_umount").
I didn't analysis root cause of problem, i am reporting this issue.

---------------------------------------------
LTP Testcase    Result    Exit Value
----------------------------------- ---------
umount03          FAIL           4
utime06             FAIL           2
--------------------------------------------

LTP utime06 Fail Log:
/dev/loop0 is mounted; will not make a filesystem here!
utime06     0  TINFO  :  Using test device LTP_DEV='/dev/loop0'
utime06     0  TINFO  :  Formatting /dev/loop0 with ext2 opts='' extra opts=''
utime06     1  TBROK  :  tst_mkfs.c:102: utime06.c:122: mkfs.ext2 failed with 1
utime06     2  TBROK  :  tst_mkfs.c:102: Remaining cases broken

LTP umount03 Fail Log:
tst_device.c:262: INFO: Using test device LTP_DEV='/dev/loop0'
tst_mkfs.c:89: INFO: Formatting /dev/loop0 with ext2 opts='' extra opts=''
mke2fs 1.44.5 (15-Dec-2018)
tst_test.c:1244: INFO: Timeout per run is 0h 05m 00s
umount03.c:35: PASS: umount() fails as expected: EPERM (1)
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 1...
tst_device.c:387: INFO: Likely gvfsd-trash is probing newly mounted fs, kill it to speed up tests.
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 2...
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 3...
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 48...
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 49...
tst_device.c:383: INFO: umount('mntpoint') failed with EBUSY, try 50...
tst_device.c:394: WARN: Failed to umount('mntpoint') after 50 retries
tst_tmpdir.c:336: WARN: tst_rmdir: rmobj(/scratch/ltp-Lnmh7tbxY6/gx0hJU) failed: remove(/scratch/ltp-Lnmh7tbxY6/gx0hJU/mntpoint) failed; errno=16: EBUSY

Regards,

Vikas

19 more lines

diff --git a/fs/namespace.c b/fs/namespace.c
index 6f8234f74bed90..43834b59eff6c3 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1706,36 +1706,19 @@ static inline bool may_mandlock(void)
 }
 #endif

-/*
- * Now umount can handle mount points as well as block devices.
- * This is important for filesystems which use unnamed block devices.
- *
- * We now support a flag for forced unmount like the other 'big iron'
- * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
- */
-
-int ksys_umount(char __user *name, int flags)
+static int path_umount(struct path *path, int flags)
 {
-    struct path path;
     struct mount *mnt;
     int retval;
-    int lookup_flags = LOOKUP_MOUNTPOINT;

     if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
         return -EINVAL;
-
     if (!may_mount())
         return -EPERM;

-    if (!(flags & UMOUNT_NOFOLLOW))
-        lookup_flags |= LOOKUP_FOLLOW;
-
-    retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
-    if (retval)
-        goto out;
-    mnt = real_mount(path.mnt);
+    mnt = real_mount(path->mnt);
     retval = -EINVAL;
-    if (path.dentry != path.mnt->mnt_root)
+    if (path->dentry != path->mnt->mnt_root)
         goto dput_and_out;
     if (!check_mnt(mnt))
         goto dput_and_out;
@@ -1748,12 +1731,25 @@ int ksys_umount(char __user *name, int flags)
     retval = do_umount(mnt, flags);
 dput_and_out:
     /* we mustn't call path_put() as that would clear mnt_expiry_mark */
-    dput(path.dentry);
+    dput(path->dentry);
     mntput_no_expire(mnt);
-out:
     return retval;
 }

+int ksys_umount(char __user *name, int flags)
+{
+    int lookup_flags = LOOKUP_MOUNTPOINT;
+    struct path path;
+    int ret;
+
+    if (!(flags & UMOUNT_NOFOLLOW))
+        lookup_flags |= LOOKUP_FOLLOW;
+    ret = user_path_at(AT_FDCWD, name, lookup_flags, &path);
+    if (ret)
+        return ret;
+    return path_umount(&path, flags);
+}
+
SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
 {

Message details and related content
Fix for umount03 below.  The other one works fine here, but from
your logs this might be a follow on if you run it after umount without
the fix.

---
From 718c12b6559c6be5fac39837b496fd1cd325a0d5 Mon Sep 17 00:00:00 2001
From: Christoph Hellwig <hch@lst.de>
Date: Thu, 6 Aug 2020 16:07:10 +0200
Subject: fs: fix a struct path leak in path_umount

Make sure we also put the dentry and vfsmnt in the illegal flags
and !may_mount cases.

Fixes: 41525f56e256 ("fs: refactor ksys_umount")
Reported-by: Vikas Kumar <redacted>
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/namespace.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index a7301790abb211..1180437dfab909 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1708,15 +1708,16 @@ static inline bool may_mandlock(void)
 
 int path_umount(struct path *path, int flags)
 {
-	struct mount *mnt;
+	struct mount *mnt = real_mount(path->mnt);
 	int retval;
 
+	retval = -EINVAL;
 	if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
-		return -EINVAL;
+		goto dput_and_out;
+	retval = -EPERM;
 	if (!may_mount())
-		return -EPERM;
+		goto dput_and_out;
 
-	mnt = real_mount(path->mnt);
 	retval = -EINVAL;
 	if (path->dentry != path->mnt->mnt_root)
 		goto dput_and_out;
-- 
2.27.0
Message details and related content
On Thu, Aug 06, 2020 at 04:17:32PM +0200, Christoph Hellwig wrote:
Fix for umount03 below.  The other one works fine here, but from
your logs this might be a follow on if you run it after umount without
the fix.
Ugh...

How about 
static int may_umount(const struct path *path, int flags)
{
        struct mount *mnt = real_mount(path->mnt);

        if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
                return -EINVAL;
        if (!may_mount())
                return -EPERM;
        if (path->dentry != path->mnt->mnt_root)
                return -EINVAL;
        if (!check_mnt(mnt))
                return -EINVAL;
        if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */
                return -EINVAL;
        if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))
                return -EPERM;
	return 0;
}

int path_umount(const struct path *path, int flags)
{
        struct mount *mnt = real_mount(path->mnt);
        int err;

	err = may_umount(path, flags);
	if (!err)
		err = do_umount(mnt, flags);

        /* we mustn't call path_put() as that would clear mnt_expiry_mark */
        dput(path->dentry);
        mntput_no_expire(mnt);
        return err;
}

instead?
Message details and related content
On Thu, Aug 06, 2020 at 03:32:21PM +0100, Al Viro wrote:
On Thu, Aug 06, 2020 at 04:17:32PM +0200, Christoph Hellwig wrote:
quoted
Fix for umount03 below.  The other one works fine here, but from
your logs this might be a follow on if you run it after umount without
the fix.
Ugh...

How about 
static int may_umount(const struct path *path, int flags)
may_umount is already take.  But with can_umount this would work:

---
From e4ccb3da160831a43eeea48c68d2d43fd7cf6724 Mon Sep 17 00:00:00 2001
From: Christoph Hellwig <hch@lst.de>
Date: Thu, 6 Aug 2020 16:07:10 +0200
Subject: fs: fix a struct path leak in path_umount

Make sure we also put the dentry and vfsmnt in the illegal flags
and !may_umount cases.

Fixes: 41525f56e256 ("fs: refactor ksys_umount")
Reported-by: Vikas Kumar <redacted>
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/namespace.c | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index a7301790abb211..1c74a46367df4e 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1706,34 +1706,38 @@ static inline bool may_mandlock(void)
 }
 #endif
 
-int path_umount(struct path *path, int flags)
+static int can_umount(const struct path *path, int flags)
 {
-	struct mount *mnt;
-	int retval;
+	struct mount *mnt = real_mount(path->mnt);
 
 	if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
 		return -EINVAL;
 	if (!may_mount())
 		return -EPERM;
-
-	mnt = real_mount(path->mnt);
-	retval = -EINVAL;
 	if (path->dentry != path->mnt->mnt_root)
-		goto dput_and_out;
+		return -EINVAL;
 	if (!check_mnt(mnt))
-		goto dput_and_out;
+		return -EINVAL;
 	if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */
-		goto dput_and_out;
-	retval = -EPERM;
+		return -EINVAL;
 	if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))
-		goto dput_and_out;
+		return -EPERM;
+	return 0;
+}
+
+int path_umount(struct path *path, int flags)
+{
+	struct mount *mnt = real_mount(path->mnt);
+	int ret;
+
+	ret = can_umount(path, flags);
+	if (!ret)
+		ret = do_umount(mnt, flags);
 
-	retval = do_umount(mnt, flags);
-dput_and_out:
 	/* we mustn't call path_put() as that would clear mnt_expiry_mark */
 	dput(path->dentry);
 	mntput_no_expire(mnt);
-	return retval;
+	return ret;
 }
 
 static int ksys_umount(char __user *name, int flags)
-- 
2.27.0
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help