All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] scsi: cxgbi: fix retry_timer use-after-free on adapter detach
@ 2026-08-14  2:09 Fan Wu
  2026-08-14  2:33 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Fan Wu @ 2026-08-14  2:09 UTC (permalink / raw)
  To: linux-scsi; +Cc: varun, martin.petersen, linux-kernel, stable, Fan Wu

An active-open connection may keep retry_timer armed because
cxgbi_sock_closed() returns early in CTP_ACTIVE_OPEN state.  On adapter
detach, the timer callback can then dereference csk->cdev after cdev has
been freed.

Set CTPF_OFFLOAD_DOWN under csk->lock during portmap cleanup, reject new
retry_timer arming, and make an already queued callback a no-op while
offload is down.  A connection still in CTP_ACTIVE_OPEN is pushed through
cxgbi_sock_fail_act_open() so that its ATID, L2T entry, port mapping, and
final reference are released while cdev is still valid, rather than left
for the now-disabled retry timer.  timer_delete_sync() then drains a
pending or running callback before the final cxgbi_sock_put().

This issue was found by an in-house static analysis tool.

Fixes: 7b36b6e03b0d ("[SCSI] cxgb4i v5: iscsi driver")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
Changes in v2:
 - drop the lldi/t4 deref-move in csk_act_open_retry_timer(): csk->cdev is
   always valid when the callback runs (timer_delete_sync drains before the
   cdev free), per Varun Prakash.

 drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 8 +++++++-
 drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 9 ++++++++-
 drivers/scsi/cxgbi/libcxgbi.c      | 6 +++++-
 3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
index 69de965..f61f280 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
@@ -556,6 +556,11 @@ static void act_open_retry_timer(struct timer_list *t)
 
 	cxgbi_sock_get(csk);
 	spin_lock_bh(&csk->lock);
+	if (cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN)) {
+		spin_unlock_bh(&csk->lock);
+		cxgbi_sock_put(csk);
+		return;
+	}
 	skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_ATOMIC);
 	if (!skb)
 		cxgbi_sock_fail_act_open(csk, -ENOMEM);
@@ -585,7 +590,8 @@ static int do_act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
 
 	cxgbi_sock_get(csk);
 	spin_lock_bh(&csk->lock);
-	if (rpl->status == CPL_ERR_CONN_EXIST &&
+	if (!cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN) &&
+	    rpl->status == CPL_ERR_CONN_EXIST &&
 	    csk->retry_timer.function != act_open_retry_timer) {
 		csk->retry_timer.function = act_open_retry_timer;
 		mod_timer(&csk->retry_timer, jiffies + HZ / 2);
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 4267662..7c9b465 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1001,6 +1001,12 @@ static void csk_act_open_retry_timer(struct timer_list *t)
 	cxgbi_sock_get(csk);
 	spin_lock_bh(&csk->lock);
 
+	if (cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN)) {
+		spin_unlock_bh(&csk->lock);
+		cxgbi_sock_put(csk);
+		return;
+	}
+
 	if (t4) {
 		size = sizeof(struct cpl_act_open_req);
 		size6 = sizeof(struct cpl_act_open_req6);
@@ -1075,7 +1081,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
 	cxgbi_sock_get(csk);
 	spin_lock_bh(&csk->lock);
 
-	if (status == CPL_ERR_CONN_EXIST &&
+	if (!cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN) &&
+	    status == CPL_ERR_CONN_EXIST &&
 	    csk->retry_timer.function != csk_act_open_retry_timer) {
 		csk->retry_timer.function = csk_act_open_retry_timer;
 		mod_timer(&csk->retry_timer, jiffies + HZ / 2);
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index ea9631b..131d4f0 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -107,8 +107,12 @@ void cxgbi_device_portmap_cleanup(struct cxgbi_device *cdev)
 				csk, cdev);
 			spin_lock_bh(&csk->lock);
 			cxgbi_sock_set_flag(csk, CTPF_OFFLOAD_DOWN);
-			cxgbi_sock_closed(csk);
+			if (csk->state == CTP_ACTIVE_OPEN)
+				cxgbi_sock_fail_act_open(csk, -ENODEV);
+			else
+				cxgbi_sock_closed(csk);
 			spin_unlock_bh(&csk->lock);
+			timer_delete_sync(&csk->retry_timer);
 			cxgbi_sock_put(csk);
 		}
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] scsi: cxgbi: fix retry_timer use-after-free on adapter detach
  2026-08-14  2:09 [PATCH v2] scsi: cxgbi: fix retry_timer use-after-free on adapter detach Fan Wu
@ 2026-08-14  2:33 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-14  2:33 UTC (permalink / raw)
  To: Fan Wu; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [Critical] Use-After-Free and Double Free for CTP_ACTIVE_OPEN connections during adapter detach
- [Critical] Race Condition leading to NULL Pointer Dereference in do_act_open_rpl

Pre-existing issues:
- [High] Memory leak and un-cancelled timer on user-initiated disconnect
--

--- Patch [1]: [PATCH v2] scsi: cxgbi: fix retry_timer use-after-free on adapter detach ---
commit 5cacab0f814551fa10e52ff589125571018e3cee
Author: Fan Wu <fanwu01@zju.edu.cn>

scsi: cxgbi: fix retry_timer use-after-free on adapter detach

This commit attempts to fix a use-after-free on the retry_timer by setting
a new flag CTPF_OFFLOAD_DOWN during portmap cleanup. It rejects new timer
arming and forces connections in CTP_ACTIVE_OPEN to be pushed through
cxgbi_sock_fail_act_open() to release resources.

> diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
> index 0e2bee937fe81..207c8ffb5f188 100644
> --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
> +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
[ ... ]
> @@ -584,7 +589,8 @@ static int do_act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
>  
>  	cxgbi_sock_get(csk);
>  	spin_lock_bh(&csk->lock);
> -	if (rpl->status == CPL_ERR_CONN_EXIST &&
> +	if (!cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN) &&
> +	    rpl->status == CPL_ERR_CONN_EXIST &&
>  	    csk->retry_timer.function != act_open_retry_timer) {
>  		csk->retry_timer.function = act_open_retry_timer;
>  		mod_timer(&csk->retry_timer, jiffies + HZ / 2);

[Severity: Critical]
If CTPF_OFFLOAD_DOWN is set by a concurrent adapter detach, will this logic
fall through to the else branch and call cxgbi_sock_fail_act_open()?

When cxgbi_device_portmap_cleanup() already called cxgbi_sock_fail_act_open(),
csk->cdev gets cleared:

cxgbi_sock_closed()
   csk->cdev->csk_release_offload_resources(csk); 

If do_act_open_rpl() then calls cxgbi_sock_fail_act_open() again, it will set
the state to CTP_CONNECTING and call cxgbi_sock_closed().

Could this result in a NULL pointer dereference when cxgbi_sock_closed()
tries to access csk->cdev again?


> diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
> index 804806e1cbb4b..2e2a10117afcd 100644
> --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
> +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
[ ... ]
> @@ -892,7 +898,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
>  	cxgbi_sock_get(csk);
>  	spin_lock_bh(&csk->lock);
>  
> -	if (status == CPL_ERR_CONN_EXIST &&
> +	if (!cxgbi_sock_flag(csk, CTPF_OFFLOAD_DOWN) &&
> +	    status == CPL_ERR_CONN_EXIST &&
>  	    csk->retry_timer.function != csk_act_open_retry_timer) {
>  		csk->retry_timer.function = csk_act_open_retry_timer;
>  		mod_timer(&csk->retry_timer, jiffies + HZ / 2);

[Severity: Critical]
Is this susceptible to the same concurrent execution issue as cxgb3i above?

If do_act_open_rpl() executes concurrently with cxgbi_device_portmap_cleanup()
and falls into the else branch, it seems we might trigger a redundant teardown
and hit a NULL pointer dereference on csk->cdev.


> diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
> index f3bb7af4e984e..7b44d47c03946 100644
> --- a/drivers/scsi/cxgbi/libcxgbi.c
> +++ b/drivers/scsi/cxgbi/libcxgbi.c
[ ... ]
> @@ -99,8 +99,12 @@ void cxgbi_device_portmap_cleanup(struct cxgbi_device *cdev)
>  				csk, cdev);
>  			spin_lock_bh(&csk->lock);
>  			cxgbi_sock_set_flag(csk, CTPF_OFFLOAD_DOWN);
> -			cxgbi_sock_closed(csk);
> +			if (csk->state == CTP_ACTIVE_OPEN)
> +				cxgbi_sock_fail_act_open(csk, -ENODEV);
> +			else
> +				cxgbi_sock_closed(csk);

[Severity: High]
This isn't a bug introduced by this patch, but does the primary user-initiated
disconnect path also need to be fixed for active-open connections?

When the iSCSI daemon initiates a disconnect while the connection is still in
CTP_ACTIVE_OPEN, cxgbi_ep_disconnect() calls cxgbi_sock_closed():

cxgbi_ep_disconnect()
    if (likely(csk->state >= CTP_ESTABLISHED))
        need_active_close(csk);
    else
        cxgbi_sock_closed(csk);

Because the state is CTP_ACTIVE_OPEN, cxgbi_sock_closed() returns early
without doing anything:

cxgbi_sock_closed()
    if (csk->state == CTP_ACTIVE_OPEN || csk->state == CTP_CLOSED)
        return;

Does this leave the retry_timer armed and offload resources unreleased,
causing a memory leak and a background timer loop?

[Severity: Critical]
Could calling cxgbi_sock_fail_act_open() here drop too many references and
lead to a use-after-free?

cxgbi_sock_fail_act_open() sets the state to CTP_CONNECTING and calls
cxgbi_sock_closed(). Because the state is no longer CTP_ACTIVE_OPEN, it
bypasses the early return in cxgbi_sock_closed().

This causes cxgbi_sock_closed() to drop references for the port, the offload
resources, and call cxgbi_sock_put(csk).

If the iSCSI daemon already disconnected, the socket's refcount could reach
0 inside cxgbi_sock_closed(), freeing the memory.


>  			spin_unlock_bh(&csk->lock);
> +			timer_delete_sync(&csk->retry_timer);
>  			cxgbi_sock_put(csk);
>  		}
>  	}

[Severity: Critical]
Are we potentially accessing freed memory here?

If csk was already freed during the cxgbi_sock_fail_act_open() call above,
would spin_unlock_bh(), timer_delete_sync(), and cxgbi_sock_put() operate
on freed memory?

Also, doesn't cxgbi_sock_fail_act_open() steal the endpoint's reference by
bypassing the early return in cxgbi_sock_closed(), resulting in a double-free
when this cxgbi_sock_put(csk) is called?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814020948.197655-1-fanwu01@zju.edu.cn?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-14  2:33 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14  2:09 [PATCH v2] scsi: cxgbi: fix retry_timer use-after-free on adapter detach Fan Wu
2026-08-14  2:33 ` sashiko-bot

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.