[PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read

Chengfeng Ye posted 1 patch 3 weeks, 2 days ago
drivers/scsi/pm8001/pm8001_ctl.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read
Posted by Chengfeng Ye 3 weeks, 2 days ago
From: Chengfeng Ye <cyeaa@connect.ust.hk>

Fix a data race where pm8001_ctl_host_sas_address_show() reads
pm8001_ha->sas_addr without synchronization while it can be written
from interrupt context in pm8001_mpi_get_nvmd_resp().

The write path is already protected by pm8001_ha->lock (held by
process_oq() when calling pm8001_mpi_get_nvmd_resp()),
but the sysfs read path accesses the 8-byte SAS address without
any synchronization, allowing torn reads.

Thread interleaving scenario:

           Thread A (sysfs read)     |    Thread B (interrupt context)
-------------------------------------+------------------------------------
pm8001_ctl_host_sas_address_show()  |
|- read sas_addr[0..3]               |
                                     | process_oq()
                                     | |- spin_lock_irqsave(&lock)
                                     | |- process_one_iomb()
                                     | |  |- pm8001_mpi_get_nvmd_resp()
                                     | |     |- memcpy(sas_addr, new, 8)
                                     | |        /* writes all 8 bytes */
                                     | |- spin_unlock_irqrestore(&lock)
|- read sas_addr[4..7]               |
   /* gets mix of old and new */    |

Fix by protecting the sysfs read with the same pm8001_ha->lock
using guard(spinlock_irqsave) for automatic lock cleanup.

Signed-off-by: Chengfeng Ye <cyeaa@connect.ust.hk>
---
V1 -> V2: Use guard instead of lock/unlock pair

 drivers/scsi/pm8001/pm8001_ctl.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index cbfda8c04e95..200ee6bbd413 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -311,6 +311,8 @@ static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
 	struct Scsi_Host *shost = class_to_shost(cdev);
 	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
 	struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+	guard(spinlock_irqsave)(&pm8001_ha->lock);
 	return sysfs_emit(buf, "0x%016llx\n",
 			be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
 }
-- 
2.25.1
Re: [PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read
Posted by Jinpu Wang 3 weeks, 1 day ago
On Thu, Jan 15, 2026 at 6:55 PM Chengfeng Ye <dg573847474@gmail.com> wrote:
>
> From: Chengfeng Ye <cyeaa@connect.ust.hk>
>
> Fix a data race where pm8001_ctl_host_sas_address_show() reads
> pm8001_ha->sas_addr without synchronization while it can be written
> from interrupt context in pm8001_mpi_get_nvmd_resp().
>
> The write path is already protected by pm8001_ha->lock (held by
> process_oq() when calling pm8001_mpi_get_nvmd_resp()),
> but the sysfs read path accesses the 8-byte SAS address without
> any synchronization, allowing torn reads.
>
> Thread interleaving scenario:
>
>            Thread A (sysfs read)     |    Thread B (interrupt context)
> -------------------------------------+------------------------------------
> pm8001_ctl_host_sas_address_show()  |
> |- read sas_addr[0..3]               |
>                                      | process_oq()
>                                      | |- spin_lock_irqsave(&lock)
>                                      | |- process_one_iomb()
>                                      | |  |- pm8001_mpi_get_nvmd_resp()
>                                      | |     |- memcpy(sas_addr, new, 8)
>                                      | |        /* writes all 8 bytes */
>                                      | |- spin_unlock_irqrestore(&lock)
> |- read sas_addr[4..7]               |
>    /* gets mix of old and new */    |
>
> Fix by protecting the sysfs read with the same pm8001_ha->lock
> using guard(spinlock_irqsave) for automatic lock cleanup.
>
> Signed-off-by: Chengfeng Ye <cyeaa@connect.ust.hk>
As James commented, sas address is uniq, not something changing all the time.
Do you see any real issue?
> ---
> V1 -> V2: Use guard instead of lock/unlock pair
>
>  drivers/scsi/pm8001/pm8001_ctl.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
> index cbfda8c04e95..200ee6bbd413 100644
> --- a/drivers/scsi/pm8001/pm8001_ctl.c
> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
> @@ -311,6 +311,8 @@ static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
>         struct Scsi_Host *shost = class_to_shost(cdev);
>         struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
>         struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
> +
> +       guard(spinlock_irqsave)(&pm8001_ha->lock);
>         return sysfs_emit(buf, "0x%016llx\n",
>                         be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
>  }
> --
> 2.25.1
>
Re: [PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read
Posted by Chengfeng Ye 3 weeks, 1 day ago
> As James commented, sas address is uniq, not something changing all the time.

I think maybe the more noteworthy case is sas address could be read
when it is only partially initialized.
It might happen because the sysfs is mounted by scsi_add_host() at
line 1196 of pm8001_init.c
during probe, prior to the sas address being initialized by the
pm8001_init_sas_add() called
at line 1208. The small window could allow sysfs read return
non-initialized (should be
zero) or partially initialized sas address (due to the non-atomic read/write)

> Do you see any real issue?

No, I just read the code and spotted this potential issue, and so I reported it.

Reference: https://github.com/torvalds/linux/blob/master/drivers/scsi/pm8001/pm8001_init.c#L1196

Best regards,
Chengfeng




Jinpu Wang <jinpu.wang@ionos.com> 于2026年1月16日周五 13:46写道:
>
> On Thu, Jan 15, 2026 at 6:55 PM Chengfeng Ye <dg573847474@gmail.com> wrote:
> >
> > From: Chengfeng Ye <cyeaa@connect.ust.hk>
> >
> > Fix a data race where pm8001_ctl_host_sas_address_show() reads
> > pm8001_ha->sas_addr without synchronization while it can be written
> > from interrupt context in pm8001_mpi_get_nvmd_resp().
> >
> > The write path is already protected by pm8001_ha->lock (held by
> > process_oq() when calling pm8001_mpi_get_nvmd_resp()),
> > but the sysfs read path accesses the 8-byte SAS address without
> > any synchronization, allowing torn reads.
> >
> > Thread interleaving scenario:
> >
> >            Thread A (sysfs read)     |    Thread B (interrupt context)
> > -------------------------------------+------------------------------------
> > pm8001_ctl_host_sas_address_show()  |
> > |- read sas_addr[0..3]               |
> >                                      | process_oq()
> >                                      | |- spin_lock_irqsave(&lock)
> >                                      | |- process_one_iomb()
> >                                      | |  |- pm8001_mpi_get_nvmd_resp()
> >                                      | |     |- memcpy(sas_addr, new, 8)
> >                                      | |        /* writes all 8 bytes */
> >                                      | |- spin_unlock_irqrestore(&lock)
> > |- read sas_addr[4..7]               |
> >    /* gets mix of old and new */    |
> >
> > Fix by protecting the sysfs read with the same pm8001_ha->lock
> > using guard(spinlock_irqsave) for automatic lock cleanup.
> >
> > Signed-off-by: Chengfeng Ye <cyeaa@connect.ust.hk>
> As James commented, sas address is uniq, not something changing all the time.
> Do you see any real issue?
> > ---
> > V1 -> V2: Use guard instead of lock/unlock pair
> >
> >  drivers/scsi/pm8001/pm8001_ctl.c | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
> > index cbfda8c04e95..200ee6bbd413 100644
> > --- a/drivers/scsi/pm8001/pm8001_ctl.c
> > +++ b/drivers/scsi/pm8001/pm8001_ctl.c
> > @@ -311,6 +311,8 @@ static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
> >         struct Scsi_Host *shost = class_to_shost(cdev);
> >         struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
> >         struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
> > +
> > +       guard(spinlock_irqsave)(&pm8001_ha->lock);
> >         return sysfs_emit(buf, "0x%016llx\n",
> >                         be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
> >  }
> > --
> > 2.25.1
> >
Re: [PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read
Posted by James Bottomley 3 weeks, 1 day ago
On Fri, 2026-01-16 at 16:31 +0800, Chengfeng Ye wrote:
> > As James commented, sas address is uniq, not something changing all
> > the time.
> 
> I think maybe the more noteworthy case is sas address could be read
> when it is only partially initialized.

But again, do we care and is the proposed fix any better?  sas_addr is
just an indicator for tools to print.  All your fix would do is have us
output a fully uninitialized address instead which isn't really any
better (or worse) ... either way the output is wrong.  Effectively
there's a small period where the sas address may not be accurate and
tools can mitigate it by waiting.

> It might happen because the sysfs is mounted by scsi_add_host() at
> line 1196 of pm8001_init.c
> during probe, prior to the sas address being initialized by the
> pm8001_init_sas_add() called
> at line 1208. The small window could allow sysfs read return
> non-initialized (should be
> zero) or partially initialized sas address (due to the non-atomic
> read/write)

I think everyone would agree this can't happen for built in drivers
(because user space doesn't start until long after driver init), so
your theory above rests on a race between inserting the module and a
tool reading the file which can be fixed by waiting a short period ...
it just doesn't seem to be an important issue.

Regards,

James
Re: [PATCH v2] scsi: pm8001: Fix data race in sysfs SAS address read
Posted by Chengfeng Ye 3 weeks, 1 day ago
Hi James,

> I think everyone would agree this can't happen for built in drivers
> (because user space doesn't start until long after driver init), so
> your theory above rests on a race between inserting the module and a
> tool reading the file which can be fixed by waiting a short period ...
> it just doesn't seem to be an important issue.

True for the case of built-in driver (most use scenario), the race
window is short.

> Whereas taking the internal host lock in a
> sysfs read routine could potentially be a DoS vector.

Agree that using a spinlock just to fix this hardly triggered output
issue may not be a good idea.
I think this theoretical uninitialized/torn-read issue could also be
fixed by delaying the creation of the sysfs folder until
initialization finishes. Can help create a new patch if you'd like to
improve the code, or we can just ignore it if it is not worth
bothering with a small issue like this.

Best regards,
Chengfeng