]> git.hungrycats.org Git - linux/commitdiff
target: Avoid early CMD_T_PRE_EXECUTE failures during ABORT_TASK
authorNicholas Bellinger <nab@linux-iscsi.org>
Sat, 28 Oct 2017 06:19:26 +0000 (22:19 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Jan 2018 08:29:31 +0000 (09:29 +0100)
commit 1c21a48055a67ceb693e9c2587824a8de60a217c upstream.

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>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/target/target_core_tmr.c
drivers/target/target_core_transport.c
include/target/target_core_base.h

index 9b1a4000d0f9354b95b32be7328e06ef25b47990..93724738000a96d3d2a6c9aa3e8bb49ce42b20a2 100644 (file)
@@ -137,6 +137,15 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
                spin_unlock(&se_cmd->t_state_lock);
                return false;
        }
+       if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) {
+               if (se_cmd->scsi_status) {
+                       pr_debug("Attempted to abort io tag: %u early failure"
+                                " status: 0x%02x\n", se_cmd->se_tfo->get_task_tag(se_cmd),
+                                se_cmd->scsi_status);
+                       spin_unlock(&se_cmd->t_state_lock);
+                       return false;
+               }
+       }
        if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
                pr_debug("Attempted to abort io tag: %u already shutdown,"
                        " skipping\n", se_cmd->se_tfo->get_task_tag(se_cmd));
index e59c8b3fd4f16669d553e18069d4b994ed20d9ed..26afe1c74ef461270e0ed1f5bb0a9a8383bb3a5d 100644 (file)
@@ -1796,6 +1796,7 @@ void target_execute_cmd(struct se_cmd *cmd)
        }
 
        cmd->t_state = TRANSPORT_PROCESSING;
+       cmd->transport_state &= ~CMD_T_PRE_EXECUTE;
        cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
        spin_unlock_irq(&cmd->t_state_lock);
        /*
@@ -2436,6 +2437,7 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
                ret = -ESHUTDOWN;
                goto out;
        }
+       se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
        list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
 out:
        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
index ab4bff088898267466ec5649822514c14668c36b..ea5e809825f890ad23920f2b851a929fe949cdf0 100644 (file)
@@ -538,6 +538,7 @@ struct se_cmd {
 #define CMD_T_BUSY             (1 << 9)
 #define CMD_T_TAS              (1 << 10)
 #define CMD_T_FABRIC_STOP      (1 << 11)
+#define CMD_T_PRE_EXECUTE      (1 << 12)
        spinlock_t              t_state_lock;
        struct completion       t_transport_stop_comp;