[Cluster-devel] [PATCH 1/2] gfs2: Fix occasional glock use-after-free

Ross Lagerwall ross.lagerwall at citrix.com
Thu Jan 31 10:55:42 UTC 2019


Each gfs2_bufdata stores a reference to a glock but the reference count
isn't incremented. This causes an occasional use-after-free of the
glock. Fix by taking a reference on the glock during allocation and
dropping it when freeing.

Found by KASAN:

BUG: KASAN: use-after-free in revoke_lo_after_commit+0x8e/0xe0 [gfs2]
Write of size 4 at addr ffff88801aff6134 by task kworker/0:2H/20371

CPU: 0 PID: 20371 Comm: kworker/0:2H Tainted: G O 4.19.0+0 #1
Hardware name: Dell Inc. PowerEdge R805/0D456H, BIOS 4.2.1 04/14/2010
Workqueue: glock_workqueue glock_work_func [gfs2]
Call Trace:
 dump_stack+0x71/0xab
 print_address_description+0x6a/0x270
 kasan_report+0x258/0x380
 ? revoke_lo_after_commit+0x8e/0xe0 [gfs2]
 revoke_lo_after_commit+0x8e/0xe0 [gfs2]
 gfs2_log_flush+0x511/0xa70 [gfs2]
 ? gfs2_log_shutdown+0x1f0/0x1f0 [gfs2]
 ? __brelse+0x48/0x50
 ? gfs2_log_commit+0x4de/0x6e0 [gfs2]
 ? gfs2_trans_end+0x18d/0x340 [gfs2]
 gfs2_ail_empty_gl+0x1ab/0x1c0 [gfs2]
 ? inode_go_dump+0xe0/0xe0 [gfs2]
 ? inode_go_sync+0xe4/0x220 [gfs2]
 inode_go_sync+0xe4/0x220 [gfs2]
 do_xmote+0x12b/0x290 [gfs2]
 glock_work_func+0x6f/0x160 [gfs2]
 process_one_work+0x461/0x790
 worker_thread+0x69/0x6b0
 ? process_one_work+0x790/0x790
 kthread+0x1ae/0x1d0
 ? kthread_create_worker_on_cpu+0xc0/0xc0
 ret_from_fork+0x22/0x40

Allocated by task 20805:
 kasan_kmalloc+0xa0/0xd0
 kmem_cache_alloc+0xb5/0x1b0
 gfs2_glock_get+0x14b/0x620 [gfs2]
 gfs2_inode_lookup+0x20c/0x640 [gfs2]
 gfs2_dir_search+0x150/0x180 [gfs2]
 gfs2_lookupi+0x272/0x360 [gfs2]
 __gfs2_lookup+0x8b/0x1d0 [gfs2]
 gfs2_atomic_open+0x77/0x100 [gfs2]
 path_openat+0x1454/0x1c10
 do_filp_open+0x124/0x1d0
 do_sys_open+0x213/0x2c0
 do_syscall_64+0x69/0x160
 entry_SYSCALL_64_after_hwframe+0x44/0xa9

Freed by task 0:
 __kasan_slab_free+0x130/0x180
 kmem_cache_free+0x78/0x1e0
 rcu_process_callbacks+0x2ad/0x6c0
 __do_softirq+0x111/0x38c

The buggy address belongs to the object at ffff88801aff6040
 which belongs to the cache gfs2_glock(aspace) of size 560
The buggy address is located 244 bytes inside of
 560-byte region [ffff88801aff6040, ffff88801aff6270)
...

Signed-off-by: Ross Lagerwall <ross.lagerwall at citrix.com>
---
 fs/gfs2/aops.c    | 3 +--
 fs/gfs2/lops.c    | 2 +-
 fs/gfs2/meta_io.c | 2 +-
 fs/gfs2/trans.c   | 9 ++++++++-
 fs/gfs2/trans.h   | 2 ++
 5 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 05dd78f4b2b3..8c2b572a7fb1 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -868,9 +868,8 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
 			gfs2_assert_warn(sdp, bd->bd_bh == bh);
 			if (!list_empty(&bd->bd_list))
 				list_del_init(&bd->bd_list);
-			bd->bd_bh = NULL;
 			bh->b_private = NULL;
-			kmem_cache_free(gfs2_bufdata_cachep, bd);
+			gfs2_free_bufdata(bd);
 		}
 
 		bh = bh->b_this_page;
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 94dcab655bc0..f40be71677d1 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -847,7 +847,7 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
 		gl = bd->bd_gl;
 		atomic_dec(&gl->gl_revokes);
 		clear_bit(GLF_LFLUSH, &gl->gl_flags);
-		kmem_cache_free(gfs2_bufdata_cachep, bd);
+		gfs2_free_bufdata(bd);
 	}
 }
 
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index be9c0bf697fe..868caa0eb104 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -355,7 +355,7 @@ void gfs2_remove_from_journal(struct buffer_head *bh, int meta)
 			gfs2_trans_add_revoke(sdp, bd);
 		} else if (was_pinned) {
 			bh->b_private = NULL;
-			kmem_cache_free(gfs2_bufdata_cachep, bd);
+			gfs2_free_bufdata(bd);
 		}
 		spin_unlock(&sdp->sd_ail_lock);
 	}
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index cd9a94a6b5bb..423cbee8fa08 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -133,9 +133,16 @@ static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl,
 	bd->bd_gl = gl;
 	INIT_LIST_HEAD(&bd->bd_list);
 	bh->b_private = bd;
+	gfs2_glock_hold(gl);
 	return bd;
 }
 
+void gfs2_free_bufdata(struct gfs2_bufdata *bd)
+{
+	gfs2_glock_put(bd->bd_gl);
+	kmem_cache_free(gfs2_bufdata_cachep, bd);
+}
+
 /**
  * gfs2_trans_add_data - Add a databuf to the transaction.
  * @gl: The inode glock associated with the buffer
@@ -265,7 +272,7 @@ void gfs2_trans_add_unrevoke(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--;
-			kmem_cache_free(gfs2_bufdata_cachep, bd);
+			gfs2_free_bufdata(bd);
 			tr->tr_num_revoke_rm++;
 			if (--n == 0)
 				break;
diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h
index ad70087d0597..276ddca7bbe9 100644
--- a/fs/gfs2/trans.h
+++ b/fs/gfs2/trans.h
@@ -46,4 +46,6 @@ extern void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh);
 extern void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
 extern void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len);
 
+void gfs2_free_bufdata(struct gfs2_bufdata *bd);
+
 #endif /* __TRANS_DOT_H__ */
-- 
2.17.2




More information about the Cluster-devel mailing list