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;
struct iscsi_context *context;
spdk_bdev_iscsi_create_cb create_cb;
spdk_bdev_iscsi_create_cb create_cb_arg;
void *create_cb_arg;
bool unmap_supported;
int lun;
int status;
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);
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
* memory and have to free it manually now.
/*
* we are still running in the context of iscsi_service()
* so do not tear down its data structures here
*/
iscsi_destroy_context(req->context);
free(req->initiator_iqn);
free(req->bdev_name);
free(req->url);
}
free(req);
req->status = status;
}
static int
@ -145,14 +141,29 @@ _iscsi_free_lun(void *arg)
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
bdev_iscsi_finish(void)
{
struct bdev_iscsi_conn_req *req;
struct bdev_iscsi_conn_req *req, *tmp;
while (!TAILQ_EMPTY(&g_iscsi_conn_req)) {
req = TAILQ_FIRST(&g_iscsi_conn_req);
complete_conn_req(req, NULL, -EINTR);
/* clear out pending connection requests here. We cannot
* simply set the state to a non SCSI_STATUS_GOOD state as
* 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) {
@ -725,8 +736,16 @@ iscsi_bdev_conn_poll(void *arg)
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;
}
@ -748,6 +767,7 @@ create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_
return -ENOMEM;
}
req->status = SCSI_STATUS_GOOD;
req->bdev_name = strdup(bdev_name);
req->url = strdup(url);
req->initiator_iqn = strdup(initiator_iqn);