summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBart Van Assche <bvanassche@acm.org>2019-04-02 15:58:13 -0400
committerMartin K. Petersen <martin.petersen@oracle.com>2019-04-12 20:20:06 -0400
commit0ca650c13ba2f53cd3592d1a1d054adcd4164ca4 (patch)
tree9b7c794ded0a814cfe933c3aca000ea1d7de0751
parent96e8e26dd8dd8a60ef1d0dc3ef0d952ffa70a39f (diff)
scsi: target/iscsi: Handle too large immediate data buffers correctly
Since target_alloc_sgl() and iscsit_allocate_iovecs() allocate buffer space for se_cmd.data_length bytes and since that number can be smaller than the iSCSI Expected Data Transfer Length (EDTL), ensure that the iSCSI target driver does not attempt to receive more bytes than what fits in the receive buffer. Always receive the full immediate data buffer such that the iSCSI target driver does not attempt to parse immediate data as an iSCSI PDU. Note: the current code base only calls iscsit_get_dataout() if the size of the immediate data buffer does not exceed the buffer size derived from the SCSI CDB. See also target_cmd_size_check(). Cc: Mike Christie <mchristi@redhat.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Hannes Reinecke <hare@suse.de> Cc: Nicholas Bellinger <nab@linux-iscsi.org> Signed-off-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/target/iscsi/iscsi_target.c27
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c1
-rw-r--r--include/target/iscsi/iscsi_target_core.h1
3 files changed, 26 insertions, 3 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 828697015759..8cdea25f1377 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1568,9 +1568,11 @@ iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
1568{ 1568{
1569 struct kvec *iov; 1569 struct kvec *iov;
1570 u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0; 1570 u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0;
1571 u32 payload_length = ntoh24(hdr->dlength); 1571 u32 payload_length;
1572 int iov_ret, data_crc_failed = 0; 1572 int iov_ret, data_crc_failed = 0;
1573 1573
1574 payload_length = min_t(u32, cmd->se_cmd.data_length,
1575 ntoh24(hdr->dlength));
1574 rx_size += payload_length; 1576 rx_size += payload_length;
1575 iov = &cmd->iov_data[0]; 1577 iov = &cmd->iov_data[0];
1576 1578
@@ -2575,14 +2577,33 @@ static int iscsit_handle_immediate_data(
2575 u32 checksum, iov_count = 0, padding = 0; 2577 u32 checksum, iov_count = 0, padding = 0;
2576 struct iscsi_conn *conn = cmd->conn; 2578 struct iscsi_conn *conn = cmd->conn;
2577 struct kvec *iov; 2579 struct kvec *iov;
2580 void *overflow_buf = NULL;
2578 2581
2579 iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done, length); 2582 BUG_ON(cmd->write_data_done > cmd->se_cmd.data_length);
2583 rx_size = min(cmd->se_cmd.data_length - cmd->write_data_done, length);
2584 iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done,
2585 rx_size);
2580 if (iov_ret < 0) 2586 if (iov_ret < 0)
2581 return IMMEDIATE_DATA_CANNOT_RECOVER; 2587 return IMMEDIATE_DATA_CANNOT_RECOVER;
2582 2588
2583 rx_size = length;
2584 iov_count = iov_ret; 2589 iov_count = iov_ret;
2585 iov = &cmd->iov_data[0]; 2590 iov = &cmd->iov_data[0];
2591 if (rx_size < length) {
2592 /*
2593 * Special case: length of immediate data exceeds the data
2594 * buffer size derived from the CDB.
2595 */
2596 overflow_buf = kmalloc(length - rx_size, GFP_KERNEL);
2597 if (!overflow_buf) {
2598 iscsit_unmap_iovec(cmd);
2599 return IMMEDIATE_DATA_CANNOT_RECOVER;
2600 }
2601 cmd->overflow_buf = overflow_buf;
2602 iov[iov_count].iov_base = overflow_buf;
2603 iov[iov_count].iov_len = length - rx_size;
2604 iov_count++;
2605 rx_size = length;
2606 }
2586 2607
2587 padding = ((-length) & 3); 2608 padding = ((-length) & 3);
2588 if (padding != 0) { 2609 if (padding != 0) {
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 5b26bc23016a..fae85bfd790e 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -737,6 +737,7 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd)
737 kfree(cmd->pdu_list); 737 kfree(cmd->pdu_list);
738 kfree(cmd->seq_list); 738 kfree(cmd->seq_list);
739 kfree(cmd->tmr_req); 739 kfree(cmd->tmr_req);
740 kfree(cmd->overflow_buf);
740 kfree(cmd->iov_data); 741 kfree(cmd->iov_data);
741 kfree(cmd->text_in_ptr); 742 kfree(cmd->text_in_ptr);
742 743
diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 24c398f4a68f..a49d37140a64 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -473,6 +473,7 @@ struct iscsi_cmd {
473 struct timer_list dataout_timer; 473 struct timer_list dataout_timer;
474 /* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */ 474 /* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */
475 struct kvec *iov_data; 475 struct kvec *iov_data;
476 void *overflow_buf;
476 /* Iovecs for miscellaneous purposes */ 477 /* Iovecs for miscellaneous purposes */
477#define ISCSI_MISC_IOVECS 5 478#define ISCSI_MISC_IOVECS 5
478 struct kvec iov_misc[ISCSI_MISC_IOVECS]; 479 struct kvec iov_misc[ISCSI_MISC_IOVECS];