gfs2: fix an oops in gfs2_permission

[ Upstream commit 0abd1557e2 ]

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>
This commit is contained in:
Al Viro 2023-10-02 03:33:44 +01:00 committed by Greg Kroah-Hartman
parent 5bfda356e9
commit 03ce0655bf
2 changed files with 10 additions and 3 deletions

View File

@ -1850,14 +1850,21 @@ int gfs2_permission(struct user_namespace *mnt_userns, 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;
}

View File

@ -1436,7 +1436,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);
}
}