gfs2: check gl_object in rgrp glops
authorBob Peterson <rpeterso@redhat.com>
Tue, 24 Jan 2023 19:55:18 +0000 (14:55 -0500)
committerAndreas Gruenbacher <agruenba@redhat.com>
Tue, 31 Jan 2023 21:40:24 +0000 (22:40 +0100)
Function gfs2_clear_rgrpd() is called during unmount to free all rgrps
and their sub-objects. If the rgrp glock is held (e.g. in SH) it calls
gfs2_glock_cb() to unlock, then calls flush_delayed_work() to make
sure any glock work is finished. However, there is a race with other
cluster nodes who may request the rgrp glock in another mode (say, EX).

Func gfs2_clear_rgrpd() calls glock_clear_object() which sets gl_object
to NULL but that's done without holding the gl_lockref spin_lock.
While the lock is not held Another node's demote request can cause the
state machine to run again, and since the gl_lockref is released in
do_xmote, the second process's call to do_xmote can call go_inval
(rgrp_go_inval) after the gl_object has been cleared, which results in
NULL pointer reference of the rgrp glock's gl_object.

Other go_inval glops functions don't require the gl_object to exist, as
evidenced by function inode_go_inval() which explicitly checks for if
(ip) before referencing gl_object. This patch does the same thing
for rgrp glocks. Both the go_inval and go_sync ops are patched to check
the existence of gl_object (rgd) before trying to dereference it.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/glops.c

index e4c585f..007cd59 100644 (file)
@@ -193,7 +193,7 @@ static int rgrp_go_sync(struct gfs2_glock *gl)
        struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);
        int error;
 
-       if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
+       if (!rgd || !test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
                return 0;
        GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
 
@@ -222,9 +222,12 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags)
        struct address_space *mapping = &sdp->sd_aspace;
        struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);
        const unsigned bsize = sdp->sd_sb.sb_bsize;
-       loff_t start = (rgd->rd_addr * bsize) & PAGE_MASK;
-       loff_t end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1;
+       loff_t start, end;
 
+       if (!rgd)
+               return;
+       start = (rgd->rd_addr * bsize) & PAGE_MASK;
+       end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1;
        gfs2_rgrp_brelse(rgd);
        WARN_ON_ONCE(!(flags & DIO_METADATA));
        truncate_inode_pages_range(mapping, start, end);