bdev_iscsi: fix crash when connecting to a non existing iqn

When connecting to an iscsi url that effectively does not exist we free
the data structures of the very context we are processing. This results in a
use after free and subsequently in a crash.

Signed-off-by: Jeffry Molanus <jeffry.molanus@gmail.com>
Change-Id: I67cab1efb161bfa23fa1022e150661080d90b556
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/462614
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Jeffry Molanus <Jeffry.molanus@gmail.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
Jeffry Molanus 2019-07-19 15:21:10 +02:00 committed by Darek Stojaczyk
parent ed56a3d482
commit bc315deae3

View File

@ -98,9 +98,10 @@ struct bdev_iscsi_conn_req {
char *initiator_iqn; char *initiator_iqn;
struct iscsi_context *context; struct iscsi_context *context;
spdk_bdev_iscsi_create_cb create_cb; spdk_bdev_iscsi_create_cb create_cb;
spdk_bdev_iscsi_create_cb create_cb_arg; void *create_cb_arg;
bool unmap_supported; bool unmap_supported;
int lun; int lun;
int status;
TAILQ_ENTRY(bdev_iscsi_conn_req) link; TAILQ_ENTRY(bdev_iscsi_conn_req) link;
}; };
@ -110,17 +111,12 @@ complete_conn_req(struct bdev_iscsi_conn_req *req, struct spdk_bdev *bdev,
{ {
TAILQ_REMOVE(&g_iscsi_conn_req, req, link); TAILQ_REMOVE(&g_iscsi_conn_req, req, link);
req->create_cb(req->create_cb_arg, bdev, status); req->create_cb(req->create_cb_arg, bdev, status);
if (status) {
/* if the request failed and no iscsi lun was /*
* created then we could not hand over this * we are still running in the context of iscsi_service()
* memory and have to free it manually now. * so do not tear down its data structures here
*/ */
iscsi_destroy_context(req->context); req->status = status;
free(req->initiator_iqn);
free(req->bdev_name);
free(req->url);
}
free(req);
} }
static int static int
@ -145,14 +141,29 @@ _iscsi_free_lun(void *arg)
free(lun); free(lun);
} }
static void
_bdev_iscsi_conn_req_free(struct bdev_iscsi_conn_req *req)
{
free(req->initiator_iqn);
free(req->bdev_name);
free(req->url);
/* destroy will call iscsi_disconnect() implicitly if connected */
iscsi_destroy_context(req->context);
free(req);
}
static void static void
bdev_iscsi_finish(void) bdev_iscsi_finish(void)
{ {
struct bdev_iscsi_conn_req *req; struct bdev_iscsi_conn_req *req, *tmp;
while (!TAILQ_EMPTY(&g_iscsi_conn_req)) { /* clear out pending connection requests here. We cannot
req = TAILQ_FIRST(&g_iscsi_conn_req); * simply set the state to a non SCSI_STATUS_GOOD state as
complete_conn_req(req, NULL, -EINTR); * the connection poller wont run anymore
*/
TAILQ_FOREACH_SAFE(req, &g_iscsi_conn_req, link, tmp) {
_bdev_iscsi_conn_req_free(req);
} }
if (g_conn_poller) { if (g_conn_poller) {
@ -725,8 +736,16 @@ iscsi_bdev_conn_poll(void *arg)
SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(context)); SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(context));
} }
} }
}
if (req->status != SCSI_STATUS_GOOD) {
/*
* An error has occurred during connecting. This req has already
* been removed from the g_iscsi_conn_req list, but we needed to
* wait until iscsi_service unwound before we could free the req.
*/
_bdev_iscsi_conn_req_free(req);
}
}
return -1; return -1;
} }
@ -748,6 +767,7 @@ create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_
return -ENOMEM; return -ENOMEM;
} }
req->status = SCSI_STATUS_GOOD;
req->bdev_name = strdup(bdev_name); req->bdev_name = strdup(bdev_name);
req->url = strdup(url); req->url = strdup(url);
req->initiator_iqn = strdup(initiator_iqn); req->initiator_iqn = strdup(initiator_iqn);