All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] gfs2: Fix potential glock use-after-free on unmount
@ 2024-04-10 21:28 Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 1/3] gfs2: Remove ill-placed consistency check Andreas Gruenbacher
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Andreas Gruenbacher @ 2024-04-10 21:28 UTC (permalink / raw)
  To: gfs2; +Cc: Andreas Gruenbacher

On top of what we already have on for-next, here are three more patches.

gfs2: Remove ill-placed consistency check

  Removes a consistency check that isn't needed anymore and only
  gets in the way of the next patch.

gfs2: Fix potential glock use-after-free on unmount

  Prevent DLM bast callbacks from accessing glock objects after they
  have been freed.

gfs2: Unlock fewer glocks on unmount

  We can be a little more aggressive in avoiding to unlock DLM
  locks immediately before the lockspace is released.

Thanks,
Andreas

Andreas Gruenbacher (3):
  gfs2: Remove ill-placed consistency check
  gfs2: Fix potential glock use-after-free on unmount
  gfs2: Unlock fewer glocks on unmount

 fs/gfs2/glock.c      | 32 ++++++++++++++++++++++++++++----
 fs/gfs2/glock.h      |  2 ++
 fs/gfs2/incore.h     |  1 +
 fs/gfs2/lock_dlm.c   | 40 +++++++++++++++++++++++++++++-----------
 fs/gfs2/ops_fstype.c |  1 +
 fs/gfs2/super.c      |  1 +
 6 files changed, 62 insertions(+), 15 deletions(-)

-- 
2.44.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] gfs2: Remove ill-placed consistency check
  2024-04-10 21:28 [PATCH 0/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
@ 2024-04-10 21:28 ` Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 2/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 3/3] gfs2: Unlock fewer glocks " Andreas Gruenbacher
  2 siblings, 0 replies; 4+ messages in thread
From: Andreas Gruenbacher @ 2024-04-10 21:28 UTC (permalink / raw)
  To: gfs2; +Cc: Andreas Gruenbacher

This consistency check was originally added by commit 9287c6452d2b1
("gfs2: Fix occasional glock use-after-free").  It is ill-placed in
gfs2_glock_free() because if it holds there, it must equally hold in
__gfs2_glock_put() already.  Either way, the check doesn't seem
necessary anymore.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/glock.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 4be140ab53e4..60503e378740 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -170,7 +170,6 @@ void gfs2_glock_free(struct gfs2_glock *gl)
 {
 	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
 
-	gfs2_glock_assert_withdraw(gl, atomic_read(&gl->gl_revokes) == 0);
 	rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
 	smp_mb();
 	wake_up_glock(gl);
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] gfs2: Fix potential glock use-after-free on unmount
  2024-04-10 21:28 [PATCH 0/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 1/3] gfs2: Remove ill-placed consistency check Andreas Gruenbacher
@ 2024-04-10 21:28 ` Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 3/3] gfs2: Unlock fewer glocks " Andreas Gruenbacher
  2 siblings, 0 replies; 4+ messages in thread
From: Andreas Gruenbacher @ 2024-04-10 21:28 UTC (permalink / raw)
  To: gfs2; +Cc: Andreas Gruenbacher, David Teigland

When a DLM lockspace is released and there ares still locks in that
lockspace, DLM will unlock those locks automatically.  Commit
fb6791d100d1b started exploiting this behavior to speed up filesystem
unmount: gfs2 would simply free glocks it didn't want to unlock and then
release the lockspace.  This didn't take the bast callbacks for
asynchronous lock contention notifications into account, which remain
active until until a lock is unlocked or its lockspace is released.

To prevent those callbacks from accessing deallocated objects, put the
glocks that should not be unlocked on the sd_dead_glocks list, release
the lockspace, and only then free those glocks.

As an additional measure, ignore unexpected ast and bast callbacks if
the receiving glock is dead.

Fixes: fb6791d100d1b ("GFS2: skip dlm_unlock calls in unmount")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Cc: David Teigland <teigland@redhat.com>
---
 fs/gfs2/glock.c      | 31 ++++++++++++++++++++++++++++---
 fs/gfs2/glock.h      |  2 ++
 fs/gfs2/incore.h     |  1 +
 fs/gfs2/lock_dlm.c   | 32 ++++++++++++++++++++++----------
 fs/gfs2/ops_fstype.c |  1 +
 fs/gfs2/super.c      |  1 +
 6 files changed, 55 insertions(+), 13 deletions(-)

diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 60503e378740..ce62937d85b5 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -166,18 +166,43 @@ static bool glock_blocked_by_withdraw(struct gfs2_glock *gl)
 	return true;
 }
 
-void gfs2_glock_free(struct gfs2_glock *gl)
+static void __gfs2_glock_free(struct gfs2_glock *gl)
 {
-	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
-
 	rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
 	smp_mb();
 	wake_up_glock(gl);
 	call_rcu(&gl->gl_rcu, gfs2_glock_dealloc);
+}
+
+void gfs2_glock_free(struct gfs2_glock *gl) {
+	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+
+	__gfs2_glock_free(gl);
 	if (atomic_dec_and_test(&sdp->sd_glock_disposal))
 		wake_up(&sdp->sd_kill_wait);
 }
 
+void gfs2_glock_free_later(struct gfs2_glock *gl) {
+	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+
+	list_add(&gl->gl_lru, &sdp->sd_dead_glocks);
+	if (atomic_dec_and_test(&sdp->sd_glock_disposal))
+		wake_up(&sdp->sd_kill_wait);
+}
+
+void gfs2_free_dead_glocks(struct gfs2_sbd *sdp)
+{
+	struct list_head *list = &sdp->sd_dead_glocks;
+
+	while(!list_empty(list)) {
+		struct gfs2_glock *gl;
+
+		gl = list_first_entry(list, struct gfs2_glock, gl_lru);
+		list_del_init(&gl->gl_lru);
+		__gfs2_glock_free(gl);
+	}
+}
+
 /**
  * gfs2_glock_hold() - increment reference count on glock
  * @gl: The glock to hold
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index 2c697674a86f..3aa6ab6ab73f 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -252,6 +252,8 @@ void gfs2_gl_dq_holders(struct gfs2_sbd *sdp);
 void gfs2_glock_thaw(struct gfs2_sbd *sdp);
 void gfs2_glock_add_to_lru(struct gfs2_glock *gl);
 void gfs2_glock_free(struct gfs2_glock *gl);
+void gfs2_glock_free_later(struct gfs2_glock *gl);
+void gfs2_free_dead_glocks(struct gfs2_sbd *sdp);
 
 int __init gfs2_glock_init(void);
 void gfs2_glock_exit(void);
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 2c4c0ee2c014..fbb1c358b7e3 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -837,6 +837,7 @@ struct gfs2_sbd {
 	/* For quiescing the filesystem */
 	struct gfs2_holder sd_freeze_gh;
 	struct mutex sd_freeze_mutex;
+	struct list_head sd_dead_glocks;
 
 	char sd_fsname[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2];
 	char sd_table_name[GFS2_FSNAME_LEN];
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
index d1ac5d0679ea..e028e55e67d9 100644
--- a/fs/gfs2/lock_dlm.c
+++ b/fs/gfs2/lock_dlm.c
@@ -121,6 +121,11 @@ static void gdlm_ast(void *arg)
 	struct gfs2_glock *gl = arg;
 	unsigned ret = gl->gl_state;
 
+	/* If the glock is dead, we only react to a dlm_unlock() reply. */
+	if (__lockref_is_dead(&gl->gl_lockref) &&
+	    gl->gl_lksb.sb_status != -DLM_EUNLOCK)
+		return;
+
 	gfs2_update_reply_times(gl);
 	BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED);
 
@@ -171,6 +176,9 @@ static void gdlm_bast(void *arg, int mode)
 {
 	struct gfs2_glock *gl = arg;
 
+	if (__lockref_is_dead(&gl->gl_lockref))
+		return;
+
 	switch (mode) {
 	case DLM_LOCK_EX:
 		gfs2_glock_cb(gl, LM_ST_UNLOCKED);
@@ -291,8 +299,12 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
 	struct lm_lockstruct *ls = &sdp->sd_lockstruct;
 	int error;
 
-	if (gl->gl_lksb.sb_lkid == 0)
-		goto out_free;
+	BUG_ON(!__lockref_is_dead(&gl->gl_lockref));
+
+	if (gl->gl_lksb.sb_lkid == 0) {
+		gfs2_glock_free(gl);
+		return;
+	}
 
 	clear_bit(GLF_BLOCKING, &gl->gl_flags);
 	gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT);
@@ -300,13 +312,17 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
 	gfs2_update_request_times(gl);
 
 	/* don't want to call dlm if we've unmounted the lock protocol */
-	if (test_bit(DFL_UNMOUNT, &ls->ls_recover_flags))
-		goto out_free;
+	if (test_bit(DFL_UNMOUNT, &ls->ls_recover_flags)) {
+		gfs2_glock_free(gl);
+		return;
+	}
 	/* don't want to skip dlm_unlock writing the lvb when lock has one */
 
 	if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) &&
-	    !gl->gl_lksb.sb_lvbptr)
-		goto out_free;
+	    !gl->gl_lksb.sb_lvbptr) {
+		gfs2_glock_free_later(gl);
+		return;
+	}
 
 again:
 	error = dlm_unlock(ls->ls_dlm, gl->gl_lksb.sb_lkid, DLM_LKF_VALBLK,
@@ -321,10 +337,6 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
 		       gl->gl_name.ln_type,
 		       (unsigned long long)gl->gl_name.ln_number, error);
 	}
-	return;
-
-out_free:
-	gfs2_glock_free(gl);
 }
 
 static void gdlm_cancel(struct gfs2_glock *gl)
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 1281e60be639..db0df091a6a7 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -136,6 +136,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
 	atomic_set(&sdp->sd_log_in_flight, 0);
 	init_waitqueue_head(&sdp->sd_log_flush_wait);
 	mutex_init(&sdp->sd_freeze_mutex);
+	INIT_LIST_HEAD(&sdp->sd_dead_glocks);
 
 	return sdp;
 
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index d481db9510ac..4c3cec1a2cbd 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -652,6 +652,7 @@ static void gfs2_put_super(struct super_block *sb)
 	gfs2_delete_debugfs_file(sdp);
 	/*  Unmount the locking protocol  */
 	gfs2_lm_unmount(sdp);
+	gfs2_free_dead_glocks(sdp);
 
 	/*  At this point, we're through participating in the lockspace  */
 	gfs2_sys_fs_del(sdp);
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] gfs2: Unlock fewer glocks on unmount
  2024-04-10 21:28 [PATCH 0/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 1/3] gfs2: Remove ill-placed consistency check Andreas Gruenbacher
  2024-04-10 21:28 ` [PATCH 2/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
@ 2024-04-10 21:28 ` Andreas Gruenbacher
  2 siblings, 0 replies; 4+ messages in thread
From: Andreas Gruenbacher @ 2024-04-10 21:28 UTC (permalink / raw)
  To: gfs2; +Cc: Andreas Gruenbacher, David Teigland

At unmount time, we would generally like to explicitly unlock as few
glocks as possible for efficiency.  We are already skipping glocks that
don't have a lock value block (LVB), but we can also skip glocks which
are not held in DLM_LOCK_EX or DLM_LOCK_PW mode (of which gfs2 only uses
DLM_LOCK_EX under the name LM_ST_EXCLUSIVE).

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Cc: David Teigland <teigland@redhat.com>
---
 fs/gfs2/lock_dlm.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
index e028e55e67d9..49059274a528 100644
--- a/fs/gfs2/lock_dlm.c
+++ b/fs/gfs2/lock_dlm.c
@@ -316,10 +316,16 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
 		gfs2_glock_free(gl);
 		return;
 	}
-	/* don't want to skip dlm_unlock writing the lvb when lock has one */
+
+	/*
+	 * When the lockspace is released, all remaining glocks will be
+	 * unlocked automatically.  This is more efficient than unlocking them
+	 * individually, but when the lock is held in DLM_LOCK_EX or
+	 * DLM_LOCK_PW mode, the lock value block (LVB) will be lost.
+	 */
 
 	if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) &&
-	    !gl->gl_lksb.sb_lvbptr) {
+	    (!gl->gl_lksb.sb_lvbptr || gl->gl_state != LM_ST_EXCLUSIVE)) {
 		gfs2_glock_free_later(gl);
 		return;
 	}
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-04-10 21:28 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-10 21:28 [PATCH 0/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
2024-04-10 21:28 ` [PATCH 1/3] gfs2: Remove ill-placed consistency check Andreas Gruenbacher
2024-04-10 21:28 ` [PATCH 2/3] gfs2: Fix potential glock use-after-free on unmount Andreas Gruenbacher
2024-04-10 21:28 ` [PATCH 3/3] gfs2: Unlock fewer glocks " Andreas Gruenbacher

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.