diff options
author | Xiubo Li <lixiubo@cmss.chinamobile.com> | 2017-03-27 05:07:40 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-03-30 04:36:52 -0400 |
commit | ab22d2604c86ceb01bb2725c9860b88a7dd383bb (patch) | |
tree | 8b8db155ecf02c3219d796a361baa314b9651311 /drivers/target | |
parent | 49cb77e297dc611a1b795cfeb79452b3002bd331 (diff) |
tcmu: Fix possible overwrite of t_data_sg's last iov[]
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>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_user.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 6a17c78e4662..e58dfd4fe448 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c | |||
@@ -394,6 +394,20 @@ static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t d | |||
394 | return true; | 394 | return true; |
395 | } | 395 | } |
396 | 396 | ||
397 | static inline size_t tcmu_cmd_get_data_length(struct tcmu_cmd *tcmu_cmd) | ||
398 | { | ||
399 | struct se_cmd *se_cmd = tcmu_cmd->se_cmd; | ||
400 | size_t data_length = round_up(se_cmd->data_length, DATA_BLOCK_SIZE); | ||
401 | |||
402 | if (se_cmd->se_cmd_flags & SCF_BIDI) { | ||
403 | BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); | ||
404 | data_length += round_up(se_cmd->t_bidi_data_sg->length, | ||
405 | DATA_BLOCK_SIZE); | ||
406 | } | ||
407 | |||
408 | return data_length; | ||
409 | } | ||
410 | |||
397 | static sense_reason_t | 411 | static sense_reason_t |
398 | tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | 412 | tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
399 | { | 413 | { |
@@ -407,7 +421,7 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
407 | uint32_t cmd_head; | 421 | uint32_t cmd_head; |
408 | uint64_t cdb_off; | 422 | uint64_t cdb_off; |
409 | bool copy_to_data_area; | 423 | bool copy_to_data_area; |
410 | size_t data_length; | 424 | size_t data_length = tcmu_cmd_get_data_length(tcmu_cmd); |
411 | DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); | 425 | DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); |
412 | 426 | ||
413 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) | 427 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) |
@@ -433,11 +447,6 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
433 | 447 | ||
434 | mb = udev->mb_addr; | 448 | mb = udev->mb_addr; |
435 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | 449 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ |
436 | data_length = se_cmd->data_length; | ||
437 | if (se_cmd->se_cmd_flags & SCF_BIDI) { | ||
438 | BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); | ||
439 | data_length += se_cmd->t_bidi_data_sg->length; | ||
440 | } | ||
441 | if ((command_size > (udev->cmdr_size / 2)) || | 450 | if ((command_size > (udev->cmdr_size / 2)) || |
442 | data_length > udev->data_size) { | 451 | data_length > udev->data_size) { |
443 | pr_warn("TCMU: Request of size %zu/%zu is too big for %u/%zu " | 452 | pr_warn("TCMU: Request of size %zu/%zu is too big for %u/%zu " |
@@ -511,11 +520,14 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
511 | entry->req.iov_dif_cnt = 0; | 520 | entry->req.iov_dif_cnt = 0; |
512 | 521 | ||
513 | /* Handle BIDI commands */ | 522 | /* Handle BIDI commands */ |
514 | iov_cnt = 0; | 523 | if (se_cmd->se_cmd_flags & SCF_BIDI) { |
515 | alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, | 524 | iov_cnt = 0; |
516 | se_cmd->t_bidi_data_nents, &iov, &iov_cnt, false); | 525 | iov++; |
517 | entry->req.iov_bidi_cnt = iov_cnt; | 526 | alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, |
518 | 527 | se_cmd->t_bidi_data_nents, &iov, &iov_cnt, | |
528 | false); | ||
529 | entry->req.iov_bidi_cnt = iov_cnt; | ||
530 | } | ||
519 | /* cmd's data_bitmap is what changed in process */ | 531 | /* cmd's data_bitmap is what changed in process */ |
520 | bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, | 532 | bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, |
521 | DATA_BLOCK_BITS); | 533 | DATA_BLOCK_BITS); |