diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-09-05 11:09:15 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-09-05 20:20:28 -0400 |
commit | d5829eac5f7cfff89c6d1cf11717eee97cf030d0 (patch) | |
tree | 9acff1b99c654235b5ad4534735fdaf03a9c5a45 | |
parent | 27a2709912ac19c755d34c79fe11994b0bf8082b (diff) |
target: fix use-after-free with PSCSI sense data
The pointer to the sense buffer is fetched by transport_get_sense_data,
but this is called by target_complete_ok_work long after pscsi_req_done
has freed the struct that contains it.
Pass instead the fabric's sense buffer to transport_complete,
and copy the data to it directly in transport_complete. Setting
SCF_TRANSPORT_TASK_SENSE also becomes a duty of transport_complete.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_pscsi.c | 21 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 36 | ||||
-rw-r--r-- | include/target/target_core_backend.h | 4 |
3 files changed, 24 insertions, 37 deletions
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 5552fa7426bc..5f7151d90344 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c | |||
@@ -667,7 +667,8 @@ static void pscsi_free_device(void *p) | |||
667 | kfree(pdv); | 667 | kfree(pdv); |
668 | } | 668 | } |
669 | 669 | ||
670 | static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg) | 670 | static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg, |
671 | unsigned char *sense_buffer) | ||
671 | { | 672 | { |
672 | struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr; | 673 | struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr; |
673 | struct scsi_device *sd = pdv->pdv_sd; | 674 | struct scsi_device *sd = pdv->pdv_sd; |
@@ -679,7 +680,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg) | |||
679 | * not been allocated because TCM is handling the emulation directly. | 680 | * not been allocated because TCM is handling the emulation directly. |
680 | */ | 681 | */ |
681 | if (!pt) | 682 | if (!pt) |
682 | return 0; | 683 | return; |
683 | 684 | ||
684 | cdb = &pt->pscsi_cdb[0]; | 685 | cdb = &pt->pscsi_cdb[0]; |
685 | result = pt->pscsi_result; | 686 | result = pt->pscsi_result; |
@@ -750,10 +751,10 @@ after_mode_sense: | |||
750 | } | 751 | } |
751 | after_mode_select: | 752 | after_mode_select: |
752 | 753 | ||
753 | if (status_byte(result) & CHECK_CONDITION) | 754 | if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) { |
754 | return 1; | 755 | memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER); |
755 | 756 | cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; | |
756 | return 0; | 757 | } |
757 | } | 758 | } |
758 | 759 | ||
759 | enum { | 760 | enum { |
@@ -1184,13 +1185,6 @@ fail: | |||
1184 | return -ENOMEM; | 1185 | return -ENOMEM; |
1185 | } | 1186 | } |
1186 | 1187 | ||
1187 | static unsigned char *pscsi_get_sense_buffer(struct se_cmd *cmd) | ||
1188 | { | ||
1189 | struct pscsi_plugin_task *pt = cmd->priv; | ||
1190 | |||
1191 | return pt->pscsi_sense; | ||
1192 | } | ||
1193 | |||
1194 | /* pscsi_get_device_rev(): | 1188 | /* pscsi_get_device_rev(): |
1195 | * | 1189 | * |
1196 | * | 1190 | * |
@@ -1273,7 +1267,6 @@ static struct se_subsystem_api pscsi_template = { | |||
1273 | .check_configfs_dev_params = pscsi_check_configfs_dev_params, | 1267 | .check_configfs_dev_params = pscsi_check_configfs_dev_params, |
1274 | .set_configfs_dev_params = pscsi_set_configfs_dev_params, | 1268 | .set_configfs_dev_params = pscsi_set_configfs_dev_params, |
1275 | .show_configfs_dev_params = pscsi_show_configfs_dev_params, | 1269 | .show_configfs_dev_params = pscsi_show_configfs_dev_params, |
1276 | .get_sense_buffer = pscsi_get_sense_buffer, | ||
1277 | .get_device_rev = pscsi_get_device_rev, | 1270 | .get_device_rev = pscsi_get_device_rev, |
1278 | .get_device_type = pscsi_get_device_type, | 1271 | .get_device_type = pscsi_get_device_type, |
1279 | .get_blocks = pscsi_get_blocks, | 1272 | .get_blocks = pscsi_get_blocks, |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 040f05fde4d6..8a29e3fd0195 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -568,38 +568,31 @@ static void target_complete_failure_work(struct work_struct *work) | |||
568 | } | 568 | } |
569 | 569 | ||
570 | /* | 570 | /* |
571 | * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd | 571 | * Used when asking transport to copy Sense Data from the underlying |
572 | * Linux/SCSI struct scsi_cmnd | ||
572 | */ | 573 | */ |
573 | static void transport_get_sense_data(struct se_cmd *cmd) | 574 | static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd) |
574 | { | 575 | { |
575 | unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL; | 576 | unsigned char *buffer = cmd->sense_buffer; |
576 | struct se_device *dev = cmd->se_dev; | 577 | struct se_device *dev = cmd->se_dev; |
577 | unsigned long flags; | ||
578 | u32 offset = 0; | 578 | u32 offset = 0; |
579 | 579 | ||
580 | WARN_ON(!cmd->se_lun); | 580 | WARN_ON(!cmd->se_lun); |
581 | 581 | ||
582 | if (!dev) | 582 | if (!dev) |
583 | return; | 583 | return NULL; |
584 | |||
585 | spin_lock_irqsave(&cmd->t_state_lock, flags); | ||
586 | if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { | ||
587 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | ||
588 | return; | ||
589 | } | ||
590 | 584 | ||
591 | sense_buffer = dev->transport->get_sense_buffer(cmd); | 585 | if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) |
592 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 586 | return NULL; |
593 | 587 | ||
594 | offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER); | 588 | offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER); |
595 | 589 | ||
596 | memcpy(&buffer[offset], sense_buffer, TRANSPORT_SENSE_BUFFER); | ||
597 | |||
598 | /* Automatically padded */ | 590 | /* Automatically padded */ |
599 | cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset; | 591 | cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset; |
600 | 592 | ||
601 | pr_debug("HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x and sense\n", | 593 | pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n", |
602 | dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status); | 594 | dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status); |
595 | return &buffer[offset]; | ||
603 | } | 596 | } |
604 | 597 | ||
605 | void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) | 598 | void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) |
@@ -615,11 +608,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) | |||
615 | cmd->transport_state &= ~CMD_T_BUSY; | 608 | cmd->transport_state &= ~CMD_T_BUSY; |
616 | 609 | ||
617 | if (dev && dev->transport->transport_complete) { | 610 | if (dev && dev->transport->transport_complete) { |
618 | if (dev->transport->transport_complete(cmd, | 611 | dev->transport->transport_complete(cmd, |
619 | cmd->t_data_sg) != 0) { | 612 | cmd->t_data_sg, |
620 | cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; | 613 | transport_get_sense_buffer(cmd)); |
614 | if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) | ||
621 | success = 1; | 615 | success = 1; |
622 | } | ||
623 | } | 616 | } |
624 | 617 | ||
625 | /* | 618 | /* |
@@ -1987,12 +1980,11 @@ static void target_complete_ok_work(struct work_struct *work) | |||
1987 | schedule_work(&cmd->se_dev->qf_work_queue); | 1980 | schedule_work(&cmd->se_dev->qf_work_queue); |
1988 | 1981 | ||
1989 | /* | 1982 | /* |
1990 | * Check if we need to retrieve a sense buffer from | 1983 | * Check if we need to send a sense buffer from |
1991 | * the struct se_cmd in question. | 1984 | * the struct se_cmd in question. |
1992 | */ | 1985 | */ |
1993 | if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { | 1986 | if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { |
1994 | WARN_ON(!cmd->scsi_status); | 1987 | WARN_ON(!cmd->scsi_status); |
1995 | transport_get_sense_data(cmd); | ||
1996 | ret = transport_send_check_condition_and_sense( | 1988 | ret = transport_send_check_condition_and_sense( |
1997 | cmd, 0, 1); | 1989 | cmd, 0, 1); |
1998 | if (ret == -EAGAIN || ret == -ENOMEM) | 1990 | if (ret == -EAGAIN || ret == -ENOMEM) |
diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index f1405d335a96..941c84bf1065 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h | |||
@@ -23,7 +23,9 @@ struct se_subsystem_api { | |||
23 | struct se_device *(*create_virtdevice)(struct se_hba *, | 23 | struct se_device *(*create_virtdevice)(struct se_hba *, |
24 | struct se_subsystem_dev *, void *); | 24 | struct se_subsystem_dev *, void *); |
25 | void (*free_device)(void *); | 25 | void (*free_device)(void *); |
26 | int (*transport_complete)(struct se_cmd *cmd, struct scatterlist *); | 26 | void (*transport_complete)(struct se_cmd *cmd, |
27 | struct scatterlist *, | ||
28 | unsigned char *); | ||
27 | 29 | ||
28 | int (*parse_cdb)(struct se_cmd *cmd); | 30 | int (*parse_cdb)(struct se_cmd *cmd); |
29 | ssize_t (*check_configfs_dev_params)(struct se_hba *, | 31 | ssize_t (*check_configfs_dev_params)(struct se_hba *, |