| From 9c2ac436243e10bfca64d840f02c0867d79894de Mon Sep 17 00:00:00 2001 |
| From: Xiubo Li <lixiubo@cmss.chinamobile.com> |
| Date: Mon, 27 Mar 2017 17:07:40 +0800 |
| Subject: [PATCH] tcmu: Fix possible overwrite of t_data_sg's last iov[] |
| |
| commit ab22d2604c86ceb01bb2725c9860b88a7dd383bb upstream. |
| |
| If there has BIDI data, its first iov[] will overwrite the last |
| iov[] for se_cmd->t_data_sg. |
| |
| To fix this, we can just increase the iov pointer, but this may |
| introuduce a new memory leakage bug: If the se_cmd->data_length |
| and se_cmd->t_bidi_data_sg->length are all not aligned up to the |
| DATA_BLOCK_SIZE, the actual length needed maybe larger than just |
| sum of them. |
| |
| So, this could be avoided by rounding all the data lengthes up |
| to DATA_BLOCK_SIZE. |
| |
| Reviewed-by: Mike Christie <mchristi@redhat.com> |
| Tested-by: Ilias Tsitsimpis <iliastsi@arrikto.com> |
| Reviewed-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com> |
| Signed-off-by: Xiubo Li <lixiubo@cmss.chinamobile.com> |
| Cc: stable@vger.kernel.org # 3.18+ |
| Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c |
| index b8a986c2c567..2ffd4177feaa 100644 |
| --- a/drivers/target/target_core_user.c |
| +++ b/drivers/target/target_core_user.c |
| @@ -389,6 +389,20 @@ static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t d |
| return true; |
| } |
| |
| +static inline size_t tcmu_cmd_get_data_length(struct tcmu_cmd *tcmu_cmd) |
| +{ |
| + struct se_cmd *se_cmd = tcmu_cmd->se_cmd; |
| + size_t data_length = round_up(se_cmd->data_length, DATA_BLOCK_SIZE); |
| + |
| + if (se_cmd->se_cmd_flags & SCF_BIDI) { |
| + BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); |
| + data_length += round_up(se_cmd->t_bidi_data_sg->length, |
| + DATA_BLOCK_SIZE); |
| + } |
| + |
| + return data_length; |
| +} |
| + |
| static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
| { |
| struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; |
| @@ -401,7 +415,7 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
| uint32_t cmd_head; |
| uint64_t cdb_off; |
| bool copy_to_data_area; |
| - size_t data_length; |
| + size_t data_length = tcmu_cmd_get_data_length(tcmu_cmd); |
| DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); |
| |
| if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) |
| @@ -427,11 +441,6 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
| |
| mb = udev->mb_addr; |
| cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ |
| - data_length = se_cmd->data_length; |
| - if (se_cmd->se_cmd_flags & SCF_BIDI) { |
| - BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); |
| - data_length += se_cmd->t_bidi_data_sg->length; |
| - } |
| if ((command_size > (udev->cmdr_size / 2)) |
| || data_length > udev->data_size) |
| pr_warn("TCMU: Request of size %zu/%zu may be too big for %u/%zu " |
| @@ -500,11 +509,14 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
| entry->req.iov_dif_cnt = 0; |
| |
| /* Handle BIDI commands */ |
| - iov_cnt = 0; |
| - alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, |
| - se_cmd->t_bidi_data_nents, &iov, &iov_cnt, false); |
| - entry->req.iov_bidi_cnt = iov_cnt; |
| - |
| + if (se_cmd->se_cmd_flags & SCF_BIDI) { |
| + iov_cnt = 0; |
| + iov++; |
| + alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, |
| + se_cmd->t_bidi_data_nents, &iov, &iov_cnt, |
| + false); |
| + entry->req.iov_bidi_cnt = iov_cnt; |
| + } |
| /* cmd's data_bitmap is what changed in process */ |
| bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, |
| DATA_BLOCK_BITS); |
| -- |
| 2.12.0 |
| |