Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Yehyeong Lee" <yhlee@isslab.korea.ac.kr>
Cc: linux-rdma@vger.kernel.org, target-devel@vger.kernel.org
Subject: Re: [PATCH v4 2/3] IB/isert: drain the QP without holding isert_conn->mutex
Date: Tue, 03 Nov 2026 03:52:56 +0000	[thread overview]
Message-ID: <sashiko-outbox-194544@kernel.org> (raw)
In-Reply-To: <20261103032924.1073138-3-yhlee@isslab.korea.ac.kr>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] IB/isert: race with isert_accept_np() when draining QP unlocked
- [Low] IB/isert: missing colon in Return section of isert_conn_terminate() kernel-doc
--

commit 563514f12bfc26c4edaed45f85db0c8c5f4ade31
Author: Yehyeong Lee <yhlee@isslab.korea.ac.kr>

IB/isert: drain the QP without holding isert_conn->mutex

This commit changes isert_conn_terminate() to return whether it moved the
connection to ISER_CONN_TERMINATING, and moves ib_drain_qp() and
isert_handle_unbound_conn() outside isert_conn->mutex in
isert_disconnected_handler() to avoid deadlocking with
isert_login_recv_done().

> diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
> index 8255208e30c7d..c6f417bb20690 100644
> --- a/drivers/infiniband/ulp/isert/ib_isert.c
> +++ b/drivers/infiniband/ulp/isert/ib_isert.c
> @@ -578,21 +578,17 @@ isert_handle_unbound_conn(struct isert_conn *isert_conn)
>   * isert_conn_terminate() - Initiate connection termination
>   * @isert_conn: isert connection struct
>   *
> - * Notes:
> - * In case the connection state is BOUND, move state
> - * to TEMINATING and start teardown sequence (rdma_disconnect).
> - * In case the connection state is UP, complete flush as well.
> - *
> - * This routine must be called with mutex held. Thus it is
> - * safe to call multiple times.
> + * Returns true if this caller moved the connection to ISER_CONN_TERMINATING.
>   */

[Severity: Low]
This isn't a bug, but kernel-doc requires a colon after Returns (Returns:)
to recognize the return value section when building with W=2.

> -static void
> +static bool
>  isert_conn_terminate(struct isert_conn *isert_conn)
>  {
>  	int err;
>  
> +	lockdep_assert_held(&isert_conn->mutex);
> +
>  	if (isert_conn->state >= ISER_CONN_TERMINATING)
> -		return;
> +		return false;
>  
>  	isert_info("Terminating conn %p state %d\n",
>  		   isert_conn, isert_conn->state);
> @@ -601,6 +597,8 @@ isert_conn_terminate(struct isert_conn *isert_conn)
>  	if (err)
>  		isert_warn("Failed rdma_disconnect isert_conn %p\n",
>  			   isert_conn);
> +
> +	return true;
>  }
>  
>  static int
> @@ -635,15 +633,14 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
>  			   enum rdma_cm_event_type event)
>  {
>  	struct isert_conn *isert_conn = cma_id->qp->qp_context;
> +	bool terminated = false;
>  
>  	mutex_lock(&isert_conn->mutex);
>  	switch (isert_conn->state) {
>  	case ISER_CONN_TERMINATING:
>  		break;
>  	case ISER_CONN_UP:
> -		isert_conn_terminate(isert_conn);
> -		ib_drain_qp(isert_conn->qp);
> -		isert_handle_unbound_conn(isert_conn);
> +		terminated = isert_conn_terminate(isert_conn);
>  		break;
>  	case ISER_CONN_BOUND:
>  	case ISER_CONN_FULL_FEATURE: /* FALLTHRU */
> @@ -655,6 +652,11 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
>  	}
>  	mutex_unlock(&isert_conn->mutex);
>  
> +	if (terminated) {
> +		ib_drain_qp(isert_conn->qp);
> +		isert_handle_unbound_conn(isert_conn);
> +	}

[Severity: High]
Can dropping isert_conn->mutex before ib_drain_qp() and
isert_handle_unbound_conn() race with isert_accept_np()?

When a connection reaches isert_connected_handler(), its state is set to
ISER_CONN_UP, isert_conn->node is placed on isert_np->pending, and
up(&isert_np->sem) wakes the login thread in isert_accept_np().

If a disconnect arrives while the connection is still in ISER_CONN_UP,
isert_disconnected_handler() sets isert_conn->state to
ISER_CONN_TERMINATING and now unlocks isert_conn->mutex before removing
isert_conn->node from isert_np->pending in isert_handle_unbound_conn().

While ib_drain_qp() is waiting in isert_disconnected_handler(),
isert_login_recv_done() can complete isert_conn->login_req_comp for a login
PDU received just before the disconnect, and isert_accept_np() can dequeue
isert_conn from isert_np->pending and overwrite isert_conn->state without
holding isert_conn->mutex:

drivers/infiniband/ulp/isert/ib_isert.c:isert_accept_np() {
	...
	isert_conn = list_first_entry(&isert_np->pending,
			struct isert_conn, node);
	list_del_init(&isert_conn->node);
	mutex_unlock(&isert_np->mutex);

	conn->context = isert_conn;
	isert_conn->conn = conn;
	isert_conn->state = ISER_CONN_BOUND;
	...
}

Once list_del_init(&isert_conn->node) has run in isert_accept_np(),
isert_handle_unbound_conn() sees list_empty(&isert_conn->node) and becomes
a no-op.

If login then fails or times out, iscsi_target_login_sess_out() calls
isert_wait_conn() and isert_free_conn():

drivers/infiniband/ulp/isert/ib_isert.c:isert_wait_conn() {
	...
	mutex_lock(&isert_conn->mutex);
	isert_conn_terminate(isert_conn);
	mutex_unlock(&isert_conn->mutex);

	ib_drain_qp(isert_conn->qp);
	isert_put_conn(isert_conn);
	queue_work(isert_release_wq, &isert_conn->release_work);
}

Because isert_conn->state was reset to ISER_CONN_BOUND and
isert_conn->mutex is no longer held across ib_drain_qp(), wouldn't
isert_wait_conn() call ib_drain_qp(isert_conn->qp) a second time
concurrently with isert_disconnected_handler(), and allow both kref
references on isert_conn to be dropped while isert_disconnected_handler()
is still running?

Alternatively, if single-stage login succeeds in memory without posting to
the QP (isert_put_login_tx() sets isert_conn->login_rsp_pending = true),
wouldn't isert_get_rx_pdu() set isert_conn->state to ISER_CONN_FULL_FEATURE,
post to the already-disconnected QP, and block indefinitely in
wait_for_completion_interruptible(&comp) because the disconnect event was
already handled?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103032924.1073138-1-yhlee@isslab.korea.ac.kr?part=2

  reply	other threads:[~2026-11-03  3:52 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  3:29 [PATCH v4 0/3] IB/isert: fix the device removal use-after-free Yehyeong Lee
2026-11-03  3:29 ` [PATCH v4 1/3] IB/isert: do not publish connections being released on a second list Yehyeong Lee
2026-11-03  3:53   ` sashiko-bot
2026-11-03  3:29 ` [PATCH v4 2/3] IB/isert: drain the QP without holding isert_conn->mutex Yehyeong Lee
2026-11-03  3:52   ` sashiko-bot [this message]
2026-11-03  3:29 ` [PATCH v4 3/3] IB/isert: tear connections down from an ib_client on device removal Yehyeong Lee
2026-11-03  3:56   ` sashiko-bot
2026-11-03 13:04 ` [PATCH v4 0/3] IB/isert: fix the device removal use-after-free Yehyeong Lee

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-194544@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-rdma@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