| From 72b59d6ee8adaa51f70377db0a1917ed489bead8 Mon Sep 17 00:00:00 2001 |
| From: Roland Dreier <roland@purestorage.com> |
| Date: Wed, 2 Jan 2013 12:47:58 -0800 |
| Subject: target: Fix use-after-free in LUN RESET handling |
| |
| From: Roland Dreier <roland@purestorage.com> |
| |
| commit 72b59d6ee8adaa51f70377db0a1917ed489bead8 upstream. |
| |
| If a backend IO takes a really long then an initiator might abort a |
| command, and then when it gives up on the abort, send a LUN reset too, |
| all before we process any of the original command or the abort. (The |
| abort will wait for the backend IO to complete too) |
| |
| When the backend IO final completes (or fails), the abort handling |
| will proceed and queue up a "return aborted status" operation. Then, |
| while that's still pending, the LUN reset might find the original |
| command still on the LUN's list of commands and try to return aborted |
| status again, which leads to a use-after free when the first |
| se_tfo->queue_status call frees the command and then the second |
| se_tfo->queue_status call runs. |
| |
| Fix this by removing a command from the LUN state_list when we first |
| are about to queue aborted status; we shouldn't do anything |
| LUN-related after we've started returning status, so this seems like |
| the correct thing to do. |
| |
| Signed-off-by: Roland Dreier <roland@purestorage.com> |
| Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/target/target_core_transport.c | 5 ++--- |
| 1 file changed, 2 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/target/target_core_transport.c |
| +++ b/drivers/target/target_core_transport.c |
| @@ -545,9 +545,6 @@ static void transport_lun_remove_cmd(str |
| |
| void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) |
| { |
| - if (!(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) |
| - transport_lun_remove_cmd(cmd); |
| - |
| if (transport_cmd_check_stop_to_fabric(cmd)) |
| return; |
| if (remove) |
| @@ -3090,6 +3087,8 @@ void transport_send_task_abort(struct se |
| } |
| cmd->scsi_status = SAM_STAT_TASK_ABORTED; |
| |
| + transport_lun_remove_cmd(cmd); |
| + |
| pr_debug("Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x," |
| " ITT: 0x%08x\n", cmd->t_task_cdb[0], |
| cmd->se_tfo->get_task_tag(cmd)); |