aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2012-06-19 03:40:00 -0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2012-07-24 08:15:16 -0400
commitc12e9c8964215aaf2b5dcd06048444c2b672f0b9 (patch)
treea13c5561ad0325ca247f4c1d9d0b7770da0c64bb /drivers/block
parent63a6d0bb3dd69afedb2b2952eb1d1e8340c11d0d (diff)
drbd: fix potential access after free
Occasionally, if we disconnect, we triggered this assert: block drbd7: ASSERT FAILED tl_hash[27] == c30b0f04, expected NULL hlist_del() happens only on master bio completion. We used to wait for pending IO to complete before freeing tl_hash on disconnect. We no longer do so, since we learned to "freeze" IO on disconnect. If the local disk is too slow, we may reach C_STANDALONE early, and there are still some requests pending locally when we call drbd_free_tl_hash(). If we now free the tl_hash, and later the local IO completion completes the master bio, which then does hlist_del() and clobbers freed memory. Do hlist_del_init() and hlist_add_fake() before kfree(tl_hash), so the hlist_del() on master bio completion is harmless. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/drbd/drbd_receiver.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index ea4836e0ae98..83d99133f94b 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -3801,11 +3801,18 @@ void drbd_free_tl_hash(struct drbd_conf *mdev)
3801 mdev->ee_hash = NULL; 3801 mdev->ee_hash = NULL;
3802 mdev->ee_hash_s = 0; 3802 mdev->ee_hash_s = 0;
3803 3803
3804 /* paranoia code */ 3804 /* We may not have had the chance to wait for all locally pending
3805 for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) 3805 * application requests. The hlist_add_fake() prevents access after
3806 if (h->first) 3806 * free on master bio completion. */
3807 dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n", 3807 for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) {
3808 (int)(h - mdev->tl_hash), h->first); 3808 struct drbd_request *req;
3809 struct hlist_node *pos, *n;
3810 hlist_for_each_entry_safe(req, pos, n, h, collision) {
3811 hlist_del_init(&req->collision);
3812 hlist_add_fake(&req->collision);
3813 }
3814 }
3815
3809 kfree(mdev->tl_hash); 3816 kfree(mdev->tl_hash);
3810 mdev->tl_hash = NULL; 3817 mdev->tl_hash = NULL;
3811 mdev->tl_hash_s = 0; 3818 mdev->tl_hash_s = 0;