diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-10-28 02:19:26 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-11-07 22:50:24 -0500 |
commit | 1c21a48055a67ceb693e9c2587824a8de60a217c (patch) | |
tree | 43fd75f6f3cfc05510242b7d9a40fa9740b9b18a | |
parent | 9574a497df2bbc0a676b609ce0dd24d237cee3a6 (diff) |
target: Avoid early CMD_T_PRE_EXECUTE failures during ABORT_TASK
This patch fixes bug where early se_cmd exceptions that occur
before backend execution can result in use-after-free if/when
a subsequent ABORT_TASK occurs for the same tag.
Since an early se_cmd exception will have had se_cmd added to
se_session->sess_cmd_list via target_get_sess_cmd(), it will
not have CMD_T_COMPLETE set by the usual target_complete_cmd()
backend completion path.
This causes a subsequent ABORT_TASK + __target_check_io_state()
to signal ABORT_TASK should proceed. As core_tmr_abort_task()
executes, it will bring the outstanding se_cmd->cmd_kref count
down to zero releasing se_cmd, after se_cmd has already been
queued with error status into fabric driver response path code.
To address this bug, introduce a CMD_T_PRE_EXECUTE bit that is
set at target_get_sess_cmd() time, and cleared immediately before
backend driver dispatch in target_execute_cmd() once CMD_T_ACTIVE
is set.
Then, check CMD_T_PRE_EXECUTE within __target_check_io_state() to
determine when an early exception has occured, and avoid aborting
this se_cmd since it will have already been queued into fabric
driver response path code.
Reported-by: Donald White <dew@datera.io>
Cc: Donald White <dew@datera.io>
Cc: Mike Christie <mchristi@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: stable@vger.kernel.org # 3.14+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_tmr.c | 9 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 2 | ||||
-rw-r--r-- | include/target/target_core_base.h | 1 |
3 files changed, 12 insertions, 0 deletions
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 61909b23e959..9c7bc1ca341a 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c | |||
@@ -133,6 +133,15 @@ static bool __target_check_io_state(struct se_cmd *se_cmd, | |||
133 | spin_unlock(&se_cmd->t_state_lock); | 133 | spin_unlock(&se_cmd->t_state_lock); |
134 | return false; | 134 | return false; |
135 | } | 135 | } |
136 | if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) { | ||
137 | if (se_cmd->scsi_status) { | ||
138 | pr_debug("Attempted to abort io tag: %llu early failure" | ||
139 | " status: 0x%02x\n", se_cmd->tag, | ||
140 | se_cmd->scsi_status); | ||
141 | spin_unlock(&se_cmd->t_state_lock); | ||
142 | return false; | ||
143 | } | ||
144 | } | ||
136 | if (sess->sess_tearing_down || se_cmd->cmd_wait_set) { | 145 | if (sess->sess_tearing_down || se_cmd->cmd_wait_set) { |
137 | pr_debug("Attempted to abort io tag: %llu already shutdown," | 146 | pr_debug("Attempted to abort io tag: %llu already shutdown," |
138 | " skipping\n", se_cmd->tag); | 147 | " skipping\n", se_cmd->tag); |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0e89db84b200..58caacd54a3b 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -1975,6 +1975,7 @@ void target_execute_cmd(struct se_cmd *cmd) | |||
1975 | } | 1975 | } |
1976 | 1976 | ||
1977 | cmd->t_state = TRANSPORT_PROCESSING; | 1977 | cmd->t_state = TRANSPORT_PROCESSING; |
1978 | cmd->transport_state &= ~CMD_T_PRE_EXECUTE; | ||
1978 | cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; | 1979 | cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; |
1979 | spin_unlock_irq(&cmd->t_state_lock); | 1980 | spin_unlock_irq(&cmd->t_state_lock); |
1980 | 1981 | ||
@@ -2667,6 +2668,7 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref) | |||
2667 | ret = -ESHUTDOWN; | 2668 | ret = -ESHUTDOWN; |
2668 | goto out; | 2669 | goto out; |
2669 | } | 2670 | } |
2671 | se_cmd->transport_state |= CMD_T_PRE_EXECUTE; | ||
2670 | list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); | 2672 | list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); |
2671 | out: | 2673 | out: |
2672 | spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); | 2674 | spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); |
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index d3139a95ea77..ccf501b8359c 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h | |||
@@ -490,6 +490,7 @@ struct se_cmd { | |||
490 | #define CMD_T_STOP (1 << 5) | 490 | #define CMD_T_STOP (1 << 5) |
491 | #define CMD_T_TAS (1 << 10) | 491 | #define CMD_T_TAS (1 << 10) |
492 | #define CMD_T_FABRIC_STOP (1 << 11) | 492 | #define CMD_T_FABRIC_STOP (1 << 11) |
493 | #define CMD_T_PRE_EXECUTE (1 << 12) | ||
493 | spinlock_t t_state_lock; | 494 | spinlock_t t_state_lock; |
494 | struct kref cmd_kref; | 495 | struct kref cmd_kref; |
495 | struct completion t_transport_stop_comp; | 496 | struct completion t_transport_stop_comp; |