Skip to content

Commit d98779e

Browse files
author
Andreas Gruenbacher
committed
gfs2: Fix potential glock use-after-free on unmount
When a DLM lockspace is released and there ares still locks in that lockspace, DLM will unlock those locks automatically. Commit fb6791d 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: fb6791d ("GFS2: skip dlm_unlock calls in unmount") Signed-off-by: Andreas Gruenbacher <[email protected]> Cc: David Teigland <[email protected]>
1 parent 59f6000 commit d98779e

File tree

6 files changed

+57
-16
lines changed

6 files changed

+57
-16
lines changed

fs/gfs2/glock.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,18 +166,45 @@ static bool glock_blocked_by_withdraw(struct gfs2_glock *gl)
166166
return true;
167167
}
168168

169-
void gfs2_glock_free(struct gfs2_glock *gl)
169+
static void __gfs2_glock_free(struct gfs2_glock *gl)
170170
{
171-
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
172-
173171
rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
174172
smp_mb();
175173
wake_up_glock(gl);
176174
call_rcu(&gl->gl_rcu, gfs2_glock_dealloc);
175+
}
176+
177+
void gfs2_glock_free(struct gfs2_glock *gl) {
178+
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
179+
180+
__gfs2_glock_free(gl);
177181
if (atomic_dec_and_test(&sdp->sd_glock_disposal))
178182
wake_up(&sdp->sd_kill_wait);
179183
}
180184

185+
void gfs2_glock_free_later(struct gfs2_glock *gl) {
186+
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
187+
188+
spin_lock(&lru_lock);
189+
list_add(&gl->gl_lru, &sdp->sd_dead_glocks);
190+
spin_unlock(&lru_lock);
191+
if (atomic_dec_and_test(&sdp->sd_glock_disposal))
192+
wake_up(&sdp->sd_kill_wait);
193+
}
194+
195+
static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp)
196+
{
197+
struct list_head *list = &sdp->sd_dead_glocks;
198+
199+
while(!list_empty(list)) {
200+
struct gfs2_glock *gl;
201+
202+
gl = list_first_entry(list, struct gfs2_glock, gl_lru);
203+
list_del_init(&gl->gl_lru);
204+
__gfs2_glock_free(gl);
205+
}
206+
}
207+
181208
/**
182209
* gfs2_glock_hold() - increment reference count on glock
183210
* @gl: The glock to hold
@@ -2233,6 +2260,8 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp)
22332260
wait_event_timeout(sdp->sd_kill_wait,
22342261
atomic_read(&sdp->sd_glock_disposal) == 0,
22352262
HZ * 600);
2263+
gfs2_lm_unmount(sdp);
2264+
gfs2_free_dead_glocks(sdp);
22362265
glock_hash_walk(dump_glock_func, sdp);
22372266
}
22382267

fs/gfs2/glock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ void gfs2_gl_dq_holders(struct gfs2_sbd *sdp);
252252
void gfs2_glock_thaw(struct gfs2_sbd *sdp);
253253
void gfs2_glock_add_to_lru(struct gfs2_glock *gl);
254254
void gfs2_glock_free(struct gfs2_glock *gl);
255+
void gfs2_glock_free_later(struct gfs2_glock *gl);
255256

256257
int __init gfs2_glock_init(void);
257258
void gfs2_glock_exit(void);

fs/gfs2/incore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ struct gfs2_sbd {
838838
/* For quiescing the filesystem */
839839
struct gfs2_holder sd_freeze_gh;
840840
struct mutex sd_freeze_mutex;
841+
struct list_head sd_dead_glocks;
841842

842843
char sd_fsname[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2];
843844
char sd_table_name[GFS2_FSNAME_LEN];

fs/gfs2/lock_dlm.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ static void gdlm_ast(void *arg)
121121
struct gfs2_glock *gl = arg;
122122
unsigned ret = gl->gl_state;
123123

124+
/* If the glock is dead, we only react to a dlm_unlock() reply. */
125+
if (__lockref_is_dead(&gl->gl_lockref) &&
126+
gl->gl_lksb.sb_status != -DLM_EUNLOCK)
127+
return;
128+
124129
gfs2_update_reply_times(gl);
125130
BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED);
126131

@@ -171,6 +176,9 @@ static void gdlm_bast(void *arg, int mode)
171176
{
172177
struct gfs2_glock *gl = arg;
173178

179+
if (__lockref_is_dead(&gl->gl_lockref))
180+
return;
181+
174182
switch (mode) {
175183
case DLM_LOCK_EX:
176184
gfs2_glock_cb(gl, LM_ST_UNLOCKED);
@@ -291,22 +299,30 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
291299
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
292300
int error;
293301

294-
if (gl->gl_lksb.sb_lkid == 0)
295-
goto out_free;
302+
BUG_ON(!__lockref_is_dead(&gl->gl_lockref));
303+
304+
if (gl->gl_lksb.sb_lkid == 0) {
305+
gfs2_glock_free(gl);
306+
return;
307+
}
296308

297309
clear_bit(GLF_BLOCKING, &gl->gl_flags);
298310
gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT);
299311
gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT);
300312
gfs2_update_request_times(gl);
301313

302314
/* don't want to call dlm if we've unmounted the lock protocol */
303-
if (test_bit(DFL_UNMOUNT, &ls->ls_recover_flags))
304-
goto out_free;
315+
if (test_bit(DFL_UNMOUNT, &ls->ls_recover_flags)) {
316+
gfs2_glock_free(gl);
317+
return;
318+
}
305319
/* don't want to skip dlm_unlock writing the lvb when lock has one */
306320

307321
if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) &&
308-
!gl->gl_lksb.sb_lvbptr)
309-
goto out_free;
322+
!gl->gl_lksb.sb_lvbptr) {
323+
gfs2_glock_free_later(gl);
324+
return;
325+
}
310326

311327
again:
312328
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)
321337
gl->gl_name.ln_type,
322338
(unsigned long long)gl->gl_name.ln_number, error);
323339
}
324-
return;
325-
326-
out_free:
327-
gfs2_glock_free(gl);
328340
}
329341

330342
static void gdlm_cancel(struct gfs2_glock *gl)

fs/gfs2/ops_fstype.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
136136
atomic_set(&sdp->sd_log_in_flight, 0);
137137
init_waitqueue_head(&sdp->sd_log_flush_wait);
138138
mutex_init(&sdp->sd_freeze_mutex);
139+
INIT_LIST_HEAD(&sdp->sd_dead_glocks);
139140

140141
return sdp;
141142

fs/gfs2/super.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,7 @@ static void gfs2_put_super(struct super_block *sb)
650650
gfs2_gl_hash_clear(sdp);
651651
truncate_inode_pages_final(&sdp->sd_aspace);
652652
gfs2_delete_debugfs_file(sdp);
653-
/* Unmount the locking protocol */
654-
gfs2_lm_unmount(sdp);
655653

656-
/* At this point, we're through participating in the lockspace */
657654
gfs2_sys_fs_del(sdp);
658655
free_sbd(sdp);
659656
}

0 commit comments

Comments
 (0)