]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
gfs2: fix an oops in gfs2_permission
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 2 Oct 2023 02:33:44 +0000 (03:33 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Nov 2023 17:19:47 +0000 (17:19 +0000)
[ Upstream commit 0abd1557e21c617bd13fc18f7725fc6363c05913 ]

In RCU mode, we might race with gfs2_evict_inode(), which zeroes
->i_gl.  Freeing of the object it points to is RCU-delayed, so
if we manage to fetch the pointer before it's been replaced with
NULL, we are fine.  Check if we'd fetched NULL and treat that
as "bail out and tell the caller to get out of RCU mode".

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/gfs2/inode.c
fs/gfs2/super.c

index 0eac0450790471d09b49741fbffad643ba74db85..eb4bbe1728c06e43a298b493c36ec4c8b16e858f 100644 (file)
@@ -1868,14 +1868,21 @@ int gfs2_permission(struct mnt_idmap *idmap, struct inode *inode,
 {
        struct gfs2_inode *ip;
        struct gfs2_holder i_gh;
+       struct gfs2_glock *gl;
        int error;
 
        gfs2_holder_mark_uninitialized(&i_gh);
        ip = GFS2_I(inode);
-       if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
+       gl = rcu_dereference(ip->i_gl);
+       if (unlikely(!gl)) {
+               /* inode is getting torn down, must be RCU mode */
+               WARN_ON_ONCE(!(mask & MAY_NOT_BLOCK));
+               return -ECHILD;
+        }
+       if (gfs2_glock_is_locked_by_me(gl) == NULL) {
                if (mask & MAY_NOT_BLOCK)
                        return -ECHILD;
-               error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+               error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
                if (error)
                        return error;
        }
index 02d93da21b2b07643c6a7be334ca5b6838efb115..0dd5641990b90e8c26dc58320c64801ec6a171dc 100644 (file)
@@ -1550,7 +1550,7 @@ out:
                wait_on_bit_io(&ip->i_flags, GIF_GLOP_PENDING, TASK_UNINTERRUPTIBLE);
                gfs2_glock_add_to_lru(ip->i_gl);
                gfs2_glock_put_eventually(ip->i_gl);
-               ip->i_gl = NULL;
+               rcu_assign_pointer(ip->i_gl, NULL);
        }
 }