drivers/scsi/pm8001/pm8001_ctl.c | 2 ++ 1 file changed, 2 insertions(+)
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
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 >
> 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 > >
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
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
© 2016 - 2026 Red Hat, Inc.