diff options
| author | Xiubo Li <lixiubo@cmss.chinamobile.com> | 2017-03-27 05:07:40 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-21 03:31:18 -0400 |
| commit | ef599fa52429f7f8fea2d1bf83a49b0377c7dc24 (patch) | |
| tree | b7ea0067fac9c316a5b695d210091cae0fd886dd | |
| parent | f44236a1b05b583862eb8b06dd8412660fdc7fad (diff) | |
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>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -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 70c143a5c38c..8d9f5f9c32a7 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 | |||
| 389 | return true; | 389 | return true; |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | static inline size_t tcmu_cmd_get_data_length(struct tcmu_cmd *tcmu_cmd) | ||
| 393 | { | ||
| 394 | struct se_cmd *se_cmd = tcmu_cmd->se_cmd; | ||
| 395 | size_t data_length = round_up(se_cmd->data_length, DATA_BLOCK_SIZE); | ||
| 396 | |||
| 397 | if (se_cmd->se_cmd_flags & SCF_BIDI) { | ||
| 398 | BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); | ||
| 399 | data_length += round_up(se_cmd->t_bidi_data_sg->length, | ||
| 400 | DATA_BLOCK_SIZE); | ||
| 401 | } | ||
| 402 | |||
| 403 | return data_length; | ||
| 404 | } | ||
| 405 | |||
| 392 | static sense_reason_t | 406 | static sense_reason_t |
| 393 | tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | 407 | tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) |
| 394 | { | 408 | { |
| @@ -402,7 +416,7 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
| 402 | uint32_t cmd_head; | 416 | uint32_t cmd_head; |
| 403 | uint64_t cdb_off; | 417 | uint64_t cdb_off; |
| 404 | bool copy_to_data_area; | 418 | bool copy_to_data_area; |
| 405 | size_t data_length; | 419 | size_t data_length = tcmu_cmd_get_data_length(tcmu_cmd); |
| 406 | DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); | 420 | DECLARE_BITMAP(old_bitmap, DATA_BLOCK_BITS); |
| 407 | 421 | ||
| 408 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) | 422 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) |
| @@ -428,11 +442,6 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
| 428 | 442 | ||
| 429 | mb = udev->mb_addr; | 443 | mb = udev->mb_addr; |
| 430 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | 444 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ |
| 431 | data_length = se_cmd->data_length; | ||
| 432 | if (se_cmd->se_cmd_flags & SCF_BIDI) { | ||
| 433 | BUG_ON(!(se_cmd->t_bidi_data_sg && se_cmd->t_bidi_data_nents)); | ||
| 434 | data_length += se_cmd->t_bidi_data_sg->length; | ||
| 435 | } | ||
| 436 | if ((command_size > (udev->cmdr_size / 2)) || | 445 | if ((command_size > (udev->cmdr_size / 2)) || |
| 437 | data_length > udev->data_size) { | 446 | data_length > udev->data_size) { |
| 438 | pr_warn("TCMU: Request of size %zu/%zu is too big for %u/%zu " | 447 | pr_warn("TCMU: Request of size %zu/%zu is too big for %u/%zu " |
| @@ -502,11 +511,14 @@ tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | |||
| 502 | entry->req.iov_dif_cnt = 0; | 511 | entry->req.iov_dif_cnt = 0; |
| 503 | 512 | ||
| 504 | /* Handle BIDI commands */ | 513 | /* Handle BIDI commands */ |
| 505 | iov_cnt = 0; | 514 | if (se_cmd->se_cmd_flags & SCF_BIDI) { |
| 506 | alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, | 515 | iov_cnt = 0; |
| 507 | se_cmd->t_bidi_data_nents, &iov, &iov_cnt, false); | 516 | iov++; |
| 508 | entry->req.iov_bidi_cnt = iov_cnt; | 517 | alloc_and_scatter_data_area(udev, se_cmd->t_bidi_data_sg, |
| 509 | 518 | se_cmd->t_bidi_data_nents, &iov, &iov_cnt, | |
| 519 | false); | ||
| 520 | entry->req.iov_bidi_cnt = iov_cnt; | ||
| 521 | } | ||
| 510 | /* cmd's data_bitmap is what changed in process */ | 522 | /* cmd's data_bitmap is what changed in process */ |
| 511 | bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, | 523 | bitmap_xor(tcmu_cmd->data_bitmap, old_bitmap, udev->data_bitmap, |
| 512 | DATA_BLOCK_BITS); | 524 | DATA_BLOCK_BITS); |
