iscsi: fix segfault when r2t

Fixes #2781

This patch fixes two issue causing segfault on r2t:
1. pdu buffer is allocated from immediate_data_pool, but data_buf_len is set as data_out_pool
2. task->desired_data_transfer_length is rewrite by iscsi_send_r2t, which causes a wrong calculated pdu->data_buf_len

Signed-off-by: melon.masou <melon.masou@outlook.com>
Change-Id: I151859afff7104f29ad7f0ec57a8479d88b742bd
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15542
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
melon.masou 2022-11-21 17:29:09 +08:00 committed by Ben Walker
parent 3d9b062852
commit 565a44628d
2 changed files with 37 additions and 19 deletions

View File

@ -4156,6 +4156,16 @@ iscsi_pdu_hdr_op_snack(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
return rc; return rc;
} }
static inline uint32_t
iscsi_get_mobj_max_data_len(struct spdk_mobj *mobj)
{
if (mobj->mp == g_iscsi.pdu_immediate_data_pool) {
return iscsi_get_max_immediate_data_size();
} else {
return SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH;
}
}
static int static int
iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
{ {
@ -4169,6 +4179,7 @@ iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
uint32_t DataSN; uint32_t DataSN;
uint32_t buffer_offset; uint32_t buffer_offset;
uint32_t len; uint32_t len;
uint32_t current_desired_data_transfer_length;
int F_bit; int F_bit;
int rc; int rc;
@ -4195,6 +4206,7 @@ iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
} }
lun_dev = spdk_scsi_dev_get_lun(conn->dev, task->lun_id); lun_dev = spdk_scsi_dev_get_lun(conn->dev, task->lun_id);
current_desired_data_transfer_length = task->desired_data_transfer_length;
if (pdu->data_segment_len > task->desired_data_transfer_length) { if (pdu->data_segment_len > task->desired_data_transfer_length) {
SPDK_ERRLOG("the dataout pdu data length is larger than the value sent by R2T PDU\n"); SPDK_ERRLOG("the dataout pdu data length is larger than the value sent by R2T PDU\n");
@ -4272,7 +4284,7 @@ iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
* SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH to merge them into a * SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH to merge them into a
* single subtask. * single subtask.
*/ */
pdu->data_buf_len = spdk_min(task->desired_data_transfer_length, pdu->data_buf_len = spdk_min(current_desired_data_transfer_length,
SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH); SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
} }
} else { } else {
@ -4280,7 +4292,7 @@ iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
pdu->mobj[0] = mobj; pdu->mobj[0] = mobj;
pdu->data = (void *)((uint64_t)mobj->buf + mobj->data_len); pdu->data = (void *)((uint64_t)mobj->buf + mobj->data_len);
pdu->data_from_mempool = true; pdu->data_from_mempool = true;
pdu->data_buf_len = SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH); pdu->data_buf_len = SPDK_BDEV_BUF_SIZE_WITH_MD(iscsi_get_mobj_max_data_len(mobj));
iscsi_task_set_mobj(task, NULL); iscsi_task_set_mobj(task, NULL);
} }
@ -4626,7 +4638,7 @@ iscsi_pdu_payload_read(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
pdu->mobj[0] = mobj; pdu->mobj[0] = mobj;
pdu->data = mobj->buf; pdu->data = mobj->buf;
pdu->data_from_mempool = true; pdu->data_from_mempool = true;
} else if (mobj->data_len == SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH && read_len > 0) { } else if (mobj->data_len == iscsi_get_mobj_max_data_len(mobj) && read_len > 0) {
mobj = pdu->mobj[1]; mobj = pdu->mobj[1];
if (mobj == NULL) { if (mobj == NULL) {
/* The first data buffer just ran out. Allocate the second data buffer and /* The first data buffer just ran out. Allocate the second data buffer and
@ -4650,7 +4662,7 @@ iscsi_pdu_payload_read(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
} }
/* copy the actual data into local buffer */ /* copy the actual data into local buffer */
read_len = spdk_min(read_len, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH - mobj->data_len); read_len = spdk_min(read_len, iscsi_get_mobj_max_data_len(mobj) - mobj->data_len);
if (read_len > 0) { if (read_len > 0) {
rc = iscsi_conn_read_data_segment(conn, rc = iscsi_conn_read_data_segment(conn,

View File

@ -122,6 +122,21 @@ DEFINE_STUB(spdk_scsi_lun_get_dif_ctx, bool,
(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task, (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task,
struct spdk_dif_ctx *dif_ctx), false); struct spdk_dif_ctx *dif_ctx), false);
static void
alloc_mock_mobj(struct spdk_mobj *mobj, int len)
{
mobj->buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(len));
SPDK_CU_ASSERT_FATAL(mobj->buf != NULL);
g_iscsi.pdu_immediate_data_pool = (struct spdk_mempool *)100;
g_iscsi.pdu_data_out_pool = (struct spdk_mempool *)200;
if (len == SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH) {
mobj->mp = g_iscsi.pdu_data_out_pool;
} else {
mobj->mp = g_iscsi.pdu_immediate_data_pool;
}
}
static void static void
op_login_check_target_test(void) op_login_check_target_test(void)
{ {
@ -2037,11 +2052,8 @@ pdu_payload_read_test(void)
g_iscsi.FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH; g_iscsi.FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH;
mobj1.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH)); alloc_mock_mobj(&mobj1, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
SPDK_CU_ASSERT_FATAL(mobj1.buf != NULL); alloc_mock_mobj(&mobj2, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
mobj2.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
SPDK_CU_ASSERT_FATAL(mobj2.buf != NULL);
MOCK_SET(spdk_mempool_get, &mobj1); MOCK_SET(spdk_mempool_get, &mobj1);
@ -2212,14 +2224,9 @@ data_out_pdu_sequence_test(void)
TAILQ_INSERT_TAIL(&dev.luns, &lun, tailq); TAILQ_INSERT_TAIL(&dev.luns, &lun, tailq);
mobj1.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH)); alloc_mock_mobj(&mobj1, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
SPDK_CU_ASSERT_FATAL(mobj1.buf != NULL); alloc_mock_mobj(&mobj2, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
alloc_mock_mobj(&mobj3, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
mobj2.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
SPDK_CU_ASSERT_FATAL(mobj2.buf != NULL);
mobj3.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH));
SPDK_CU_ASSERT_FATAL(mobj3.buf != NULL);
/* Test scenario is as follows. /* Test scenario is as follows.
* *
@ -2382,8 +2389,7 @@ immediate_data_and_data_out_pdu_sequence_test(void)
TAILQ_INSERT_TAIL(&dev.luns, &lun, tailq); TAILQ_INSERT_TAIL(&dev.luns, &lun, tailq);
mobj.buf = calloc(1, SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH)); alloc_mock_mobj(&mobj, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
SPDK_CU_ASSERT_FATAL(mobj.buf != NULL);
/* Test scenario is as follows. /* Test scenario is as follows.
* *