SCSI target 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 v5 5/5] IB/isert: do not build a new isert_device for a device being removed
Date: Tue, 03 Nov 2026 13:39:24 +0000	[thread overview]
Message-ID: <sashiko-outbox-195380@kernel.org> (raw)
In-Reply-To: <20261103125955.1530355-6-yhlee@isslab.korea.ac.kr>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] IB/isert: stale removal marker in isert_device_get() before isert_add_one()
- [Medium] IB/isert: unbounded leak of isert_device markers in device_list
- [Medium] IB/isert: memory leak of removal markers in isert_init() on error
--

commit 812d1184eb1546b67f3290f713769f5e49331bb1
Author: Yehyeong Lee <yhlee@isslab.korea.ac.kr>

IB/isert: do not build a new isert_device for a device being removed

This patch retains or allocates a removing struct isert_device marker on
device_list during isert_remove_one() so that concurrent isert_device_get()
calls reject devices being removed with -ENODEV. The markers are then freed
when a matching ib_device pointer is seen in isert_add_one() or when the
module unloads in isert_exit().

> diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
> index 712d6165150e3..4bcd14f5045ac 100644
> --- a/drivers/infiniband/ulp/isert/ib_isert.c
> +++ b/drivers/infiniband/ulp/isert/ib_isert.c
> @@ -260,10 +260,12 @@ isert_device_put(struct isert_device *device)
>  	isert_info("device %p refcount %d\n", device, device->refcount);
>  	if (!device->refcount) {
>  		isert_free_device_ib_res(device);
> -		list_del(&device->dev_node);
> -		if (device->removal_comp)
> +		if (device->removing) {
>  			complete(device->removal_comp);
> -		kfree(device);
> +		} else {
> +			list_del(&device->dev_node);
> +			kfree(device);
> +		}
>  	}
>  	mutex_unlock(&device_list_mutex);
>  }
> @@ -276,14 +278,21 @@ isert_device_get(struct rdma_cm_id *cma_id)
>  
>  	mutex_lock(&device_list_mutex);
>  	list_for_each_entry(device, &device_list, dev_node) {
> -		if (device->ib_device->node_guid == cma_id->device->node_guid) {
> -			device->refcount++;
> -			isert_info("Found iser device %p refcount %d\n",
> -				   device, device->refcount);
> +		if (device->removing && device->ib_device == cma_id->device) {
>  			mutex_unlock(&device_list_mutex);
> -			return device;
> +			return ERR_PTR(-ENODEV);
>  		}
>  	}

[Severity: High]
Can a stale removal marker in device_list cause isert_device_get() to reject
valid connection requests on a newly enabled or namespace-moved RDMA device
before isert_add_one() has a chance to clear it?

Because ib_isert depends on rdma_cm, cma_client is registered before
isert_ib_client. In enable_device_and_get(), client add callbacks run in
registration order:

drivers/infiniband/core/device.c:enable_device_and_get()
  xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED)
    add_client_context(device, client)

When cma_add_one() runs first, it immediately activates wildcard listeners
(such as an isert_np bound to INADDR_ANY) via cma_listen_on_dev() before
returning:

drivers/infiniband/core/cma.c:cma_add_one()
  list_for_each_entry(id_priv, &listen_any_list, listen_any_item)
    cma_listen_on_dev(id_priv, cma_dev, &to_destroy)

When the same struct ib_device pointer is re-enabled by
rdma_dev_change_netns() (which calls disable_device() followed by
enable_device_and_get() on the same struct ib_device) or when a freed
struct ib_device address is reused by the slab allocator, the marker left by
isert_remove_one() is still on device_list in the window after cma_add_one()
starts listening and before isert_add_one() runs. Any incoming
RDMA_CM_EVENT_CONNECT_REQUEST during that window will match
device->removing && device->ib_device == cma_id->device in the first pass of
isert_device_get() and fail with -ENODEV.

Additionally, if a connection request arrives on a new ib_device after
cma_add_one() starts listening and add_client_context() subsequently fails
before setting CLIENT_DATA_REGISTERED for isert_ib_client, isert_device_get()
will have built a live isert_device, but the error unwind in
disable_device() will skip isert_remove_one(), leaving its IB resources
unreleased.

> +	list_for_each_entry(device, &device_list, dev_node) {
> +		if (device->removing ||
> +		    device->ib_device->node_guid != cma_id->device->node_guid)
> +			continue;
> +		device->refcount++;
> +		isert_info("Found iser device %p refcount %d\n",
> +			   device, device->refcount);
> +		mutex_unlock(&device_list_mutex);
> +		return device;
> +	}
>  
>  	device = kzalloc_obj(struct isert_device);
>  	if (!device) {
> @@ -2792,20 +2801,29 @@ isert_remove_one(struct ib_device *ib_device, void *client_data)
>  
>  	mutex_lock(&device_list_mutex);
>  	list_for_each_entry(device, &device_list, dev_node) {
> -		if (device->ib_device != ib_device)
> +		if (device->ib_device != ib_device || device->removing)
>  			continue;
>  		device->refcount++;
>  		device->removal_comp = &removal_comp;
>  		found = device;
>  		break;
>  	}
> -	mutex_unlock(&device_list_mutex);
> -
> -	if (!found)
> +	if (!found) {
> +		device = kzalloc_obj(struct isert_device,
> +				     GFP_KERNEL | __GFP_NOFAIL);
> +		INIT_LIST_HEAD(&device->dev_node);
> +		device->ib_device = ib_device;
> +		device->removing = true;
> +		list_add_tail(&device->dev_node, &device_list);
> +		mutex_unlock(&device_list_mutex);
>  		return;
> +	}
> +	mutex_unlock(&device_list_mutex);
>  
>  	mutex_lock(&isert_conn_mutex);
> +	mutex_lock(&device_list_mutex);
>  	found->removing = true;
> +	mutex_unlock(&device_list_mutex);
>  	list_for_each_entry(isert_conn, &isert_conn_list, conn_list) {
>  		if (isert_conn->device != found)
>  			continue;
> @@ -2828,8 +2846,38 @@ isert_remove_one(struct ib_device *ib_device, void *client_data)
>  	wait_for_completion(&removal_comp);
>  }
>  
> +static int isert_add_one(struct ib_device *ib_device)
> +{
> +	struct isert_device *device, *tmp;
> +
> +	mutex_lock(&device_list_mutex);
> +	list_for_each_entry_safe(device, tmp, &device_list, dev_node) {
> +		if (device->ib_device != ib_device || !device->removing ||
> +		    device->refcount)
> +			continue;
> +		list_del(&device->dev_node);
> +		kfree(device);
> +	}
> +	mutex_unlock(&device_list_mutex);
> +
> +	return 0;
> +}

[Severity: Medium]
Does this accumulate struct isert_device markers on device_list without
bound across RDMA device unregistrations?

Whenever any IB device is unregistered via __ib_unregister_device() ->
disable_device() -> remove_client_context() -> isert_remove_one() (even a
device never used by ib_isert), isert_remove_one() either keeps the existing
struct isert_device on device_list via isert_device_put() (with dangling
pointers in device->ib_device, device->pd, and the stack-allocated
device->removal_comp) or allocates a new struct isert_device marker with
GFP_KERNEL | __GFP_NOFAIL and appends it to device_list.

Because isert_add_one() only frees a marker if a newly registered device
happens to reuse the exact same struct ib_device pointer address
(device->ib_device == ib_device), any removed ib_device whose slab memory is
not reused by a later ib_device allocation (for example, during repeated
rxe/siw link deletion or VF hotplug cycles) leaves its struct isert_device
marker on device_list until module unload.

> +
> +static void isert_free_markers(void)
> +{
> +	struct isert_device *device, *tmp;
> +
> +	mutex_lock(&device_list_mutex);
> +	list_for_each_entry_safe(device, tmp, &device_list, dev_node) {
> +		list_del(&device->dev_node);
> +		kfree(device);
> +	}
> +	mutex_unlock(&device_list_mutex);
> +}
> +
>  static struct ib_client isert_ib_client = {
>  	.name   = "isert",
> +	.add    = isert_add_one,
>  	.remove = isert_remove_one,
>  };
>  
> @@ -2881,6 +2929,7 @@ static int __init isert_init(void)
>  static void __exit isert_exit(void)
>  {
>  	ib_unregister_client(&isert_ib_client);
> +	isert_free_markers();

[Severity: Medium]
Does isert_init() leak the struct isert_device markers if
ib_register_client(&isert_ib_client) fails partway through?

In ib_register_client(), if add_client_context() succeeds for one or more
devices and then fails on a later device, it rolls back by calling
ib_unregister_client():

drivers/infiniband/core/device.c:ib_register_client()
  xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) {
    ret = add_client_context(device, client);
    if (ret)
      goto out;
  }
  ...
out:
  ...
  if (need_unreg && ret)
    ib_unregister_client(client);

That rollback invokes remove_client_context() -> isert_remove_one() for each
device where add_client_context() succeeded, which allocates a struct
isert_device marker and adds it to device_list. In isert_init(), the error
path jumps to destroy_release_wq without calling isert_free_markers():

drivers/infiniband/ulp/isert/ib_isert.c:isert_init()
	ret = ib_register_client(&isert_ib_client);
	if (ret) {
		isert_err("Unable to register isert ib client\n");
		goto destroy_release_wq;
	}

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

  reply	other threads:[~2026-11-03 13:39 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 12:59 [PATCH v5 0/5] IB/isert: fix the device removal use-after-free Yehyeong Lee
2026-11-03 12:59 ` [PATCH v5 1/5] IB/isert: do not publish connections being released on a second list Yehyeong Lee
2026-11-03 12:59 ` [PATCH v5 2/5] IB/isert: drain the QP without holding isert_conn->mutex Yehyeong Lee
2026-11-03 12:59 ` [PATCH v5 3/5] IB/isert: tear connections down from an ib_client on device removal Yehyeong Lee
2026-11-03 13:32   ` sashiko-bot
2026-11-03 12:59 ` [PATCH v5 4/5] IB/isert: drop the promotion reference when a connection errors out Yehyeong Lee
2026-11-03 12:59 ` [PATCH v5 5/5] IB/isert: do not build a new isert_device for a device being removed Yehyeong Lee
2026-11-03 13:39   ` sashiko-bot [this message]
2026-11-03 22:39 ` [PATCH v5 0/5] 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-195380@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