mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-14 09:11:27 +00:00
isert: Fix use-after-free when work requests were submitted after drain_wr
We were submitting work requests sometimes after the drain_wr, so we would try to process flushes on something that is already destroyed. This can be seen with very high login/logout load. Signed-off-by: Yan Burman <yanb@mellanox.com> git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6428 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -155,6 +155,7 @@ struct isert_cq {
|
||||
#define ISERT_CONNECTION_ABORTED 0
|
||||
#define ISERT_DRAIN_POSTED 1
|
||||
#define ISERT_DRAIN_FAILED 2
|
||||
#define ISERT_DISCON_CALLED 3
|
||||
|
||||
struct isert_connection {
|
||||
struct iscsi_conn iscsi ____cacheline_aligned;
|
||||
@@ -210,6 +211,7 @@ struct isert_connection {
|
||||
unsigned long flags;
|
||||
struct work_struct close_work;
|
||||
struct work_struct drain_work;
|
||||
struct work_struct discon_work;
|
||||
struct isert_wr drain_wr;
|
||||
struct kref kref;
|
||||
|
||||
@@ -280,6 +282,7 @@ int isert_alloc_conn_resources(struct isert_connection *isert_conn);
|
||||
void isert_free_conn_resources(struct isert_connection *isert_conn);
|
||||
void isert_conn_free(struct isert_connection *isert_conn);
|
||||
void isert_conn_disconnect(struct isert_connection *isert_conn);
|
||||
void isert_post_drain(struct isert_connection *isert_conn);
|
||||
|
||||
static inline struct isert_connection *isert_conn_alloc(void)
|
||||
{
|
||||
|
||||
@@ -107,6 +107,7 @@ int isert_portal_remove(void *portal_h)
|
||||
void isert_free_connection(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
isert_post_drain(isert_conn);
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,14 +123,12 @@ int isert_post_send(struct isert_connection *isert_conn,
|
||||
return err;
|
||||
}
|
||||
|
||||
void isert_conn_disconnect(struct isert_connection *isert_conn)
|
||||
void isert_post_drain(struct isert_connection *isert_conn)
|
||||
{
|
||||
struct ib_send_wr *bad_wr;
|
||||
int err = rdma_disconnect(isert_conn->cm_id);
|
||||
if (unlikely(err))
|
||||
pr_err("Failed to rdma disconnect, err:%d\n", err);
|
||||
|
||||
if (!test_and_set_bit(ISERT_DRAIN_POSTED, &isert_conn->flags)) {
|
||||
struct ib_send_wr *bad_wr;
|
||||
int err;
|
||||
|
||||
isert_wr_set_fields(&isert_conn->drain_wr, isert_conn, NULL);
|
||||
isert_conn->drain_wr.wr_op = ISER_WR_SEND;
|
||||
isert_conn->drain_wr.send_wr.wr_id = _ptr_to_u64(&isert_conn->drain_wr);
|
||||
@@ -146,6 +144,14 @@ void isert_conn_disconnect(struct isert_connection *isert_conn)
|
||||
}
|
||||
}
|
||||
|
||||
void isert_conn_disconnect(struct isert_connection *isert_conn)
|
||||
{
|
||||
int err = rdma_disconnect(isert_conn->cm_id);
|
||||
|
||||
if (unlikely(err))
|
||||
pr_err("Failed to rdma disconnect, err:%d\n", err);
|
||||
}
|
||||
|
||||
static int isert_pdu_handle_hello_req(struct isert_cmnd *pdu)
|
||||
{
|
||||
pr_info("iSER Hello not supported\n");
|
||||
@@ -498,6 +504,33 @@ static const char *wr_status_str(enum ib_wc_status status)
|
||||
}
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
static void isert_discon_do_work(void *ctx)
|
||||
#else
|
||||
static void isert_discon_do_work(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
struct isert_connection *isert_conn = ctx;
|
||||
#else
|
||||
struct isert_connection *isert_conn =
|
||||
container_of(work, struct isert_connection, discon_work);
|
||||
#endif
|
||||
|
||||
/* notify upper layer */
|
||||
isert_connection_closed(&isert_conn->iscsi);
|
||||
}
|
||||
|
||||
static void isert_sched_discon(struct isert_connection *isert_conn)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
INIT_WORK(&isert_conn->discon_work, isert_discon_do_work, isert_conn);
|
||||
#else
|
||||
INIT_WORK(&isert_conn->discon_work, isert_discon_do_work);
|
||||
#endif
|
||||
isert_conn_queue_work(&isert_conn->discon_work);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
static void isert_conn_drained_do_work(void *ctx)
|
||||
#else
|
||||
@@ -511,10 +544,6 @@ static void isert_conn_drained_do_work(struct work_struct *work)
|
||||
container_of(work, struct isert_connection, drain_work);
|
||||
#endif
|
||||
|
||||
/* notify upper layer */
|
||||
if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags))
|
||||
isert_connection_closed(&isert_conn->iscsi);
|
||||
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
@@ -528,6 +557,38 @@ static void isert_sched_conn_drained(struct isert_connection *isert_conn)
|
||||
isert_conn_queue_work(&isert_conn->drain_work);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
static void isert_conn_closed_do_work(void *ctx)
|
||||
#else
|
||||
static void isert_conn_closed_do_work(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
struct isert_connection *isert_conn = ctx;
|
||||
#else
|
||||
struct isert_connection *isert_conn =
|
||||
container_of(work, struct isert_connection, close_work);
|
||||
#endif
|
||||
|
||||
if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags))
|
||||
if (!test_and_set_bit(ISERT_DISCON_CALLED, &isert_conn->flags))
|
||||
isert_connection_closed(&isert_conn->iscsi);
|
||||
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
static void isert_sched_conn_closed(struct isert_connection *isert_conn)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work,
|
||||
isert_conn);
|
||||
#else
|
||||
INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work);
|
||||
#endif
|
||||
isert_conn_queue_work(&isert_conn->close_work);
|
||||
}
|
||||
|
||||
|
||||
static void isert_handle_wc_error(struct ib_wc *wc)
|
||||
{
|
||||
struct isert_wr *wr = _u64_to_ptr(wc->wr_id);
|
||||
@@ -544,6 +605,10 @@ static void isert_handle_wc_error(struct ib_wc *wc)
|
||||
isert_conn, wr, wr_status_str(wc->status),
|
||||
wc->vendor_err);
|
||||
|
||||
if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags))
|
||||
if (!test_and_set_bit(ISERT_DISCON_CALLED, &isert_conn->flags))
|
||||
isert_sched_discon(isert_conn);
|
||||
|
||||
switch (wr->wr_op) {
|
||||
case ISER_WR_SEND:
|
||||
if (unlikely(wr->send_wr.num_sge == 0)) /* Drain WR */
|
||||
@@ -1204,36 +1269,6 @@ void isert_conn_free(struct isert_connection *isert_conn)
|
||||
kref_put(&isert_conn->kref, isert_kref_free);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
static void isert_conn_closed_do_work(void *ctx)
|
||||
#else
|
||||
static void isert_conn_closed_do_work(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
struct isert_connection *isert_conn = ctx;
|
||||
#else
|
||||
struct isert_connection *isert_conn =
|
||||
container_of(work, struct isert_connection, close_work);
|
||||
#endif
|
||||
|
||||
/* notify upper layer */
|
||||
if (test_bit(ISERT_DRAIN_FAILED, &isert_conn->flags))
|
||||
isert_connection_closed(&isert_conn->iscsi);
|
||||
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
static void isert_sched_conn_closed(struct isert_connection *isert_conn)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work, isert_conn);
|
||||
#else
|
||||
INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work);
|
||||
#endif
|
||||
isert_conn_queue_work(&isert_conn->close_work);
|
||||
}
|
||||
|
||||
static int isert_cm_timewait_exit_handler(struct rdma_cm_id *cm_id,
|
||||
struct rdma_cm_event *event)
|
||||
{
|
||||
@@ -1395,6 +1430,7 @@ static int isert_cm_connect_handler(struct rdma_cm_id *cm_id,
|
||||
isert_conn->peer_addrsz);
|
||||
if (unlikely(ret)) {
|
||||
set_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags);
|
||||
isert_post_drain(isert_conn);
|
||||
isert_conn_free(isert_conn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user