gfs2: fix glock reference problem in gfs2_trans_remove_revoke
authorBob Peterson <rpeterso@redhat.com>
Thu, 14 Nov 2019 14:49:11 +0000 (09:49 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 21 Dec 2019 10:04:34 +0000 (11:04 +0100)
commit fe5e7ba11fcf1d75af8173836309e8562aefedef upstream.

Commit 9287c6452d2b fixed a situation in which gfs2 could use a glock
after it had been freed. To do that, it temporarily added a new glock
reference by calling gfs2_glock_hold in function gfs2_add_revoke.
However, if the bd element was removed by gfs2_trans_remove_revoke, it
failed to drop the additional reference.

This patch adds logic to gfs2_trans_remove_revoke to properly drop the
additional glock reference.

Fixes: 9287c6452d2b ("gfs2: Fix occasional glock use-after-free")
Cc: stable@vger.kernel.org # v5.2+
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/gfs2/log.c
fs/gfs2/log.h
fs/gfs2/lops.c
fs/gfs2/trans.c

index 58e237fba565060b4677d59a50739c8e44c8add8..2aed73666a657031c56bf646641629765d188d59 100644 (file)
@@ -609,6 +609,14 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
        list_add(&bd->bd_list, &sdp->sd_log_revokes);
 }
 
+void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
+{
+       if (atomic_dec_return(&gl->gl_revokes) == 0) {
+               clear_bit(GLF_LFLUSH, &gl->gl_flags);
+               gfs2_glock_queue_put(gl);
+       }
+}
+
 void gfs2_write_revokes(struct gfs2_sbd *sdp)
 {
        struct gfs2_trans *tr;
index 2315fca47a2bbb22dfc237128af063461aae9531..c762da49454688696da6a2885de65e11aa43e8bc 100644 (file)
@@ -77,6 +77,7 @@ extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
 extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
 extern int gfs2_logd(void *data);
 extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
+extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl);
 extern void gfs2_write_revokes(struct gfs2_sbd *sdp);
 
 #endif /* __LOG_DOT_H__ */
index 5b17979af5396ee6e5eb0d85bc94bb293e75bc47..e2437b775456644172d3879de0d22e66e6c6c342 100644 (file)
@@ -882,10 +882,7 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
                bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
                list_del_init(&bd->bd_list);
                gl = bd->bd_gl;
-               if (atomic_dec_return(&gl->gl_revokes) == 0) {
-                       clear_bit(GLF_LFLUSH, &gl->gl_flags);
-                       gfs2_glock_queue_put(gl);
-               }
+               gfs2_glock_remove_revoke(gl);
                kmem_cache_free(gfs2_bufdata_cachep, bd);
        }
 }
index 35e3059255fe28275292a826ccead9d149f26bca..9d4227330de470b9f98966b867fa2689c88f17b8 100644 (file)
@@ -262,6 +262,8 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
                        list_del_init(&bd->bd_list);
                        gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke);
                        sdp->sd_log_num_revoke--;
+                       if (bd->bd_gl)
+                               gfs2_glock_remove_revoke(bd->bd_gl);
                        kmem_cache_free(gfs2_bufdata_cachep, bd);
                        tr->tr_num_revoke--;
                        if (--n == 0)