aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2017-10-28 02:19:26 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2017-11-07 22:50:24 -0500
commit1c21a48055a67ceb693e9c2587824a8de60a217c (patch)
tree43fd75f6f3cfc05510242b7d9a40fa9740b9b18a
parent9574a497df2bbc0a676b609ce0dd24d237cee3a6 (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.c9
-rw-r--r--drivers/target/target_core_transport.c2
-rw-r--r--include/target/target_core_base.h1
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);
2671out: 2673out:
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;