From 270559b5094e269d2a330162e2c496aa0e9de0a6 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 16 Nov 2014 13:24:04 +0000 Subject: [PATCH] isert: Fix use-after-free when killing iscsi-scstd Our portal may be destroyed while there are connections alive. This means we are doing list_del() from list_head that no longer exists Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5866 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 7 +++++++ iscsi-scst/kernel/isert-scst/iser_rdma.c | 25 +++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 2c75cc6d7..c33992ef1 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -9,12 +9,18 @@ #include "iser_hdr.h" +enum isert_portal_state { + ISERT_PORTAL_ACTIVE, + ISERT_PORTAL_INACTIVE +}; + struct isert_portal { struct rdma_cm_id *cm_id; struct sockaddr_storage addr; struct list_head list_node; /* in portals list */ /* protected by dev_list_mutex */ struct list_head conn_list; /* head of conns list */ + enum isert_portal_state state; }; struct isert_buf { @@ -164,6 +170,7 @@ struct isert_connection { struct isert_wr drain_wr; struct kref kref; + struct isert_portal *portal; void *priv_data; /* for connection tracking */ }; diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index be40338fa..1b334890b 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -45,6 +45,8 @@ static DEFINE_MUTEX(dev_list_mutex); +void isert_portal_free(struct isert_portal *portal); + static int isert_num_recv_posted_on_err(struct ib_recv_wr *first_ib_wr, struct ib_recv_wr *bad_wr) { @@ -1141,6 +1143,8 @@ static void isert_kref_free(struct kref *kref) isert_dev->cq_qps[cq->idx]--; list_del(&isert_conn->portal_node); isert_deref_device(isert_dev); + if (unlikely(isert_conn->portal->state == ISERT_PORTAL_INACTIVE)) + isert_portal_free(isert_conn->portal); mutex_unlock(&dev_list_mutex); rdma_destroy_id(isert_conn->cm_id); @@ -1231,6 +1235,7 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, } isert_conn->state = ISER_CONN_HANDSHAKE; + isert_conn->portal = portal; mutex_lock(&dev_list_mutex); list_add_tail(&isert_conn->portal_node, &portal->conn_list); @@ -1636,6 +1641,17 @@ out: return err; } +void isert_portal_free(struct isert_portal *portal) +{ + lockdep_assert_held(&dev_list_mutex); + + if (!list_empty(&portal->conn_list)) + return; + + kfree(portal); + module_put(THIS_MODULE); +} + void isert_portal_release(struct isert_portal *portal) { struct isert_connection *conn; @@ -1647,15 +1663,14 @@ void isert_portal_release(struct isert_portal *portal) portal->cm_id = NULL; } + isert_portal_list_remove(portal); + mutex_lock(&dev_list_mutex); list_for_each_entry(conn, &portal->conn_list, portal_node) isert_conn_disconnect(conn); + portal->state = ISERT_PORTAL_INACTIVE; + isert_portal_free(portal); mutex_unlock(&dev_list_mutex); - - isert_portal_list_remove(portal); - - kfree(portal); - module_put(THIS_MODULE); } struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len)