From: sashiko-bot@kernel.org
To: "Daniel Wade" <danjwade95@gmail.com>
Cc: linux-scsi@vger.kernel.org, target-devel@vger.kernel.org
Subject: Re: [PATCH 2/3] scsi: target: iscsi: don't attach a connection to a closing session
Date: Sun, 15 Nov 2026 09:25:01 +0000 [thread overview]
Message-ID: <sashiko-outbox-215362@kernel.org> (raw)
In-Reply-To: <3ae0ce68449390d9e3893e5328367c4db3818f96.1794732723.git.danjwade95@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] scsi: target: iscsi: incomplete teardown check in
iscsi_post_login_handler()
- [Medium] scsi: target: iscsi: premature LOGGED_IN state in
iscsi_post_login_handler()
--
commit fbb74f9a87b2184ab2a30c73faf991dfc1060e88
Author: Daniel Wade <danjwade95@gmail.com>
scsi: target: iscsi: don't attach a connection to a closing session
This commit sets sess->session_close under sess->conn_lock when the last
connection closes in the ERL=0 fallback path and re-checks
sess->session_close in iscsi_post_login_handler() before attaching a
non-leading connection to the session.
> diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
> index f4df551817116..de0cd95aff017 100644
> --- a/drivers/target/iscsi/iscsi_target_login.c
> +++ b/drivers/target/iscsi/iscsi_target_login.c
> @@ -660,6 +660,33 @@ void iscsit_stop_kthreads(struct iscsit_conn *conn)
> spin_unlock(&iscsit_global->ts_bitmap_lock);
> }
>
> +/*
> + * Drop a connection whose kthreads are already running but that must not enter
> + * full feature phase. conn->sess must not be touched once this returns.
> + */
> +static void iscsi_post_login_fail_conn(struct iscsit_conn *conn)
> +{
> + /*
> + * iscsi_target_tx_thread() only leaves cleanup to this context while
> + * conn_state is TARG_CONN_STATE_IN_LOGIN; otherwise it would reach
> + * iscsit_close_connection() and walk the session being refused.
> + */
> + conn->conn_state = TARG_CONN_STATE_IN_LOGIN;
> + iscsit_stop_kthreads(conn);
> + iscsit_dec_conn_usage_count(conn);
> +
> + if (conn->sock) {
> + sock_release(conn->sock);
> + conn->sock = NULL;
> + }
> + if (conn->conn_transport->iscsit_wait_conn)
> + conn->conn_transport->iscsit_wait_conn(conn);
> + if (conn->conn_transport->iscsit_free_conn)
> + conn->conn_transport->iscsit_free_conn(conn);
> +
> + iscsit_free_conn(conn);
> +}
[Severity: Medium]
Can setting conn->conn_state = TARG_CONN_STATE_LOGGED_IN at the start of
iscsi_post_login_handler() and only resetting it back to
TARG_CONN_STATE_IN_LOGIN here deadlock with conn->tx_thread?
iscsi_post_login_handler() increments conn->conn_usage_count and sets
conn->conn_state = TARG_CONN_STATE_LOGGED_IN before taking sess->conn_lock
to check sess->session_close:
drivers/target/iscsi/iscsi_target_login.c:iscsi_post_login_handler() {
iscsit_inc_conn_usage_count(conn);
iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_SUCCESS,
ISCSI_LOGIN_STATUS_ACCEPT);
pr_debug("Moving to TARG_CONN_STATE_LOGGED_IN.\n");
conn->conn_state = TARG_CONN_STATE_LOGGED_IN;
iscsi_set_connection_parameters(conn->conn_ops, conn->param_list);
/*
* SCSI Initiator -> SCSI Target Port Mapping
*/
if (!zero_tsih) {
iscsi_set_session_parameters(sess->sess_ops,
conn->param_list, 0);
}
If an asynchronous transport disconnect sends SIGINT to conn->tx_thread
during that window (for example via isert_disconnected_handler() ->
iscsit_cause_connection_reinstatement()), tx_thread sees conn_state !=
TARG_CONN_STATE_IN_LOGIN and enters iscsit_close_connection() on the
unattached connection:
CPU0 (login worker) CPU1 (conn->tx_thread)
iscsi_post_login_handler()
iscsit_inc_conn_usage_count()
conn->conn_state =
TARG_CONN_STATE_LOGGED_IN
iscsi_target_tx_thread()
if (conn_state !=
TARG_CONN_STATE_IN_LOGIN)
iscsit_take_action_...()
iscsit_close_connection()
iscsit_check_conn_usage_count()
wait_for_completion()
iscsi_post_login_fail_conn()
conn->conn_state =
TARG_CONN_STATE_IN_LOGIN
iscsit_stop_kthreads()
kthread_stop(tx_thread) <-- deadlocks waiting for tx_thread
iscsit_dec_conn_usage_count()
In addition, iscsi_set_session_parameters() mutates sess->sess_ops and
iscsit_collect_login_stats() records an accepted login before the session
is checked under sess->conn_lock. Checking whether the session is closing
before transitioning conn->conn_state or mutating session state would
avoid this window.
> void iscsi_post_login_handler(
> struct iscsi_np *np,
> struct iscsit_conn *conn,
> @@ -690,6 +717,20 @@ void iscsi_post_login_handler(
> conn->param_list = NULL;
>
> spin_lock_bh(&sess->conn_lock);
> + /*
> + * The skip filter in iscsi_login_non_zero_tsih_s2() sampled this
> + * before the negotiation. Re-test under the lock that serialises
> + * against the ->nconn test in iscsit_close_connection().
> + */
> + if (atomic_read(&sess->session_close)) {
> + spin_unlock_bh(&sess->conn_lock);
> + pr_err("iSCSI session is closing, failing login on CID: %u\n",
> + conn->cid);
> + iscsi_post_login_fail_conn(conn);
> + /* Last access to @sess: this lets close_session() free it. */
> + iscsit_dec_session_usage_count(sess);
> + return;
> + }
> atomic_set(&sess->session_continuation, 0);
> if (sess->session_state == TARG_SESS_STATE_FAILED) {
> pr_debug("Moving to"
[Severity: High]
Does checking only sess->session_close here still allow a non-leading
connection to attach to a session that is already tearing down?
iscsi_login_non_zero_tsih_s2() skips sessions that have any of
session_fall_back_to_erl0, session_logout, session_close or
ISCSI_TF_EXPIRED set, but iscsi_post_login_handler() only re-checks
session_close, and iscsit_close_connection() only sets session_close when
sess->nconn reaches 0 in the ERL=0 fallback branch.
For example, if a session has one active connection (conn1) when conn2
passes iscsi_login_non_zero_tsih_s2(), sess->session_state is
TARG_SESS_STATE_LOGGED_IN, so sess->session_continuation remains 0 while
conn2 negotiates holding session_usage_count. If conn1 then closes via a
connection logout (or ERL=2 failure) and the Time2Retain timer expires,
iscsit_handle_time2retain_timeout() sets ISCSI_TF_EXPIRED and calls
iscsit_close_session(sess, false) without setting sess->session_close:
CPU0 (conn1 / timer) CPU1 (conn2 login)
iscsit_close_connection(conn1)
sess->session_state =
TARG_SESS_STATE_FAILED
iscsit_start_time2retain_...()
<softirq>
iscsit_handle_time2retain_...()
sess->time2retain_timer_flags |=
ISCSI_TF_EXPIRED
iscsit_close_session(sess, false)
iscsi_post_login_handler(conn2)
spin_lock_bh(&sess->conn_lock)
atomic_read(&sess->session_close) == 0
list_add_tail(&conn2->conn_list, ...)
atomic_inc(&sess->nconn)
spin_unlock_bh(&sess->conn_lock)
if (atomic_read(&sess->nconn))
BUG() <-- kernel panic (or kfree(sess) if past the nconn check)
Similarly, in a multi-connection session (nconn == 2) falling back to
ERL=0, when the first connection closes it sets session_fall_back_to_erl0
and session_stop_active under sess->conn_lock without setting
session_close, and calls iscsit_stop_session(). If conn2 attaches in
iscsi_post_login_handler() before the second connection takes
sess->conn_lock in iscsit_close_connection(), the second connection
decrements nconn from 2 to 1, sees session_stop_active is already 1 and
returns early:
drivers/target/iscsi/iscsi_target.c:iscsit_close_connection() {
if (atomic_read(&sess->nconn)) {
if (!atomic_read(&sess->session_reinstatement) &&
!atomic_read(&sess->session_fall_back_to_erl0)) {
spin_unlock_bh(&sess->conn_lock);
return 0;
}
if (!atomic_read(&sess->session_stop_active)) {
atomic_set(&sess->session_stop_active, 1);
spin_unlock_bh(&sess->conn_lock);
iscsit_stop_session(sess, 0, 0);
return 0;
}
spin_unlock_bh(&sess->conn_lock);
return 0;
}
}
That leaves conn2 stranded on a session with session_fall_back_to_erl0
set, which iscsit_release_sessions_for_tpg() and lio_tpg_close_session()
both skip during TPG teardown. A CLOSESESSION logout also sets
sess->session_logout under sess->conn_lock in iscsit_logout_closesession()
before sess->session_close is set in
iscsit_logout_post_handler_closesession().
Re-checking the full teardown condition (session_close,
session_fall_back_to_erl0, session_logout, and ISCSI_TF_EXPIRED under
se_tpg->session_lock) in iscsi_post_login_handler() would close these
windows as well.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794732723.git.danjwade95@gmail.com?part=2
next prev parent reply other threads:[~2026-11-15 9:25 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-15 8:57 [PATCH 0/3] scsi: target: iscsi: fix session use-after-free on non-leading login Daniel Wade
2026-11-15 8:57 ` [PATCH 1/3] scsi: target: iscsi: factor out iscsit_stop_kthreads() Daniel Wade
2026-11-15 8:57 ` [PATCH 2/3] scsi: target: iscsi: don't attach a connection to a closing session Daniel Wade
2026-11-15 9:25 ` sashiko-bot [this message]
2026-11-15 8:57 ` [PATCH 3/3] scsi: target: iscsi: don't re-arm Time2Retain on " Daniel Wade
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=sashiko-outbox-215362@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=danjwade95@gmail.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.