aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro2023-10-02 03:33:44 +0100
committerGreg Kroah-Hartman2023-11-28 17:07:02 +0000
commite2ccedd4d182def14811cb039677bba15e6bda5d (patch)
tree9b59a3de0e75301d2f7cf08e949bb646f1734dd8
parent53fc16c1ad84f5467ec24341670b63aa759335d3 (diff)
gfs2: fix an oops in gfs2_permission
[ 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>
-rw-r--r--fs/gfs2/inode.c11
-rw-r--r--fs/gfs2/super.c2
2 files changed, 10 insertions, 3 deletions
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 04a201584fa7..d126b02893eb 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1847,14 +1847,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;
}
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 44c564f0bc62..302d1e43d701 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1435,7 +1435,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);
}
}