Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: Dmitry Bogdanov <d.bogdanov@yadro.com>
To: <sashiko-reviews@lists.linux.dev>
Cc: <target-devel@vger.kernel.org>, <linux-scsi@vger.kernel.org>
Subject: Re: [PATCH 2/2] scsi: target: iscsi: fix hanging WRITE_PENDING commands
Date: Wed, 28 Oct 2026 01:55:45 +0300	[thread overview]
Message-ID: <20261027225545.GA32229@yadro.com> (raw)
In-Reply-To: <sashiko-outbox-187009@kernel.org>

On Tue, Oct 27, 2026 at 06:18:12PM +0000, sashiko-bot@kernel.org wrote:
> 
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] scsi: target: iscsi: double completion in iscsit_release_commands_from_conn()
> --
> 
> commit 31a87057639fce26dc0f2f4bbcd6ddacf96b85a5
> Author: Dmitry Bogdanov <d.bogdanov@yadro.com>
> 
> scsi: target: iscsi: fix hanging WRITE_PENDING commands
> 
> This commit removes early CMD_T_ABORTED checks in the iSCSI Data-Out and
> command execution paths so that aborted WRITE commands in WRITE_PENDING state
> are passed to the target core once all Data-Out PDUs have been received.
> 
> > diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
> > index 62ada3a52210e..03d618cd76721 100644
> > --- a/drivers/target/iscsi/iscsi_target.c
> > +++ b/drivers/target/iscsi/iscsi_target.c
> > @@ -1518,23 +1518,6 @@ __iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf,
> >                       if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
> >                               iscsit_stop_dataout_timer(cmd);
> >
> > -                     return iscsit_dump_data_payload(conn, payload_length, 1);
> > -             }
> > -     } else {
> > -             /*
> > -              * For the normal solicited data path:
> > -              *
> > -              * Check for a delayed TASK_ABORTED status and dump any
> > -              * incoming data out payload if one exists.  Also, when the
> > -              * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current
> > -              * data out sequence, we decrement outstanding_r2ts.  Once
> > -              * outstanding_r2ts reaches zero, go ahead and send the delayed
> > -              * TASK_ABORTED status.
> > -              */
> > -             if (se_cmd->transport_state & CMD_T_ABORTED) {
> > -                     if (hdr->flags & ISCSI_FLAG_CMD_FINAL &&
> > -                         --cmd->outstanding_r2ts < 1)
> > -                             iscsit_stop_dataout_timer(cmd);
> 
> [Severity: High]
> Could passing an aborted WRITE_PENDING command to target_execute_cmd() cause
> a double abort completion and use-after-free if the connection closes afterward
> in iscsit_release_commands_from_conn()?

Yes, it can cause. But my patch neither was intended to address that
problem neither increases its probability. WRITE_PENDING always may be
aborted, in parallel from other session, right
before target_cmd_interrupted() in target_execute_cmd().

And that is easily fixed by changing cmd->t_state in
target_execute_cmd() before target_cmd_interrupted() to take the
cmd ownership by the target core:

-----------------
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 2c9f4b58c024f..14ea1a9d7fa40 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2561,6 +2561,10 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
 void target_execute_cmd(struct se_cmd *cmd)
 {
        trace_target_execute_cmd(cmd);
+
+       spin_lock_irq(&cmd->t_state_lock);
+       cmd->t_state = TRANSPORT_PROCESSING;
+       spin_unlock_irq(&cmd->t_state_lock);
        /*
         * Determine if frontend context caller is requesting the stopping of
         * this command for frontend exceptions.
@@ -2571,7 +2575,6 @@ void target_execute_cmd(struct se_cmd *cmd)
                return;

        spin_lock_irq(&cmd->t_state_lock);
-       cmd->t_state = TRANSPORT_PROCESSING;
        cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT;
        spin_unlock_irq(&cmd->t_state_lock);

---------------


> 
> With this check removed in __iscsit_check_dataout_hdr(), an aborted WRITE
> command in TRANSPORT_WRITE_PENDING state receives its remaining Data-Out PDUs
> and calls target_execute_cmd() in iscsit_check_dataout_payload() (or in
> iscsit_execute_cmd() for out-of-order CmdSNs):
> 
> drivers/target/iscsi/iscsi_target.c:iscsit_check_dataout_payload() {
>         ...
>                 iscsit_stop_dataout_timer(cmd);
>                 if (ooo_cmdsn)
>                         return 0;
>                 target_execute_cmd(&cmd->se_cmd);
>                 return 0;
>         ...
> }
> 
> In target_execute_cmd(), target_cmd_interrupted() is called before
> cmd->t_state is updated to TRANSPORT_PROCESSING:
> 
> drivers/target/target_core_transport.c:target_execute_cmd() {
>         if (target_cmd_interrupted(cmd))
>                 return;
> 
>         spin_lock_irq(&cmd->t_state_lock);
>         cmd->t_state = TRANSPORT_PROCESSING;
>         cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT;
>         spin_unlock_irq(&cmd->t_state_lock);
>         ...
> }
> 
> Because CMD_T_ABORTED is set, target_cmd_interrupted() queues
> target_abort_work on target_completion_wq and returns true without changing
> cmd->t_state:
> 
> drivers/target/target_core_transport.c:target_cmd_interrupted() {
>         ...
>         if (cmd->transport_state & CMD_T_ABORTED) {
>                 if (cmd->transport_complete_callback)
>                         cmd->transport_complete_callback(cmd, false, &post_ret);
>                 INIT_WORK(&cmd->work, target_abort_work);
>                 queue_work(target_completion_wq, &cmd->work);
>                 return true;
>         }
>         ...
> }
> 
> As a result, cmd->se_cmd.t_state remains TRANSPORT_WRITE_PENDING. When
> CMD_T_TAS is set (for example, when aborted by a LUN_RESET on another session
> with emulate_tas enabled), target_handle_abort() queues SAM_STAT_TASK_ABORTED
> status and drops one cmd_kref reference via
> transport_cmd_check_stop_to_fabric(), leaving the command on
> conn->conn_cmd_list in TRANSPORT_WRITE_PENDING state until ExpStatSN
> acknowledges the status (or, when CMD_T_TAS is not set, until
> target_abort_work runs iscsit_aborted_task()).
> 
> If the connection closes while the command is still on conn->conn_cmd_list,
> iscsit_release_commands_from_conn() checks for TRANSPORT_WRITE_PENDING:
> 
> drivers/target/iscsi/iscsi_target.c:iscsit_release_commands_from_conn() {
>         ...
>                 if (cmd->se_cmd.t_state == TRANSPORT_WRITE_PENDING) {
>                         /*
>                          * We never submitted the cmd to LIO core, so we have
>                          * to tell LIO to perform the completion process.
>                          */
>                         spin_unlock_irq(&se_cmd->t_state_lock);
>                         target_complete_cmd(&cmd->se_cmd, SAM_STAT_TASK_ABORTED);
>                         continue;
>                 }
>         ...
> }
> 
> Wouldn't this call target_complete_cmd() -> target_cmd_interrupted() a second
> time on the same command, re-initializing and re-queueing cmd->work for
> target_abort_work while iscsit_release_commands_from_conn() also frees the
> command via iscsit_free_cmd(), leading to workqueue corruption, cmd_kref
> underflow, and use-after-free of struct iscsit_cmd?
> 
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20261027180123.13163-1-d.bogdanov@yadro.com?part=2

  reply	other threads:[~2026-10-27 22:55 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 18:01 [PATCH 0/2] target: iscsi: fix hanged WRITE_PENDING commands Dmitry Bogdanov
2026-10-27 18:01 ` [PATCH 1/2] Revert "scsi: target: iscsi: Fix hang for aborted WRITE_PENDING commands" Dmitry Bogdanov
2026-10-27 18:01 ` [PATCH 2/2] scsi: target: iscsi: fix hanging WRITE_PENDING commands Dmitry Bogdanov
2026-10-27 18:18   ` sashiko-bot
2026-10-27 22:55     ` Dmitry Bogdanov [this message]
2026-10-27 20:08 ` [PATCH 0/2] target: iscsi: fix hanged " Laurence Oberman
2026-10-28 14:02   ` Dmitry Bogdanov
2026-10-28 14:30     ` Laurence Oberman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261027225545.GA32229@yadro.com \
    --to=d.bogdanov@yadro.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=target-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox