From: sashiko-bot@kernel.org
To: "Yehyeong Lee" <yhlee@isslab.korea.ac.kr>
Cc: target-devel@vger.kernel.org, linux-scsi@vger.kernel.org
Subject: Re: [PATCH] scsi: target: iscsi: run the Time2Retain handler in process context
Date: Sun, 15 Nov 2026 12:42:38 +0000 [thread overview]
Message-ID: <sashiko-outbox-215504@kernel.org> (raw)
In-Reply-To: <20261115121943.1446744-1-yhlee@isslab.korea.ac.kr>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] scsi: target: iscsi: UAF race in
iscsit_stop_time2retain_timer() on restart
- [High] scsi: target: iscsi: session leak in iscsit_close_session()
when !can_sleep
- [High] scsi: target: iscsi: UAF in iscsit_close_connection() when
Time2Retain is 0
- [Medium] scsi: target: iscsi: session_lock dropped in
iscsit_stop_time2retain_timer()
--
commit cd2ff8f1169a8e47b09d7eb1b677944735610f1c
Author: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
scsi: target: iscsi: run the Time2Retain handler in process context
This commit converts the session Time2Retain timer into a delayed work item on
system_dfl_wq so that iscsit_handle_time2retain_timeout() closes the session
and acquires se_tpg->session_lock in process context rather than timer softirq
context.
> diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
> index 03d618cd76721..4c6579eb803f3 100644
> --- a/drivers/target/iscsi/iscsi_target.c
> +++ b/drivers/target/iscsi/iscsi_target.c
> @@ -4485,8 +4485,8 @@ int iscsit_close_session(struct iscsit_session *sess, bool can_sleep)
>
> /*
> * If any other processes are accessing this session pointer we must
> - * wait until they have completed. If we are in an interrupt (the
> - * time2retain handler) and contain and active session usage count we
> + * wait until they have completed. If we are the Time2Retain work
> + * (can_sleep is false) and contain and active session usage count we
> * restart the timer and exit.
> */
> if (iscsit_check_session_usage_count(sess, can_sleep)) {
[Severity: High]
Can keeping can_sleep as false when iscsit_handle_time2retain_timeout() calls
iscsit_close_session() leak the session if session_usage_count is non-zero?
In iscsi_target_login_sess_out(), the old_sess_out path starts the Time2Retain
handler and drops se_tpg->session_lock before calling
iscsit_dec_session_usage_count(). When DefaultTime2Retain is 0,
mod_delayed_work() queues time2retain_work with zero delay, so
iscsit_handle_time2retain_timeout() can run on another CPU while
session_usage_count is still elevated.
iscsit_handle_time2retain_timeout() sets ISCSI_TF_EXPIRED (with
ISCSI_TF_RUNNING still set) and calls iscsit_close_session(sess, false).
iscsit_close_session() sets session_reinstatement to 1, fails to stop the
timer because ISCSI_TF_EXPIRED is set, deregisters configfs, and enters the
!can_sleep branch above because session_usage_count is non-zero. There,
iscsit_start_time2retain_handler() returns immediately without re-arming the
work because ISCSI_TF_RUNNING was never cleared, leaving the half-closed
session leaked with ISCSI_TF_EXPIRED and session_reinstatement set.
Since iscsit_handle_time2retain_timeout() now runs in process context, could
it wait on session_waiting_on_uc_comp instead of taking the non-sleeping
restart path?
[ ... ]
> diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
> index 24db6b07493e6..9db833f3eeaec 100644
> --- a/drivers/target/iscsi/iscsi_target_erl0.c
> +++ b/drivers/target/iscsi/iscsi_target_erl0.c
[ ... ]
> @@ -791,8 +791,8 @@ void iscsit_start_time2retain_handler(struct iscsit_session *sess)
>
> sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
> sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
> - mod_timer(&sess->time2retain_timer,
> - jiffies + sess->sess_ops->DefaultTime2Retain * HZ);
> + mod_delayed_work(system_dfl_wq, &sess->time2retain_work,
> + sess->sess_ops->DefaultTime2Retain * HZ);
> }
>
> int iscsit_stop_time2retain_timer(struct iscsit_session *sess)
[Severity: High]
Can this cause a use-after-free in iscsit_close_connection() when
DefaultTime2Retain is 0?
drivers/target/iscsi/iscsi_target.c:iscsit_close_connection() {
...
if (!atomic_read(&sess->session_continuation))
iscsit_start_time2retain_handler(sess);
if (atomic_read(&sess->session_close)) {
spin_unlock_bh(&sess->conn_lock);
complete_all(&sess->session_wait_comp);
iscsit_close_session(sess, true);
} else {
spin_unlock_bh(&sess->conn_lock);
}
...
}
Previously, mod_timer() armed a softirq timer while spin_lock_bh() kept local
softirqs disabled across iscsit_start_time2retain_handler() and the subsequent
sess accesses. With mod_delayed_work(), a zero delay bypasses the timer wheel
and queues time2retain_work immediately on an unbound worker, and neither
iscsit_handle_time2retain_timeout() nor iscsit_close_session() takes
sess->conn_lock:
CPU0 CPU1 (kworker)
iscsit_close_connection()
spin_lock_bh(&sess->conn_lock)
iscsit_start_time2retain_handler(sess)
mod_delayed_work(..., 0)
iscsit_handle_time2retain_timeout()
iscsit_close_session(sess, false)
kfree(sess)
atomic_read(&sess->session_close)
spin_unlock_bh(&sess->conn_lock) <-- use-after-free
> @@ -811,7 +811,7 @@ int iscsit_stop_time2retain_timer(struct iscsit_session *sess)
> sess->time2retain_timer_flags |= ISCSI_TF_STOP;
> spin_unlock(&se_tpg->session_lock);
>
> - timer_delete_sync(&sess->time2retain_timer);
> + cancel_delayed_work(&sess->time2retain_work);
>
> spin_lock(&se_tpg->session_lock);
> sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
[Severity: High]
Can replacing timer_delete_sync() with cancel_delayed_work() here allow an
already-dispatched work item to close and free the session after the timer is
stopped and restarted?
Because cancel_delayed_work() does not wait for a running work item,
iscsit_stop_time2retain_timer() clears ISCSI_TF_RUNNING while a worker thread
can still be preempted before taking se_tpg->session_lock. If the timer is
then restarted (for example via the old_sess_out path in
iscsi_target_login_sess_out() when a continuation login fails),
iscsit_start_time2retain_handler() clears ISCSI_TF_STOP and re-queues
sess->time2retain_work:
CPU0 (kworker) CPU1
iscsit_handle_time2retain_timeout()
/* preempted before session_lock */
iscsi_login_non_zero_tsih_s2()
iscsit_stop_time2retain_timer()
flags |= ISCSI_TF_STOP
cancel_delayed_work()
flags &= ~ISCSI_TF_RUNNING
...
iscsi_target_login_sess_out()
iscsit_start_time2retain_handler()
flags &= ~ISCSI_TF_STOP
flags |= ISCSI_TF_RUNNING
mod_delayed_work()
spin_lock_bh(&se_tpg->session_lock)
/* ISCSI_TF_STOP is now clear */
flags |= ISCSI_TF_EXPIRED
spin_unlock_bh(&se_tpg->session_lock)
iscsit_close_session(sess, false)
iscsit_stop_time2retain_timer() <-- returns -1 (ISCSI_TF_EXPIRED)
/* cancel_delayed_work_sync() skipped because !can_sleep */
kfree(sess) <-- time2retain_work still queued
When CPU0 acquires se_tpg->session_lock, ISCSI_TF_STOP is no longer set, so
the stale work sets ISCSI_TF_EXPIRED and calls iscsit_close_session(sess,
false). Because ISCSI_TF_EXPIRED is set and can_sleep is false, neither
iscsit_stop_time2retain_timer() nor cancel_delayed_work_sync() cancels the
newly armed time2retain_work before kfree(sess) frees the session (or if a
new connection is already active on the session, iscsit_close_session() hits
BUG() on sess->nconn).
[Severity: Medium]
Why does iscsit_stop_time2retain_timer() still drop and re-acquire
se_tpg->session_lock around cancel_delayed_work()?
The commit message notes that cancel_delayed_work() is called with
se_tpg->session_lock held because it does not sleep, but the unlock and lock
around it remain from timer_delete_sync(). Dropping se_tpg->session_lock
splits the update of time2retain_timer_flags across two critical sections, so
an unlocked caller of iscsit_start_time2retain_handler() such as
iscsit_close_connection() can observe ISCSI_TF_RUNNING still set in that
window and return without arming the work just before ISCSI_TF_RUNNING is
cleared.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261115121943.1446744-1-yhlee@isslab.korea.ac.kr?part=1
prev parent reply other threads:[~2026-11-15 12:42 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-15 12:19 [PATCH] scsi: target: iscsi: run the Time2Retain handler in process context Yehyeong Lee
2026-11-15 12:42 ` sashiko-bot [this message]
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-215504@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=target-devel@vger.kernel.org \
--cc=yhlee@isslab.korea.ac.kr \
/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