Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Phil Pemberton" <philpem@philpem.me.uk>
Cc: cassel@kernel.org, linux-scsi@vger.kernel.org,
	dlemoal@kernel.org, linux-ide@vger.kernel.org
Subject: Re: [PATCH v9 07/11] ata: libata-scsi: convert dev->sdev to per-LUN array
Date: Tue, 06 Oct 2026 23:58:47 +0000	[thread overview]
Message-ID: <sashiko-outbox-162543@kernel.org> (raw)
In-Reply-To: <20261006234202.1139018-8-philpem@philpem.me.uk>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] ata: UAF in ata_acpi_uevent() accessing freed scsi_device kobject
- [Medium] of: memory leak in ata_scsi_assign_ofnode() due to missing of_node_put()
- [Medium] ata: libata-zpodd: stale from_notify flag in zpodd_wake_dev() causes unintended tray ejection
- [Medium] ata: redundant scsi_resume_device() calls on already-resumed LUNs due to shared device flag
--

commit 6719cc4aa927e2dd12a61aea767af00a2eb99075
Author: Phil Pemberton <philpem@philpem.me.uk>

ata: libata-scsi: convert dev->sdev to per-LUN array

Multi-LUN ATAPI devices share one ata_device and expose several SCSI
devices. Replace dev->sdev with an eight-slot array indexed by LUN and
update its callers. Single-LUN devices use slot 0; unused slots are NULL.

> diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
> index 4433f626246ba..2d1662f6f0642 100644
> --- a/drivers/ata/libata-acpi.c
> +++ b/drivers/ata/libata-acpi.c
> @@ -153,10 +153,13 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
>  	char *envp[] = { event_string, NULL };
>  
>  	if (dev) {
> -		if (dev->sdev)
> -			kobj = &dev->sdev->sdev_gendev.kobj;
> -	} else
> +		struct scsi_device *sdev = ata_dev_scsi_device(dev, 0);
> +
> +		if (sdev)
> +			kobj = &sdev->sdev_gendev.kobj;

[Severity: High]
Is this scsi_device kobject safe from being freed during this call?

The scsi_device is retrieved via ata_dev_scsi_device() without taking
ap->lock or acquiring a reference with scsi_device_get(). A concurrent
unplug could drop the reference count and free the device object before
kobject_uevent_env() executes, leading to a use-after-free.

> +	} else {
>  		kobj = &ap->dev->kobj;
> +	}
>  
>  	if (kobj) {

[ ... ]

> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
> index 3131d84204ed7..68d6ca1f791dd 100644
> --- a/drivers/ata/libata-scsi.c
> +++ b/drivers/ata/libata-scsi.c

[ ... ]

> @@ -5259,9 +5270,9 @@ int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *s
>  }
>  
>  #ifdef CONFIG_OF
> -static void ata_scsi_assign_ofnode(struct ata_device *dev, struct ata_port *ap)
> +static void ata_scsi_assign_ofnode(struct scsi_device *sdev,
> +				   struct ata_device *dev, struct ata_port *ap)
>  {

[Severity: Medium]
Does ata_scsi_assign_ofnode() leak a device tree node reference?

When for_each_available_child_of_node() inside this function finds a
matching port, it assigns the child to sdev->sdev_gendev.of_node and
returns early. The iterator implicitly acquires a reference to the child
node on each iteration. Since the SCSI and driver cores do not
automatically call of_node_put() on the device of_node during teardown,
this leaves a persistent reference leak.

> -	struct scsi_device *sdev = dev->sdev;
>  	struct device *d = ap->host->dev;
>  	struct device_node *np = d->of_node;
>  	struct device_node *child;

[ ... ]

> @@ -5610,7 +5619,9 @@ void ata_scsi_dev_rescan(struct work_struct *work)
>  
>  	ata_for_each_link(link, ap, EDGE) {
>  		ata_for_each_dev(dev, link, ENABLED) {
> -			struct scsi_device *sdev = dev->sdev;
> +			struct scsi_device *sdevs[ATAPI_MAX_LUN] = {};
> +			bool retry = false;
> +			int lun, err;
>  
>  			/*
>  			 * If the port was suspended before this was scheduled,

[ ... ]

> -			spin_unlock_irqrestore(ap->lock, flags);
> -			if (do_resume) {
> -				ret = scsi_resume_device(sdev);
> -				if (ret == -EWOULDBLOCK) {
> -					scsi_device_put(sdev);
> -					goto unlock_scan;
> +			for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
> +				if (!sdevs[lun])
> +					continue;
> +
> +				spin_unlock_irqrestore(ap->lock, flags);
> +				if (do_resume) {
> +					err = scsi_resume_device(sdevs[lun]);
> +					if (err == -EWOULDBLOCK)
> +						goto put_sdev;
>  				}
> -				dev->flags &= ~ATA_DFLAG_RESUMING;
> +				err = scsi_rescan_device(sdevs[lun]);

[Severity: Medium]
Will this cause redundant resume attempts on already-resumed LUNs?

When do_resume is true, both scsi_resume_device() and scsi_rescan_device()
execute for every valid LUN without checking if that specific LUN was
already successfully resumed in a previous pass.

> +put_sdev:
> +				/* A deferred LUN must not prevent scanning its siblings. */
> +				if (err) {
> +					ret = err;
> +					retry = true;
> +				}
> +				scsi_device_put(sdevs[lun]);
> +				spin_lock_irqsave(ap->lock, flags);
>  			}
> -			ret = scsi_rescan_device(sdev);
> -			scsi_device_put(sdev);
> -			spin_lock_irqsave(ap->lock, flags);
>  
> -			if (ret)
> -				goto unlock_ap;
> +			if (do_resume && !retry)
> +				dev->flags &= ~ATA_DFLAG_RESUMING;

[Severity: Medium]
If any LUN sets retry to true by returning -EWOULDBLOCK, the per-device
ATA_DFLAG_RESUMING flag is kept set. Consequently, the next execution of
the workqueue will see do_resume as true and unconditionally re-resume all
sibling LUNs, even the ones that previously succeeded.

>  		}
>  	}

[ ... ]

> diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
> index 581eb41ea0f3c..bbdcaaa357980 100644
> --- a/drivers/ata/libata-zpodd.c
> +++ b/drivers/ata/libata-zpodd.c

[ ... ]

> @@ -265,18 +272,21 @@ static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
>  {
>  	struct ata_device *ata_dev = context;
>  	struct zpodd *zpodd = ata_dev->zpodd;
> -	struct scsi_device *sdev;
> +	int lun;
>  
>  	if (event != ACPI_NOTIFY_DEVICE_WAKE)
>  		return;
> -	sdev = zpodd_get_sdev(ata_dev);
> -	if (!sdev)
> -		return;
> -	if (pm_runtime_suspended(&sdev->sdev_gendev)) {
> -		zpodd->from_notify = true;
> -		pm_runtime_resume(&sdev->sdev_gendev);
> +	for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
> +		struct scsi_device *sdev = zpodd_get_sdev(ata_dev, lun);
> +
> +		if (!sdev)
> +			continue;
> +		if (pm_runtime_suspended(&sdev->sdev_gendev)) {
> +			zpodd->from_notify = true;
> +			pm_runtime_resume(&sdev->sdev_gendev);
> +		}

[Severity: Medium]
Can the from_notify flag become stuck in a true state here?

Setting this per-ATA-device flag inside a per-LUN loop allows the flag to
be incorrectly left true. If the first suspended LUN causes an actual
resume, from_notify is set and then cleared in zpodd_post_poweron() when
EH runs. For subsequent suspended LUNs, from_notify is set to true again,
but pm_runtime_resume() finds the parent already active and does not
trigger EH. This leaves from_notify set to true, which will cause an
unintended physical tray ejection during the next power-on.

> +		scsi_device_put(sdev);
>  	}
> -	scsi_device_put(sdev);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261006234202.1139018-1-philpem@philpem.me.uk?part=7

  reply	other threads:[~2026-10-06 23:58 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-06 23:41 [PATCH v9 00/11] ata: add multi-LUN support for ATAPI devices Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 01/11] ata: libata-core: pin SCSI devices during port detach Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 02/11] ata: libata-zpodd: reference SCSI devices in PM callbacks Phil Pemberton
2026-10-06 23:59   ` sashiko-bot
2026-10-06 23:41 ` [PATCH v9 03/11] scsi: scsi_devinfo: preserve full-width quirk identifiers Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 04/11] scsi: sr: initialize logical block size before registering the disk Phil Pemberton
2026-10-06 23:53   ` sashiko-bot
2026-10-06 23:41 ` [PATCH v9 05/11] scsi: sr: retry unfinished media revalidation on the next open Phil Pemberton
2026-10-06 23:52   ` sashiko-bot
2026-10-06 23:41 ` [PATCH v9 06/11] ata: libata-scsi: add atapi_max_lun module parameter Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 07/11] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
2026-10-06 23:58   ` sashiko-bot [this message]
2026-10-07  2:38     ` Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 08/11] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
2026-10-06 23:41 ` [PATCH v9 09/11] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
2026-10-06 23:42 ` [PATCH v9 10/11] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
2026-10-06 23:42 ` [PATCH v9 11/11] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton

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-162543@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=cassel@kernel.org \
    --cc=dlemoal@kernel.org \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=philpem@philpem.me.uk \
    --cc=sashiko-reviews@lists.linux.dev \
    /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