Skip to contentSkip to conversation

Enable lock context analysis for the SCSI subsystem

PATCH Superseded Bart Van Assche 3 participants 91 messages Jul 30 – Aug 3

Subsystems (41)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Bart Van Assche

author reviewer
#0 lore raw

v3 cover Enable lock context analysis for the SCSI subsystem

This patch series enables lock context analysis for all SCSI drivers and also for the SCSI core. The advantages are as follows:
- The compiler (only Clang) verifies whether the lock and unlock calls match what has been declared via __must_hold(), __acquires() or __releases(). This is useful for catching locking bugs in error paths.
- Support for __guarded_by() is enabled. If a member variable is annotated with __guarded_by(lock), the compiler will issue a warning if that member variable is accessed without holding 'lock'.

Additionally, a patch is included that suppresses KCSAN complaints about SCSI host state changes.

More information about lock context analysis is available in the cover letter of [PATCH v5 00/36] Compiler-Based Context- and Locking-Analysis
(https://lore.kernel.org/lkml/[email protected]/).

Please consider this patch series for the next merge window.

Thanks,

Bart.

Changes compared to v2:
- Added patch "scsi: core: Pass the SCSI host pointer directly".
- Corrected several annotations that had missing or extra ampersands (&).
- Reduced the number of __assume_ctx_lock() statements.

Changes compared to v1:
- Split patch "Enable lock context analysis" into one patch per driver.

Bart Van Assche (57):
PCI: Convert to_pci_dev() into an inline function
scsi: scsi_debug: Prepare for enabling lock context analysis
scsi: sg: Prepare for enabling lock context analysis
scsi: st: Prepare for enabling lock context analysis
scsi: BusLogic: Introduce two local variables
scsi: BusLogic: Prepare for enabling lock context analysis
scsi: NCR5380: Prepare for enabling lock context analysis
scsi: aacraid: Prepare for enabling lock context analysis
scsi: aic7xxx: Enable lock context analysis
scsi: aha152x: Prepare for enabling lock context analysis
scsi: aic7xxx: Prepare for enabling lock context analysis
scsi: aic94xx: Enable lock context analysis
scsi: arcmsr: Enable lock context analysis
scsi: arm: Enable lock context analysis
scsi: be2iscsi: Prepare for enabling lock context analysis
scsi: be2iscsi: Enable lock context analysis
scsi: cxgbi: Enable lock context analysis
scsi: bfa: Enable lock context analysis
scsi: bnx2fc: Enable lock context analysis
scsi: bnx2i: Introduce a local variable
scsi: bnx2i: Enable lock context analysis
scsi: csiostor: Enable lock context analysis
scsi: elx: Enable lock context analysis
scsi: esas2r: Enable lock context analysis
scsi: fcoe: Enable lock context analysis
scsi: fnic: Enable lock context analysis
scsi: hisi_sas: Enable lock context analysis
scsi: hpsa: Prepare for enabling lock context analysis
scsi: ibmvscsi: Enable lock context analysis
scsi: ibmvscsi_tgt: Enable lock context analysis
scsi: ipr: Prepare for enabling lock context analysis
scsi: ips: Prepare for enabling lock context analysis
scsi: isci: Enable lock context analysis
scsi: libfc: Enable lock context analysis
scsi: libiscsi: Prepare for enabling lock context analysis
scsi: libsas: Prepare for enabling lock context analysis
scsi: libsas: Enable lock context analysis
scsi: lpfc: Prepare for enabling lock context analysis
scsi: megaraid_sas: Prepare for enabling lock context analysis
scsi: megaraid: Enable lock context analysis
scsi: mpt3sas: Enable lock context analysis
scsi: mvsas: Enable lock context analysis
scsi: pcmcia: Enable lock context analysis
scsi: pm8001: Enable lock context analysis
scsi: qedf: Enable lock context analysis
scsi: qedi: Enable lock context analysis
scsi: qla1280: Prepare for enabling lock context analysis
scsi: qla2xxx: Enable lock context analysis
scsi: qla4xxx: Enable lock context analysis
scsi: ufs: Enable lock context analysis
scsi: iSCSI transport: Prepare for enabling lock context analysis
scsi: smartpqi: Enable lock context analysis
scsi: snic: Enable lock context analysis
scsi: sym53c8xx_2: Enable lock context analysis
scsi: core: Pass the SCSI host pointer directly
scsi: core: Enable lock context analysis
scsi: core: Protect host state changes with the host lock

 drivers/scsi/BusLogic.c                     | 19 +++---
 drivers/scsi/BusLogic.h                     |  3 +-
 drivers/scsi/Makefile                       |  1 +
 drivers/scsi/NCR5380.c                      |  4 +-
 drivers/scsi/aacraid/Makefile               |  2 +
 drivers/scsi/aacraid/commctrl.c             |  1 +
 drivers/scsi/aacraid/commsup.c              |  3 +
 drivers/scsi/aha152x.c                      |  1 +
 drivers/scsi/aic7xxx/Makefile               |  2 +
 drivers/scsi/aic7xxx/aic79xx_osm.h          |  2 +
 drivers/scsi/aic7xxx/aic7xxx_osm.h          |  2 +
 drivers/scsi/aic94xx/Makefile               |  2 +
 drivers/scsi/arcmsr/Makefile                |  2 +
 drivers/scsi/arm/Makefile                   |  2 +
 drivers/scsi/be2iscsi/Makefile              |  2 +
 drivers/scsi/be2iscsi/be_main.c             |  4 ++
 drivers/scsi/bfa/Makefile                   |  3 +
 drivers/scsi/bnx2fc/Makefile                |  3 +
 drivers/scsi/bnx2fc/bnx2fc.h                |  5 +-
 drivers/scsi/bnx2fc/bnx2fc_els.c            |  2 +
 drivers/scsi/bnx2fc/bnx2fc_hwi.c            |  3 +
 drivers/scsi/bnx2fc/bnx2fc_io.c             |  6 +-
 drivers/scsi/bnx2i/Makefile                 |  3 +
 drivers/scsi/bnx2i/bnx2i_hwi.c              | 14 ++++-
 drivers/scsi/bnx2i/bnx2i_iscsi.c            |  1 +
 drivers/scsi/csiostor/Makefile              |  2 +
 drivers/scsi/csiostor/csio_hw.c             | 12 ++++
 drivers/scsi/csiostor/csio_lnode.c          |  3 +
 drivers/scsi/csiostor/csio_rnode.c          |  6 ++
 drivers/scsi/csiostor/csio_scsi.c           |  6 ++
 drivers/scsi/cxgbi/Makefile                 |  3 +
 drivers/scsi/device_handler/Makefile        |  3 +
 drivers/scsi/elx/Makefile                   |  1 +
 drivers/scsi/esas2r/Makefile                |  3 +
 drivers/scsi/fcoe/Makefile                  |  3 +
 drivers/scsi/fnic/Makefile                  |  3 +
 drivers/scsi/fnic/fdls_disc.c               | 52 +++++++++++++++-
 drivers/scsi/fnic/fip.c                     |  2 +
 drivers/scsi/fnic/fnic_fcs.c                |  6 ++
 drivers/scsi/fnic/fnic_scsi.c               |  4 ++
 drivers/scsi/hisi_sas/Makefile              |  3 +
 drivers/scsi/hosts.c                        | 13 ++--
 drivers/scsi/hpsa.c                         |  2 +-
 drivers/scsi/ibmvscsi/Makefile              |  3 +
 drivers/scsi/ibmvscsi_tgt/Makefile          |  3 +
 drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c    |  7 +++
 drivers/scsi/ipr.c                          | 11 ++++
 drivers/scsi/ips.c                          |  1 +
 drivers/scsi/isci/Makefile                  |  3 +
 drivers/scsi/libfc/Makefile                 |  2 +
 drivers/scsi/libfc/fc_disc.c                |  6 +-
 drivers/scsi/libfc/fc_exch.c                |  6 ++
 drivers/scsi/libfc/fc_fcp.c                 |  4 ++
 drivers/scsi/libiscsi.c                     | 19 +++++-
 drivers/scsi/libsas/Makefile                |  2 +
 drivers/scsi/libsas/sas_ata.c               |  2 +-
 drivers/scsi/lpfc/lpfc_els.c                |  2 +
 drivers/scsi/lpfc/lpfc_nportdisc.c          |  1 +
 drivers/scsi/lpfc/lpfc_scsi.c               |  1 +
 drivers/scsi/lpfc/lpfc_sli.c                |  2 +
 drivers/scsi/megaraid/Makefile              |  3 +
 drivers/scsi/megaraid/megaraid_sas.h        |  9 ++-
 drivers/scsi/megaraid/megaraid_sas_base.c   | 17 ++++--
 drivers/scsi/megaraid/megaraid_sas_fusion.c |  2 +
 drivers/scsi/mpt3sas/Makefile               |  3 +
 drivers/scsi/mpt3sas/mpt3sas_scsih.c        |  2 +-
 drivers/scsi/mvsas/Makefile                 |  2 +
 drivers/scsi/mvsas/mv_sas.c                 |  7 +++
 drivers/scsi/pcmcia/Makefile                |  2 +
 drivers/scsi/pm8001/Makefile                |  1 +
 drivers/scsi/pm8001/pm80xx_hwi.c            |  2 +
 drivers/scsi/qedf/Makefile                  |  3 +
 drivers/scsi/qedi/Makefile                  |  3 +
 drivers/scsi/qedi/qedi_fw.c                 |  1 +
 drivers/scsi/qla1280.c                      | 18 ++++++
 drivers/scsi/qla2xxx/Makefile               |  3 +
 drivers/scsi/qla2xxx/qla_nx.c               |  2 +
 drivers/scsi/qla2xxx/qla_target.c           | 29 +++++----
 drivers/scsi/qla2xxx/qla_tmpl.c             |  1 +
 drivers/scsi/qla4xxx/Makefile               |  3 +
 drivers/scsi/qla4xxx/ql4_nx.c               |  2 +
 drivers/scsi/qla4xxx/ql4_os.c               |  6 +-
 drivers/scsi/scsi_debug.c                   | 65 +++++++++-----------
 drivers/scsi/scsi_lib.c                     |  3 +-
 drivers/scsi/scsi_scan.c                    | 51 +++++++++-------
 drivers/scsi/scsi_sysfs.c                   |  7 ++-
 drivers/scsi/scsi_transport_iscsi.c         |  1 +
 drivers/scsi/sg.c                           |  1 +
 drivers/scsi/smartpqi/Makefile              |  3 +
 drivers/scsi/smartpqi/smartpqi_init.c       | 67 +++++++++++++++++----
 drivers/scsi/snic/Makefile                  |  3 +
 drivers/scsi/st.c                           |  1 +
 drivers/scsi/sym53c8xx_2/Makefile           |  2 +
 drivers/ufs/core/Makefile                   |  2 +
 drivers/ufs/core/ufs-debugfs.c              |  8 ++-
 drivers/ufs/core/ufshcd.c                   | 14 +++++
 drivers/ufs/host/Makefile                   |  2 +
 include/linux/pci.h                         |  5 +-
 include/scsi/libiscsi.h                     |  5 +-
 include/scsi/scsi_host.h                    | 25 +++++---
 100 files changed, 543 insertions(+), 137 deletions(-)
author reviewer
#1 lore raw

PATCH v3 1/57 PCI: Convert to_pci_dev() into an inline function

Clang's context analysis checker performs alias analysis on expressions passed to annotations like __acquires(). Clang's alias analysis does not support container_of(). Convert to_pci_dev() from a macro into an inline function such that Clang recognizes multiple to_pci_dev() expressions as identical if the 'dev' argument is the same.

It is on purpose that to_pci_dev() accepts a const pointer and returns a pointer that is not const. Any other choice, e.g. accepting a non-const pointer or returning a const pointer, triggers compiler errors in code that uses the to_pci_dev() macro.

While _Generic() could be used to support const and non-const struct device pointers in a more elegant way, Clang's alias analysis does not support _Generic().

---
 include/linux/pci.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
include/linux/pci.h +4 -1
index ebb5b9d76360..727050c5425a 100644
@@ -605,7 +605,10 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
 
 struct pci_dev *pci_alloc_dev(struct pci_bus *bus);
 
-#define	to_pci_dev(n) container_of(n, struct pci_dev, dev)
+static inline struct pci_dev *to_pci_dev(const struct device *dev)
+{
+	return container_of(dev, struct pci_dev, dev);
+}
 #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
 #define for_each_pci_dev_reverse(d) \
 	while ((d = pci_get_device_reverse(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
author reviewer
#2 lore raw

PATCH v3 2/57 scsi: scsi_debug: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Suppress lock context analysis for the functions that perform conditional locking to prevent that the Clang thread-safety analyzer complains about these functions. Remove incorrect __acquire() and __release() annotations.

---
 drivers/scsi/scsi_debug.c | 65 +++++++++++++++++----------------------
 1 file changed, 29 insertions(+), 36 deletions(-)
drivers/scsi/scsi_debug.c +29 -36
index 9d1c9c41d0f9..d88164d5f409 100644
@@ -4006,42 +4006,39 @@ static inline struct sdeb_store_info *devip2sip(struct sdebug_dev_info *devip,
 
 static inline void
 sdeb_read_lock(rwlock_t *lock)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock)
-		__acquire(lock);
-	else
+	if (!sdebug_no_rwlock)
 		read_lock(lock);
 }
 
 static inline void
 sdeb_read_unlock(rwlock_t *lock)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock)
-		__release(lock);
-	else
+	if (!sdebug_no_rwlock)
 		read_unlock(lock);
 }
 
 static inline void
 sdeb_write_lock(rwlock_t *lock)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock)
-		__acquire(lock);
-	else
+	if (!sdebug_no_rwlock)
 		write_lock(lock);
 }
 
 static inline void
 sdeb_write_unlock(rwlock_t *lock)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock)
-		__release(lock);
-	else
+	if (!sdebug_no_rwlock)
 		write_unlock(lock);
 }
 
 static inline void
 sdeb_data_read_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4050,6 +4047,7 @@ sdeb_data_read_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_read_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4058,6 +4056,7 @@ sdeb_data_read_unlock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_write_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4066,6 +4065,7 @@ sdeb_data_write_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_write_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4074,6 +4074,7 @@ sdeb_data_write_unlock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_sector_read_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4082,6 +4083,7 @@ sdeb_data_sector_read_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_sector_read_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4090,6 +4092,7 @@ sdeb_data_sector_read_unlock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_sector_write_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4098,6 +4101,7 @@ sdeb_data_sector_write_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_sector_write_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
 	BUG_ON(!sip);
 
@@ -4119,6 +4123,7 @@ sdeb_data_sector_write_unlock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_data_lock(struct sdeb_store_info *sip, bool atomic)
+	__context_unsafe(conditional locking)
 {
 	if (atomic)
 		sdeb_data_write_lock(sip);
@@ -4128,6 +4133,7 @@ sdeb_data_lock(struct sdeb_store_info *sip, bool atomic)
 
 static inline void
 sdeb_data_unlock(struct sdeb_store_info *sip, bool atomic)
+	__context_unsafe(conditional locking)
 {
 	if (atomic)
 		sdeb_data_write_unlock(sip);
@@ -4138,6 +4144,7 @@ sdeb_data_unlock(struct sdeb_store_info *sip, bool atomic)
 /* Allow many reads but only 1x write per sector */
 static inline void
 sdeb_data_sector_lock(struct sdeb_store_info *sip, bool do_write)
+	__context_unsafe(conditional locking)
 {
 	if (do_write)
 		sdeb_data_sector_write_lock(sip);
@@ -4147,6 +4154,7 @@ sdeb_data_sector_lock(struct sdeb_store_info *sip, bool do_write)
 
 static inline void
 sdeb_data_sector_unlock(struct sdeb_store_info *sip, bool do_write)
+	__context_unsafe(conditional locking)
 {
 	if (do_write)
 		sdeb_data_sector_write_unlock(sip);
@@ -4156,13 +4164,9 @@ sdeb_data_sector_unlock(struct sdeb_store_info *sip, bool do_write)
 
 static inline void
 sdeb_meta_read_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock) {
-		if (sip)
-			__acquire(&sip->macc_meta_lck);
-		else
-			__acquire(&sdeb_fake_rw_lck);
-	} else {
+	if (!sdebug_no_rwlock) {
 		if (sip)
 			read_lock(&sip->macc_meta_lck);
 		else
@@ -4172,13 +4176,9 @@ sdeb_meta_read_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_meta_read_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock) {
-		if (sip)
-			__release(&sip->macc_meta_lck);
-		else
-			__release(&sdeb_fake_rw_lck);
-	} else {
+	if (!sdebug_no_rwlock) {
 		if (sip)
 			read_unlock(&sip->macc_meta_lck);
 		else
@@ -4188,13 +4188,9 @@ sdeb_meta_read_unlock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_meta_write_lock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock) {
-		if (sip)
-			__acquire(&sip->macc_meta_lck);
-		else
-			__acquire(&sdeb_fake_rw_lck);
-	} else {
+	if (!sdebug_no_rwlock) {
 		if (sip)
 			write_lock(&sip->macc_meta_lck);
 		else
@@ -4204,13 +4200,9 @@ sdeb_meta_write_lock(struct sdeb_store_info *sip)
 
 static inline void
 sdeb_meta_write_unlock(struct sdeb_store_info *sip)
+	__context_unsafe(conditional locking)
 {
-	if (sdebug_no_rwlock) {
-		if (sip)
-			__release(&sip->macc_meta_lck);
-		else
-			__release(&sdeb_fake_rw_lck);
-	} else {
+	if (!sdebug_no_rwlock) {
 		if (sip)
 			write_unlock(&sip->macc_meta_lck);
 		else
@@ -5239,6 +5231,7 @@ static int resp_write_scat(struct scsi_cmnd *scp,
 
 static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 			   u32 ei_lba, bool unmap, bool ndob)
+	__context_unsafe(conditional locking)
 {
 	struct scsi_device *sdp = scp->device;
 	struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
#3 lore raw

Re: [PATCH v3 02/57] scsi: scsi_debug: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [Medium] The __context_unsafe(conditional locking) annotation was applied to resp_write_same but missed on resp_read_dt0 and resp_write_dt0, which use identical conditional locking logic.

Pre-existing issues:
- [High] The function resp_write_same() leaks a write lock on the data lock (sip->macc_data_lck) when fetch_to_dev_buffer() fails.
- [High] AB-BA deadlock between sip->macc_meta_lck and sip->macc_data_lck due to inconsistent lock acquisition order. --

--- Patch [2]: [PATCH v3 02/57] scsi: scsi_debug: Prepare for enabling lock context analysis --- commit ad9c0fb7632790d874892a1d6261443b39766d7d
Author: Bart Van Assche <[email protected]>

scsi: scsi_debug: Prepare for enabling lock context analysis

Suppress lock context analysis for the functions that perform conditional locking to prevent that the Clang thread-safety analyzer complains about these functions. Remove incorrect __acquire() and __release() annotations.

Quoted 4 lines
drivers/scsi/scsi_debug.c +0 -0
index 4a08f0cfae08d..cb6663e24c225 100644

[ ... ]

drivers/scsi/scsi_debug.c static int resp_write_scat(struct scsi_cmnd *scp, · line 5191

Quoted 8 lines
drivers/scsi/scsi_debug.c +1 -0
@@ -5193,6 +5185,7 @@ static int resp_write_scat(struct scsi_cmnd *scp,
 
 static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
 			   u32 ei_lba, bool unmap, bool ndob)
+	__context_unsafe(conditional locking)
 {
 	struct scsi_device *sdp = scp->device;
 	struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;

[Severity: Medium]
Should drivers/scsi/scsi_debug.c:resp_read_dt0() and resp_write_dt0() also get this annotation?

They appear to use the same conditional locking pattern with meta_data_locked, conditionally acquiring sip->macc_meta_lck.

[Severity: High]
This is a pre-existing issue, but does drivers/scsi/scsi_debug.c:resp_write_same() leak the sip->macc_data_lck write lock when fetch_to_dev_buffer() fails?

If fetch_to_dev_buffer() returns -1, the code jumps to the out label:

	sdeb_data_write_lock(sip);
	if (ndob) {
		memset(fs1p, 0, lb_size);
		ret = 0;
	} else
		ret = fetch_to_dev_buffer(scp, fs1p, lb_size);

	if (-1 == ret) {
		ret = DID_ERROR << 16;
		goto out;
	}

But the out label only unlocks sip->macc_meta_lck and skips sdeb_data_write_unlock():

out:

	if (meta_data_locked)
		sdeb_meta_write_unlock(sip);
	return ret;

[Severity: High]
This is also a pre-existing issue, but could drivers/scsi/scsi_debug.c:resp_write_same() cause a lock ordering deadlock with resp_comp_write()?

In resp_write_same(), the meta lock is acquired first, followed by the data lock:

	if (sdebug_dev_is_zoned(devip) || scsi_debug_lbp()) {
		sdeb_meta_write_lock(sip);
		meta_data_locked = true;
	}
...
	sdeb_data_write_lock(sip);

However, in resp_comp_write(), the acquisition order is reversed:

	sdeb_data_write_lock(sip);
	sdeb_meta_write_lock(sip);

Could this lead to a deadlock if a compare and write command and a write same command are processed concurrently on the same store?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author reviewer
#4 lore raw

PATCH v3 3/57 scsi: sg: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Annotate open_wait() with __must_hold() since it unlocks and locks a mutex.

---
 drivers/scsi/sg.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/sg.c +1 -0
index 74cd4e8a61c2..dd3ca717ed13 100644
@@ -245,6 +245,7 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd)
 
 static int
 open_wait(Sg_device *sdp, int flags)
+	__must_hold(&sdp->open_rel_lock)
 {
 	int retval = 0;
 
author reviewer
#5 lore raw

PATCH v3 4/57 scsi: st: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document what mutex is released by st_common_ioctl().

---
 drivers/scsi/st.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/st.c +1 -0
index f1c3c4946637..234482e1b70b 100644
@@ -3535,6 +3535,7 @@ static int partition_tape(struct scsi_tape *STp, int size)
 static long st_common_ioctl(struct scsi_tape *STp, struct st_modedef *STm,
 			    struct file *file, unsigned int cmd_in,
 			    unsigned long arg)
+	__releases(&STp->lock)
 {
 	int i, retval = 0;
 
author reviewer
#6 lore raw

PATCH v3 5/57 scsi: BusLogic: Introduce two local variables

In reply to Bart Van Assche

Introduce two local variables to prepare for enabling lock context analysis. No functionality has been changed.

---
 drivers/scsi/BusLogic.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/scsi/BusLogic.c +9 -9
index 5304d2febd63..f2a6ed710c77 100644
@@ -2859,17 +2859,16 @@ static int blogic_hostreset(struct scsi_cmnd *SCpnt)
 {
 	struct blogic_adapter *adapter =
 		(struct blogic_adapter *) SCpnt->device->host->hostdata;
-
+	struct Scsi_Host *shost = adapter->scsi_host;
 	unsigned int id = SCpnt->device->id;
 	struct blogic_tgt_stats *stats = &adapter->tgt_stats[id];
 	int rc;
 
-	spin_lock_irq(SCpnt->device->host->host_lock);
-
+	spin_lock_irq(shost->host_lock);
 	blogic_inc_count(&stats->adapter_reset_req);
-
 	rc = blogic_resetadapter(adapter, false);
-	spin_unlock_irq(SCpnt->device->host->host_lock);
+	spin_unlock_irq(shost->host_lock);
+
 	return rc;
 }
 
@@ -2886,6 +2885,7 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
 	struct blogic_tgt_flags *tgt_flags =
 		&adapter->tgt_flags[command->device->id];
 	struct blogic_tgt_stats *tgt_stats = adapter->tgt_stats;
+	struct Scsi_Host *const shost = command->device->host;
 	unsigned char *cdb = command->cmnd;
 	int cdblen = command->cmd_len;
 	int tgt_id = command->device->id;
@@ -2915,9 +2915,9 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
 	 */
 	ccb = blogic_alloc_ccb(adapter);
 	if (ccb == NULL) {
-		spin_unlock_irq(adapter->scsi_host->host_lock);
+		spin_unlock_irq(shost->host_lock);
 		blogic_delay(1);
-		spin_lock_irq(adapter->scsi_host->host_lock);
+		spin_lock_irq(shost->host_lock);
 		ccb = blogic_alloc_ccb(adapter);
 		if (ccb == NULL) {
 			command->result = DID_ERROR << 16;
@@ -3062,10 +3062,10 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
 		   be initiated soon.
 		 */
 		if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, ccb)) {
-			spin_unlock_irq(adapter->scsi_host->host_lock);
+			spin_unlock_irq(shost->host_lock);
 			blogic_warn("Unable to write Outgoing Mailbox - Pausing for 1 second\n", adapter);
 			blogic_delay(1);
-			spin_lock_irq(adapter->scsi_host->host_lock);
+			spin_lock_irq(shost->host_lock);
 			if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START,
 						ccb)) {
 				blogic_warn("Still unable to write Outgoing Mailbox - Host Adapter Dead?\n", adapter);
#7 lore raw

Re: [PATCH v3 05/57] scsi: BusLogic: Introduce two local variables

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The driver incorrectly uses spin_unlock_irq() and spin_lock_irq() to temporarily drop the host lock in paths protected by spin_lock_irqsave() or inside hardirq handlers, unconditionally enabling local interrupts and violating IRQ context guarantees. --

--- Patch [5]: [PATCH v3 05/57] scsi: BusLogic: Introduce two local variables --- commit 0f5e462089dbe7ff33a0eecd6a02a423a0f2c9ec
Author: Bart Van Assche <[email protected]>

scsi: BusLogic: Introduce two local variables

The patch introduces two local variables to prepare for enabling lock context analysis. No functionality has been changed.

Quoted 4 lines
drivers/scsi/BusLogic.c +0 -0
index 5304d2febd63e..f2a6ed710c770 100644

[ ... ]

drivers/scsi/BusLogic.c static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) · line 2918

Quoted 6 lines
drivers/scsi/BusLogic.c +1 -1
@@ -2915,9 +2915,9 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
 	 */
 	ccb = blogic_alloc_ccb(adapter);
 	if (ccb == NULL) {
-		spin_unlock_irq(adapter->scsi_host->host_lock);
+		spin_unlock_irq(shost->host_lock);

[Severity: High]
This isn't a bug introduced by this patch, but does using spin_unlock_irq() here unconditionally enable interrupts while we are inside a spin_lock_irqsave() block?

Since blogic_qcmd_lck() is wrapped by DEF_SCSI_QCMD() which acquires the lock with spin_lock_irqsave(), it appears dropping the lock with spin_unlock_irq() will unexpectedly enable local interrupts and corrupt the IRQ state context.

This same pattern also appears to exist in blogic_resetadapter() when called from the hardirq handler blogic_inthandler() during adapter internal errors.

Could this lead to unexpected nested interrupts or IRQ storms on error paths?

Quoted 6 lines
drivers/scsi/BusLogic.c +1 -1
 		blogic_delay(1);
-		spin_lock_irq(adapter->scsi_host->host_lock);
+		spin_lock_irq(shost->host_lock);
 		ccb = blogic_alloc_ccb(adapter);
 		if (ccb == NULL) {
 			command->result = DID_ERROR << 16;

[ ... ]

drivers/scsi/BusLogic.c static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command) · line 3065

Quoted 6 lines
drivers/scsi/BusLogic.c +1 -1
@@ -3062,10 +3062,10 @@ static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
 		   be initiated soon.
 		 */
 		if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, ccb)) {
-			spin_unlock_irq(adapter->scsi_host->host_lock);
+			spin_unlock_irq(shost->host_lock);

[Severity: High]
This is also a pre-existing issue, but won't this spin_unlock_irq() call similarly enable local interrupts unexpectedly for the same reasons as above?

Quoted 7 lines
drivers/scsi/BusLogic.c +1 -1
 			blogic_warn("Unable to write Outgoing Mailbox - Pausing for 1 second\n", adapter);
 			blogic_delay(1);
-			spin_lock_irq(adapter->scsi_host->host_lock);
+			spin_lock_irq(shost->host_lock);
 			if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START,
 						ccb)) {
 				blogic_warn("Still unable to write Outgoing Mailbox - Host Adapter Dead?\n", adapter);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
author reviewer
#8 lore raw

PATCH v3 6/57 scsi: BusLogic: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/BusLogic.c | 1 +
 drivers/scsi/BusLogic.h | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)
drivers/scsi/BusLogic.c +1 -0
index f2a6ed710c77..dafec8b24e58 100644
@@ -2878,6 +2878,7 @@ static int blogic_hostreset(struct scsi_cmnd *SCpnt)
 */
 
 static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
+	__must_hold(command->device->host->host_lock)
 {
 	void (*comp_cb)(struct scsi_cmnd *) = scsi_done;
 	struct blogic_adapter *adapter =
drivers/scsi/BusLogic.h +2 -1
index 24697a5bedc8..c152b93fb6dd 100644
@@ -1279,7 +1279,8 @@ static int blogic_sdev_configure(struct scsi_device *,
 				 struct queue_limits *lim);
 static void blogic_qcompleted_ccb(struct blogic_ccb *);
 static irqreturn_t blogic_inthandler(int, void *);
-static int blogic_resetadapter(struct blogic_adapter *, bool hard_reset);
+static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset)
+	__must_hold(adapter->scsi_host->host_lock);
 static void blogic_msg(enum blogic_msglevel, char *, struct blogic_adapter *, ...);
 static int __init blogic_setup(char *);
 
#9 lore raw

Re: [PATCH v3 06/57] scsi: BusLogic: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [Medium] The lock expressions used in the new __must_hold annotations do not syntactically match the lock variables used inside the functions and their callers, causing false positive context imbalance warnings in static analyzers.
- [Low] The patch misses several other functions in the driver that explicitly require the host lock, leaving the lock context annotations incomplete.

Pre-existing issues:
- [High] spin_unlock_irq() unconditionally enables interrupts in contexts where they must be preserved (e.g., inside spin_lock_irqsave() regions and hard IRQ handlers).
- [High] A failure in DMA mapping triggers a kernel panic via BUG_ON() rather than gracefully failing the command.
- [High] Missing memory barrier (wmb()) between CCB and Action Code writes in blogic_write_outbox.
--

--- Patch [6]: [PATCH v3 06/57] scsi: BusLogic: Prepare for enabling lock context analysis --- commit 927e16987713b901aca16b28f9b8a4d54db270de
Author: Bart Van Assche <[email protected]>

scsi: BusLogic: Prepare for enabling lock context analysis

This commit prepares the BusLogic SCSI driver for lock context analysis. It adds the __must_hold() macro to document locking requirements for blogic_qcmd_lck() and blogic_resetadapter().

drivers/scsi/BusLogic.c static int blogic_hostreset(struct scsi_cmnd *SCpnt) · line 2882

Quoted 10 lines
drivers/scsi/BusLogic.c +1 -0
index f2a6ed710c770..dafec8b24e586 100644
@@ -2878,6 +2878,7 @@ static int blogic_hostreset(struct scsi_cmnd *SCpnt)
 */
 
 static enum scsi_qc_status blogic_qcmd_lck(struct scsi_cmnd *command)
+	__must_hold(command->device->host->host_lock)
 {

[Severity: Medium]
Will this annotation cause false positive context imbalance warnings in static analyzers like Sparse?

The annotation requires command->device->host->host_lock, but the function body internally unlocks and locks shost->host_lock:

	spin_unlock_irq(shost->host_lock);
	blogic_delay(1);
	spin_lock_irq(shost->host_lock);

Since Sparse evaluates lock context expressions syntactically, will this structural mismatch trigger the exact warnings this patch intends to prevent?

[Severity: High]
This isn't a bug introduced by this patch, but doesn't spin_unlock_irq() unconditionally enable interrupts in contexts where they must be preserved?

Looking at blogic_qcmd_lck() in drivers/scsi/BusLogic.c:

	spin_unlock_irq(shost->host_lock);
	blogic_delay(1);
	spin_lock_irq(shost->host_lock);

Since blogic_qcmd_lck() is called from inside a region protected by spin_lock_irqsave() via DEF_SCSI_QCMD, does this break the caller's IRQ state by forcefully enabling interrupts?

[Severity: High]
This is a pre-existing issue, but does a DMA mapping failure here trigger a kernel panic instead of gracefully failing the command?

drivers/scsi/BusLogic.c:blogic_qcmd_lck() {
    ...
	count = scsi_dma_map(command);
	BUG_ON(count < 0);
    ...
}

Under high memory pressure or extreme fragmentation, could scsi_dma_map() return a negative error code and cause a Denial of Service?

Quoted 2 lines
drivers/scsi/BusLogic.c +0 -0
 	void (*comp_cb)(struct scsi_cmnd *) = scsi_done;
 	struct blogic_adapter *adapter =

[ ... ]

drivers/scsi/BusLogic.h static int blogic_sdev_configure(struct scsi_device *, · line 1283

Quoted 11 lines
drivers/scsi/BusLogic.h +2 -1
index 24697a5bedc8b..c152b93fb6ddb 100644
@@ -1279,7 +1279,8 @@ static int blogic_sdev_configure(struct scsi_device *,
 				 struct queue_limits *lim);
 static void blogic_qcompleted_ccb(struct blogic_ccb *);
 static irqreturn_t blogic_inthandler(int, void *);
-static int blogic_resetadapter(struct blogic_adapter *, bool hard_reset);
+static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset)
+	__must_hold(adapter->scsi_host->host_lock);

[Severity: Medium]
Will this syntactic mismatch also cause warnings with static analyzers?

The caller blogic_hostreset() acquires shost->host_lock:

	spin_lock_irq(shost->host_lock);
	blogic_inc_count(&stats->adapter_reset_req);
	rc = blogic_resetadapter(adapter, false);
	spin_unlock_irq(shost->host_lock);

But the annotation requires adapter->scsi_host->host_lock. Could this mismatch lead to analysis noise?

[Severity: Low]
Are there other functions in this driver that explicitly require the host lock but missed this annotation?

Functions like blogic_alloc_ccb() and blogic_write_outbox() have header comments explicitly stating "The Host Adapter's Lock should already have been acquired by the caller":

static struct blogic_ccb *blogic_alloc_ccb(struct blogic_adapter *adapter)

static bool blogic_write_outbox(struct blogic_adapter *adapter,
enum blogic_action action, struct blogic_ccb *ccb)

Would adding __must_hold() to these functions complete the static analysis coverage?

[Severity: High]
This isn't a bug introduced by this patch, but does blogic_resetadapter() forcefully enable hardware interrupts inside a hardirq context?

The interrupt handler blogic_inthandler() calls blogic_resetadapter() on errors. Inside blogic_resetadapter():

	spin_unlock_irq(adapter->scsi_host->host_lock);
	blogic_delay(2);
	spin_lock_irq(adapter->scsi_host->host_lock);

Since this drops the lock using spin_unlock_irq() and busy-waits for 2 seconds, can this lead to hard IRQ re-entrancy, stack overflows, or potential deadlocks?

[Severity: High]
This is a pre-existing issue, but is there a missing memory barrier in blogic_write_outbox()?

drivers/scsi/BusLogic.c:blogic_write_outbox() {
    ...
		next_outbox->ccb = ccb->dma_handle;
		next_outbox->action = action;
		blogic_execmbox(adapter);
    ...
}

Because these assignments to memory polled by DMA are not protected by a compiler or memory barrier, can a weakly-ordered CPU reorder the stores to next_outbox->ccb and next_outbox->action?

If the Host Adapter asynchronously polls and observes the new action code before the CCB address is fully committed to memory, could this cause the hardware to execute invalid or stale CCB addresses?

Quoted 2 lines
drivers/scsi/BusLogic.h +0 -0
 static void blogic_msg(enum blogic_msglevel, char *, struct blogic_adapter *, ...);
 static int __init blogic_setup(char *);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
author reviewer
#10 lore raw

PATCH v3 7/57 scsi: NCR5380: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Expand 'hostdata' in the lock context annotations because 'hostdata' is not a function argument.

---
 drivers/scsi/NCR5380.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/NCR5380.c +2 -2
index 006dcf981218..029fe6362629 100644
@@ -961,7 +961,7 @@ static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id)
  */
 
 static bool NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
-	__releases(&hostdata->lock) __acquires(&hostdata->lock)
+	__must_hold(&((struct NCR5380_hostdata *)shost_priv(instance))->lock)
 {
 	struct NCR5380_hostdata *hostdata = shost_priv(instance);
 	unsigned char tmp[3], phase;
@@ -1657,7 +1657,7 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance,
  */
 
 static void NCR5380_information_transfer(struct Scsi_Host *instance)
-	__releases(&hostdata->lock) __acquires(&hostdata->lock)
+	__must_hold(&((struct NCR5380_hostdata *)shost_priv(instance))->lock)
 {
 	struct NCR5380_hostdata *hostdata = shost_priv(instance);
 	unsigned char msgout = NOP;
author reviewer
#11 lore raw

PATCH v3 8/57 scsi: aacraid: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document the aac_send_reset_adapter() locking requirements with __must_hold(). Annotate functions that perform conditional locking with __no_context_analysis.

---
 drivers/scsi/aacraid/Makefile   | 2 ++
 drivers/scsi/aacraid/commctrl.c | 1 +
 drivers/scsi/aacraid/commsup.c  | 3 +++
 3 files changed, 6 insertions(+)
drivers/scsi/aacraid/Makefile +2 -0
index 8f0eec682bb6..415e0ed5ad24 100644
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 # Adaptec aacraid
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
 
 aacraid-objs	:= linit.o aachba.o commctrl.o comminit.o commsup.o \
drivers/scsi/aacraid/commctrl.c +1 -0
index bd82aeb679ae..ac73c7b6d7be 100644
@@ -1043,6 +1043,7 @@ struct aac_reset_iop {
 };
 
 static int aac_send_reset_adapter(struct aac_dev *dev, void __user *arg)
+	__must_hold(&dev->ioctl_mutex)
 {
 	struct aac_reset_iop reset;
 	int retval;
drivers/scsi/aacraid/commsup.c +3 -0
index c4485629f792..fb4d78233c6d 100644
@@ -475,6 +475,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw
 int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
 		int priority, int wait, int reply, fib_callback callback,
 		void *callback_data)
+	__context_unsafe(conditional locking)
 {
 	struct aac_dev * dev = fibptr->dev;
 	struct hw_fib * hw_fib = fibptr->hw_fib_va;
@@ -698,6 +699,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
 
 int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
 		void *callback_data)
+	__context_unsafe(conditional locking)
 {
 	struct aac_dev *dev = fibptr->dev;
 	int wait;
@@ -1466,6 +1468,7 @@ static void aac_schedule_bus_scan(struct aac_dev *aac)
 }
 
 static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
+	__context_unsafe(conditional locking)
 {
 	int index, quirks;
 	int retval;
#12 lore raw

Re: [PATCH v3 08/57] scsi: aacraid: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Data race on fibptr->flags in aac_hba_send and aac_fib_send can lead to lost flags set by the interrupt handler.
- [High] Permanent leak of dev->management_fib_count on early error paths in aac_fib_send and aac_hba_send.
- [High] Dangling pointer in dev->sync_fib_list when wait_for_completion_interruptible is interrupted by a signal.
- [High] The _aac_reset_adapter() function can sleep while holding a spinlock (host->host_lock) and with interrupts disabled. --

--- Patch [8]: [PATCH v3 08/57] scsi: aacraid: Prepare for enabling lock context analysis --- commit b24c9703822591fc27fa76398a50df3f082577ac
Author: Bart Van Assche <[email protected]>

scsi: aacraid: Prepare for enabling lock context analysis

Document the aac_send_reset_adapter() locking requirements with __must_hold(). Annotate functions that perform conditional locking with __no_context_analysis.

drivers/scsi/aacraid/commsup.c int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw · line 479

Quoted 10 lines
drivers/scsi/aacraid/commsup.c +1 -0
index c4485629f7923..fb4d78233c6d7 100644
@@ -475,6 +475,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw
 int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
 		int priority, int wait, int reply, fib_callback callback,
 		void *callback_data)
+	__context_unsafe(conditional locking)
 {

[Severity: High]
This isn't a bug introduced by this patch, but does an interrupted wait leave a dangling pointer in dev->sync_fib_list?

In aac_fib_send(), if dev->sync_mode is enabled and wait is non-zero, fibptr is appended to dev->sync_fib_list. The code then waits:

    if (wait_for_completion_interruptible(&fibptr->event_wait)) {
        fibptr->flags &= ~FIB_CONTEXT_FLAG_WAIT;
        return -EFAULT;
    }

If interrupted by a signal, it returns -EFAULT without removing fibptr from dev->sync_fib_list. The caller will free the FIB, adding it to the free list while it remains on sync_fib_list. Will this lead to a use-after-free when dev->sync_fib_list is later processed?

[ ... ]

drivers/scsi/aacraid/commsup.c int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, · line 703

Quoted 6 lines
drivers/scsi/aacraid/commsup.c +1 -0
@@ -698,6 +699,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
 
 int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
 		void *callback_data)
+	__context_unsafe(conditional locking)
 {

[Severity: High]
This isn't a bug introduced by this patch, but is it possible that dev->management_fib_count leaks on early error paths?

In aac_hba_send(), when wait is 1, dev->management_fib_count is incremented. If aac_adapter_deliver() succeeds, but the PCI device goes offline:

    if (wait) {
        spin_unlock_irqrestore(&fibptr->event_lock, flags);

        if (unlikely(aac_pci_offline(dev)))
            return -EFAULT;

The function returns -EFAULT directly without decrementing the management count. Because fibptr->done is not set to 2, the interrupt handler will also not decrement it. A similar leak appears to occur in aac_fib_send().

Will this eventually exhaust the AAC_NUM_MGT_FIB pool and cause future management commands to fail with -EBUSY?

[Severity: Critical]
This isn't a bug introduced by this patch, but does the lockless update of fibptr->flags risk losing flags set by the interrupt handler?

In aac_hba_send(), after the command is submitted via aac_adapter_deliver():

    if (wait) {
        ...
        fibptr->flags |= FIB_CONTEXT_FLAG_WAIT;
        if (wait_for_completion_interruptible(&fibptr->event_wait))
            fibptr->done = 2;
        fibptr->flags &= ~(FIB_CONTEXT_FLAG_WAIT);

These bitwise operations are not atomic. Concurrently, the adapter can complete the command and trigger the interrupt handler aac_intr_normal(), which locklessly sets flags:

    if (isFastResponse)
        fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;

Can the read-modify-write in aac_hba_send() overwrite and lose the FIB_CONTEXT_FLAG_FASTRESP flag, causing the completion path to misinterpret the response?

[ ... ]

drivers/scsi/aacraid/commsup.c static void aac_schedule_bus_scan(struct aac_dev *aac) · line 1472

Quoted 6 lines
drivers/scsi/aacraid/commsup.c +1 -0
@@ -1466,6 +1468,7 @@ static void aac_schedule_bus_scan(struct aac_dev *aac)
 }
 
 static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
+	__context_unsafe(conditional locking)
 {

[Severity: High]
This isn't a bug introduced by this patch, but does _aac_reset_adapter() sleep while holding a spinlock?

aac_reset_adapter() acquires host->host_lock (a spinlock) and calls _aac_reset_adapter(). Inside _aac_reset_adapter(), the lock is conditionally dropped (setting jafo = 1) only if aac->thread is not NULL and the current thread is not the command thread.

If aac->thread is NULL, or if called by the command thread itself, jafo remains 0 and the spinlock is never unlocked. The function then proceeds to call blocking functions like schedule() and ssleep():

    if (is_response_expected || fib->flags & FIB_CONTEXT_FLAG_WAIT) {
        ...
        schedule();
        retval = 0;
    }

    /* Give some extra time for ioctls to complete. */
    if (retval == 0)
        ssleep(2);

Could this lead to a deadlock since the spinlock is held with interrupts disabled?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
author reviewer
#13 lore raw

PATCH v3 9/57 scsi: aic7xxx: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/aic7xxx/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/aic7xxx/Makefile +2 -0
index 853c72a81ae0..2da370d3d904 100644
@@ -5,6 +5,8 @@
 # $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Makefile#8 $
 #
 
+CONTEXT_ANALYSIS := y
+
 # Let kbuild descend into aicasm when cleaning
 subdir-				+= aicasm
 
author reviewer
#14 lore raw

PATCH v3 10/57 scsi: aha152x: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Annotate is_complete() with __context_unsafe() because it performs conditional locking.

---
 drivers/scsi/aha152x.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/aha152x.c +1 -0
index e3ccb6bb62c0..c16dcb9274eb 100644
@@ -2319,6 +2319,7 @@ static void rsti_run(struct Scsi_Host *shpnt)
  *
  */
 static void is_complete(struct Scsi_Host *shpnt)
+	__context_unsafe(conditional locking)
 {
 	int dataphase;
 	unsigned long flags;
#15 lore raw

Re: [PATCH v3 10/57] scsi: aha152x: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [High] Preexisting list corruption vulnerability due to unlocked accesses to DISCONNECTED_SC.
- [High] Possible NULL pointer dereference in SCSI phase handlers if CURRENT_SC is NULL due to unexpected target behavior.
- [High] Calling INIT_WORK dynamically in an interrupt handler for a global work_struct corrupts the workqueue.
- [High] The global aha152x_host_list is modified and traversed concurrently without any locking.
- [High] The driver fails to cancel the global workqueue during device release or module unload.
--

--- Patch [10]: [PATCH v3 10/57] scsi: aha152x: Prepare for enabling lock context analysis --- commit 4e1483c06dfed36c67b1da76d5119c68317479aa
Author: Bart Van Assche <[email protected]>

scsi: aha152x: Prepare for enabling lock context analysis

Annotate is_complete() with __context_unsafe() because it performs conditional locking.

drivers/scsi/aha152x.c static void rsti_run(struct Scsi_Host *shpnt) · line 2325

Quoted 12 lines
drivers/scsi/aha152x.c +1 -0
index e3ccb6bb62c00..c16dcb9274eb8 100644
@@ -2319,6 +2319,7 @@ static void rsti_run(struct Scsi_Host *shpnt)
  *
  */
 static void is_complete(struct Scsi_Host *shpnt)
+	__context_unsafe(conditional locking)
 {
 	int dataphase;
 	unsigned long flags;

[Severity: High]
This is a pre-existing issue, but is there a risk of list corruption when accessing DISCONNECTED_SC?

In is_complete(), the host lock is dropped before calling phase handlers such as busfree_run():

	DO_UNLOCK(flags);
	...
	if(states[STATE].run)
		states[STATE].run(shpnt);

Then busfree_run() mutates the list without holding the QLOCK:

	append_SC(&DISCONNECTED_SC, CURRENT_SC);

Concurrently, SCSI error handler callbacks like aha152x_abort() modify the exact same list while holding the lock:

	disconnected = issued && remove_SC(&DISCONNECTED_SC, SCpnt);

Can this concurrent modification lead to list corruption?

[Severity: High]
This is a pre-existing issue, but could there be a NULL pointer dereference if a target behaves unexpectedly?

In is_complete(), the state machine transitions to phases like status or cmd based on hardware bus signals via update_state():

	switch(GETPORT(SCSISIG) & P_MASK) {
	case P_MSGI:    STATE=msgi;    break;
	...

This occurs without verifying if a command is currently active. The state handlers then unconditionally dereference CURRENT_SC:

status_run():

	aha152x_priv(CURRENT_SC)->status = GETPORT(SCSIDAT);

cmd_init():

	if (aha152x_priv(CURRENT_SC)->sent_command) {

If the hardware reports a phase while no command is active, would this crash the kernel?

[Severity: High]
This is a pre-existing issue, but does dynamically initializing the global work item in the interrupt handler corrupt the workqueue?

The interrupt handler intr() calls INIT_WORK() before schedule_work():

	INIT_WORK(&aha152x_tq, run);
	schedule_work(&aha152x_tq);

Since aha152x_tq is a global variable, if a second SCSI host adapter triggers an interrupt while the work is already queued by the first adapter, would INIT_WORK() overwrite the list pointers of the pending work?

[Severity: High]
This is a pre-existing issue, but is it safe to traverse and modify the global aha152x_host_list locklessly?

Nodes are added and removed from the list locklessly:

aha152x_probe_one():

	list_add_tail(&HOSTDATA(shpnt)->host_list, &aha152x_host_list);

aha152x_release():

	list_del(&HOSTDATA(shpnt)->host_list);

Concurrently, the workqueue callback run() iterates over the list locklessly:

	list_for_each_entry(hd, &aha152x_host_list, host_list) {

If aha152x_release() removes a host while the workqueue thread is traversing the list, could this lead to following invalid pointers and a use-after-free?

[Severity: High]
This is a pre-existing issue, but could pending work items execute after the module is unloaded or devices are freed?

Neither aha152x_release() nor aha152x_exit() appears to cancel the global workqueue:

aha152x_release():
void aha152x_release(struct Scsi_Host *shpnt)

aha152x_exit():
static void __exit aha152x_exit(void)

If an interrupt schedules the work and the module is immediately unloaded, will the kernel attempt to execute run() after the module's code segment has been unmapped?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
author reviewer
#16 lore raw

PATCH v3 11/57 scsi: aic7xxx: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __acquires() and __releases().

---
 drivers/scsi/aic7xxx/aic79xx_osm.h | 2 ++
 drivers/scsi/aic7xxx/aic7xxx_osm.h | 2 ++
 2 files changed, 4 insertions(+)
drivers/scsi/aic7xxx/aic79xx_osm.h +2 -0
index 793fe19993a9..c6eb2b97e929 100644
@@ -376,12 +376,14 @@ ahd_lockinit(struct ahd_softc *ahd)
 
 static inline void
 ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+	__acquires(&ahd->platform_data->spin_lock)
 {
 	spin_lock_irqsave(&ahd->platform_data->spin_lock, *flags);
 }
 
 static inline void
 ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+	__releases(&ahd->platform_data->spin_lock)
 {
 	spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags);
 }
drivers/scsi/aic7xxx/aic7xxx_osm.h +2 -0
index 51d9f4de0734..5e78fe7d3f32 100644
@@ -389,12 +389,14 @@ ahc_lockinit(struct ahc_softc *ahc)
 
 static inline void
 ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+	__acquires(&ahc->platform_data->spin_lock)
 {
 	spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
 }
 
 static inline void
 ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+	__releases(&ahc->platform_data->spin_lock)
 {
 	spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
 }
author reviewer
#17 lore raw

PATCH v3 12/57 scsi: aic94xx: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/aic94xx/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/aic94xx/Makefile +2 -0
index db9fbe3a8e4c..4ab04ce6edf2 100644
@@ -6,6 +6,8 @@
 # Copyright (C) 2005 Luben Tuikov <[email protected]>
 #
 
+CONTEXT_ANALYSIS := y
+
 ccflags-$(CONFIG_AIC94XX_DEBUG) := -DASD_DEBUG -DASD_ENTER_EXIT
 
 obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
author reviewer
#18 lore raw

PATCH v3 13/57 scsi: arcmsr: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/arcmsr/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/arcmsr/Makefile +2 -0
index 9051f66cae36..6c12d36994da 100644
@@ -2,6 +2,8 @@
 # File: drivers/arcmsr/Makefile
 # Makefile for the ARECA PCI-X PCI-EXPRESS SATA RAID controllers SCSI driver.
 
+CONTEXT_ANALYSIS := y
+
 arcmsr-objs := arcmsr_attr.o arcmsr_hba.o
 
 obj-$(CONFIG_SCSI_ARCMSR) := arcmsr.o
author reviewer
#19 lore raw

PATCH v3 14/57 scsi: arm: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/arm/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/arm/Makefile +2 -0
index b576d9276f71..417d47644a8d 100644
@@ -3,6 +3,8 @@
 # Makefile for drivers/scsi/arm
 #
 
+CONTEXT_ANALYSIS := y
+
 acornscsi_mod-objs	:= acornscsi.o acornscsi-io.o
 
 obj-$(CONFIG_SCSI_ACORNSCSI_3)	+= acornscsi_mod.o queue.o msgqueue.o
author reviewer
#20 lore raw

PATCH v3 15/57 scsi: be2iscsi: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/be2iscsi/be_main.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/scsi/be2iscsi/be_main.c +4 -0
index fd18d4d3d219..289d9d18482c 100644
@@ -1185,6 +1185,7 @@ static void
 be_complete_logout(struct beiscsi_conn *beiscsi_conn,
 		    struct iscsi_task *task,
 		    struct common_sol_cqe *csol_cqe)
+	__must_hold(&beiscsi_conn->conn->session->back_lock)
 {
 	struct iscsi_logout_rsp *hdr;
 	struct beiscsi_io_task *io_task = task->dd_data;
@@ -1212,6 +1213,7 @@ static void
 be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
 		 struct iscsi_task *task,
 		 struct common_sol_cqe *csol_cqe)
+	__must_hold(&beiscsi_conn->conn->session->back_lock)
 {
 	struct iscsi_tm_rsp *hdr;
 	struct iscsi_conn *conn = beiscsi_conn->conn;
@@ -1268,6 +1270,7 @@ static void
 be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
 			struct iscsi_task *task,
 			struct common_sol_cqe *csol_cqe)
+	__must_hold(&beiscsi_conn->conn->session->back_lock)
 {
 	struct iscsi_nopin *hdr;
 	struct iscsi_conn *conn = beiscsi_conn->conn;
@@ -1423,6 +1426,7 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
 static unsigned int
 beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn,
 		struct pdu_base *phdr, void *pdata, unsigned int dlen)
+	__must_hold(&beiscsi_conn->conn->session->back_lock)
 {
 	struct beiscsi_hba *phba = beiscsi_conn->phba;
 	struct iscsi_conn *conn = beiscsi_conn->conn;
#21 lore raw

Re: [PATCH v3 15/57] scsi: be2iscsi: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [Low] The patch adds the __must_hold() lock context annotation to several task completion handlers but misses be_complete_io() and beiscsi_cleanup_task().

Pre-existing issues:
- [High] Synthesized response PDUs fail to initialize statsn, corrupting connection state.
- [Medium] Assignment of a native integer to a __be16 field without endianness conversion. --

--- Patch [15]: [PATCH v3 15/57] scsi: be2iscsi: Prepare for enabling lock context analysis --- commit 4a36248898c49dd5d9e82e5e2120d9998eb707c9
Author: Bart Van Assche <[email protected]>

scsi: be2iscsi: Prepare for enabling lock context analysis

This commit prepares the be2iscsi driver for lock context analysis. It adds the __must_hold() macro to document locking requirements on various completion handlers.

drivers/scsi/be2iscsi/be_main.c static void · line 1188

Quoted 9 lines
drivers/scsi/be2iscsi/be_main.c +1 -0
index fd18d4d3d219b..289d9d18482c3 100644
@@ -1185,6 +1185,7 @@ static void
 be_complete_logout(struct beiscsi_conn *beiscsi_conn,
 		    struct iscsi_task *task,
 		    struct common_sol_cqe *csol_cqe)
+	__must_hold(&beiscsi_conn->conn->session->back_lock)

[Severity: Low]
Does this patch miss annotating some other completion handlers?

For example, hwi_complete_cmd() calls be_complete_io() under the exact same session->back_lock:

hwi_complete_cmd() {
    spin_lock_bh(&session->back_lock);
    ...
    be_complete_io(beiscsi_conn, task, &csol_cqe);
}

Should be_complete_io() and beiscsi_cleanup_task() also receive the __must_hold() annotation to fully document the locking requirements for the core I/O completion and cleanup paths?

[Severity: High]
This is a pre-existing issue, but synthesised response PDUs fail to initialize statsn, which can corrupt the connection state.

In this function, the response PDU is constructed using the memory of the original request PDU:

be_complete_logout() {
    hdr = (struct iscsi_logout_rsp *)task->hdr;
    hdr->opcode = ISCSI_OP_LOGOUT_RSP;
    ...
    hdr->itt = io_task->libiscsi_itt;
    __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}

Because statsn in the response overlaps exactly with cmdsn in the request, and statsn is left uninitialized, the response inherits the request's sequence number. When libiscsi processes this fake PDU via __iscsi_complete_pdu(), it updates conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1.

Can this overwrite the target status sequence number with the initiator command sequence number, leading to immediate connection state corruption and breaking iSCSI sequence numbering?

This same issue also appears in be_complete_tmf() and be_complete_nopin_resp().

[Severity: Medium]
This is a pre-existing issue, but is a native integer assigned to a __be16 field without endianness conversion here?

be_complete_logout() {
    hdr = (struct iscsi_logout_rsp *)task->hdr;
    ...
    hdr->t2wait = 5;
    ...
}

The driver assigns the native integer 5 to hdr->t2wait (a __be16 field) without using cpu_to_be16(). On little-endian architectures, this stores 0x05, 0x00.

When iscsid parses this via be16_to_cpu(), it interprets the value as 1280 seconds instead of 5. Can this cause the initiator to wait ~21 minutes before attempting task reassignment during logout recovery?

Quoted 3 lines
drivers/scsi/be2iscsi/be_main.c +0 -0
 {
 	struct iscsi_logout_rsp *hdr;
 	struct beiscsi_io_task *io_task = task->dd_data;

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=15
author reviewer
#22 lore raw

PATCH v3 16/57 scsi: be2iscsi: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/be2iscsi/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/be2iscsi/Makefile +2 -0
index 910885343a75..73d47c687007 100644
@@ -4,6 +4,8 @@
 #
 #
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_BE2ISCSI) += be2iscsi.o
 
 be2iscsi-y := be_iscsi.o be_main.o be_mgmt.o be_cmds.o
author reviewer
#23 lore raw

PATCH v3 17/57 scsi: cxgbi: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/cxgbi/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/cxgbi/Makefile +3 -0
index abfd38a26fec..c614afa18a33 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb
 
 obj-$(CONFIG_SCSI_CXGB3_ISCSI)	+= libcxgbi.o cxgb3i/
author reviewer
#24 lore raw

PATCH v3 18/57 scsi: bfa: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/bfa/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/bfa/Makefile +3 -0
index 442fc3db8f1f..50c16fcfb01c 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_BFA_FC) := bfa.o
 
 bfa-y := bfad.o bfad_im.o bfad_attr.o bfad_debugfs.o bfad_bsg.o
author reviewer
#25 lore raw

PATCH v3 19/57 scsi: bnx2fc: Enable lock context analysis

In reply to Bart Van Assche

Document the locking requirements with __must_hold(). Inform the compiler about aliases for synchronization objects with __assume_ctx_lock(). Make the argument names in the definition and the declaration of bnx2fc_process_seq_cleanup_compl() consistent.

---
 drivers/scsi/bnx2fc/Makefile     | 3 +++
 drivers/scsi/bnx2fc/bnx2fc.h     | 5 +++--
 drivers/scsi/bnx2fc/bnx2fc_els.c | 2 ++
 drivers/scsi/bnx2fc/bnx2fc_hwi.c | 3 +++
 drivers/scsi/bnx2fc/bnx2fc_io.c  | 6 +++++-
 5 files changed, 16 insertions(+), 3 deletions(-)
drivers/scsi/bnx2fc/Makefile +3 -0
index 1d72e279a97d..3feb6b4dae64 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_BNX2X_FCOE) += bnx2fc.o
 
 bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o \
drivers/scsi/bnx2fc/bnx2fc.h +3 -2
index 8c8968ec8cb4..566632d7e880 100644
@@ -592,9 +592,10 @@ int bnx2fc_send_stat_req(struct bnx2fc_hba *hba);
 int bnx2fc_post_io_req(struct bnx2fc_rport *tgt, struct bnx2fc_cmd *io_req);
 int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req);
 int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl);
-void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnup_req,
+void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnp_req,
 				      struct fcoe_task_ctx_entry *task,
-				      u8 rx_state);
+				      u8 rx_state)
+	__must_hold(&seq_clnp_req->cb_arg->aborted_io_req->tgt->tgt_lock);
 int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset,
 				enum fc_rctl r_ctl);
 
drivers/scsi/bnx2fc/bnx2fc_els.c +2 -0
index 749e30aaf926..1f333a135879 100644
@@ -263,6 +263,7 @@ int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp)
 }
 
 static void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg)
+	__must_hold(&cb_arg->aborted_io_req->tgt->tgt_lock)
 {
 	struct bnx2fc_mp_req *mp_req;
 	struct fc_frame_header *fc_hdr, *fh;
@@ -373,6 +374,7 @@ static void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg)
 }
 
 static void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg)
+	__must_hold(&cb_arg->aborted_io_req->tgt->tgt_lock)
 {
 	struct bnx2fc_cmd *orig_io_req, *new_io_req;
 	struct bnx2fc_cmd *rec_req;
drivers/scsi/bnx2fc/bnx2fc_hwi.c +3 -0
index a5ecb87d5b2d..a3670c48900b 100644
@@ -880,6 +880,9 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe,
 		return;
 	}
 
+	/* Tell the compiler that there is an alias for tgt->tgt_lock. */
+	__assume_ctx_lock(&io_req->cb_arg->aborted_io_req->tgt->tgt_lock);
+
 	/* Timestamp IO completion time */
 	cmd_type = io_req->cmd_type;
 
drivers/scsi/bnx2fc/bnx2fc_io.c +5 -1
index 9c7a541a4523..351c72404b5f 100644
@@ -1080,7 +1080,7 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 }
 
 static int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req)
-	__must_hold(&tgt->tgt_lock)
+	__must_hold(&io_req->tgt->tgt_lock)
 {
 	struct bnx2fc_rport *tgt = io_req->tgt;
 	unsigned int time_left;
@@ -1207,6 +1207,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
 		if (cancel_delayed_work(&io_req->timeout_work))
 			kref_put(&io_req->refcount,
 				 bnx2fc_cmd_release); /* drop timer hold */
+		/* Tell the compiler that io_req->tgt == tgt. */
+		__assume_ctx_lock(&io_req->tgt->tgt_lock);
 		/*
 		 * We don't want to hold off the upper layer timer so simply
 		 * cleanup the command and return that I/O was successfully
@@ -1258,6 +1260,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
 		/* Let the scsi-ml try to recover this command */
 		printk(KERN_ERR PFX "abort failed, xid = 0x%x\n",
 		       io_req->xid);
+		/* Tell the compiler that io_req->tgt == tgt. */
+		__assume_ctx_lock(&io_req->tgt->tgt_lock);
 		/*
 		 * Cleanup firmware residuals before returning control back
 		 * to SCSI ML.
#26 lore raw

Re: [PATCH v3 19/57] scsi: bnx2fc: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Unconditional evaluation of nested pointer chain in `__assume_ctx_lock()` causes a potential fatal…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unconditional evaluation of nested pointer chain in __assume_ctx_lock() causes a potential fatal NULL pointer dereference for standard SCSI commands. --

--- Patch [19]: [PATCH v3 19/57] scsi: bnx2fc: Enable lock context analysis --- commit 914539bc4256a83053d649d7450d8f075fb16ba8
Author: Bart Van Assche <[email protected]>

scsi: bnx2fc: Enable lock context analysis

This patch enables context lock analysis by documenting locking requirements and informing the compiler about lock aliases. It updates functions with __must_hold() and __assume_ctx_lock() to track context around target locks. It also standardizes an argument name in bnx2fc_process_seq_cleanup_compl().

drivers/scsi/bnx2fc/bnx2fc_hwi.c void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe, · line 884

Quoted 10 lines
drivers/scsi/bnx2fc/bnx2fc_hwi.c +2 -0
index a5ecb87d5b2d7..a3670c48900ba 100644
@@ -880,6 +880,9 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe,
 		return;
 	}
 
+	/* Tell the compiler that there is an alias for tgt->tgt_lock. */
+	__assume_ctx_lock(&io_req->cb_arg->aborted_io_req->tgt->tgt_lock);

[Severity: High]
Does this unconditionally evaluate io_req->cb_arg?

For standard SCSI commands, io_req->cb_arg is NULL.

Since the kernel is compiled with -fno-delete-null-pointer-checks, computing the address of tgt_lock requires evaluating the intermediate pointers.

This means the compiler is highly likely to emit a memory load rather than optimizing away the dead read, since it treats address 0 as valid.

Could this lead to a fatal NULL pointer dereference on every standard IO completion processed by bnx2fc_process_cq_compl()?

It appears this annotation might belong solely inside the BNX2FC_SEQ_CLEANUP case where cb_arg is known to be valid.

Quoted 4 lines
drivers/scsi/bnx2fc/bnx2fc_hwi.c +1 -0
+
 	/* Timestamp IO completion time */
 	cmd_type = io_req->cmd_type;
 
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=19
author reviewer
#27 lore raw

PATCH v3 20/57 scsi: bnx2i: Introduce a local variable

In reply to Bart Van Assche

Prepare for adding a new statement that will use the new local variable 'conn'. No functionality has been changed.

---
 drivers/scsi/bnx2i/bnx2i_hwi.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/scsi/bnx2i/bnx2i_hwi.c +3 -3
index d24cc2c795d6..4fb68ec8e9b0 100644
@@ -1741,6 +1741,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
 				     struct bnx2i_conn *bnx2i_conn,
 				     struct cqe *cqe)
 {
+	struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
 	struct bnx2i_async_msg *async_cqe;
 	struct iscsi_async *resp_hdr;
 	u8 async_event;
@@ -1751,7 +1752,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
 	async_event = async_cqe->async_event;
 
 	if (async_event == ISCSI_ASYNC_MSG_SCSI_EVENT) {
-		iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+		iscsi_conn_printk(KERN_ALERT, conn,
 				  "async: scsi events not supported\n");
 		return;
 	}
@@ -1773,8 +1774,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
 	resp_hdr->param2 = cpu_to_be16(async_cqe->param2);
 	resp_hdr->param3 = cpu_to_be16(async_cqe->param3);
 
-	__iscsi_complete_pdu(bnx2i_conn->cls_conn->dd_data,
-			     (struct iscsi_hdr *)resp_hdr, NULL, 0);
+	__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
 	spin_unlock(&session->back_lock);
 }
 
author reviewer
#28 lore raw

PATCH v3 21/57 scsi: bnx2i: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold(). Use __assume_ctx_lock() to inform the compiler about aliases for synchronization objects.

---
 drivers/scsi/bnx2i/Makefile      | 3 +++
 drivers/scsi/bnx2i/bnx2i_hwi.c   | 8 ++++++++
 drivers/scsi/bnx2i/bnx2i_iscsi.c | 1 +
 3 files changed, 12 insertions(+)
drivers/scsi/bnx2i/Makefile +3 -0
index 25378671bb1e..2d8e8f0fdd29 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 bnx2i-y := bnx2i_init.o bnx2i_hwi.o bnx2i_iscsi.o bnx2i_sysfs.o
 
 obj-$(CONFIG_SCSI_BNX2_ISCSI) += bnx2i.o
drivers/scsi/bnx2i/bnx2i_hwi.c +8 -0
index 4fb68ec8e9b0..5d927880d297 100644
@@ -1347,6 +1347,7 @@ int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
 
 	resp_cqe = (struct bnx2i_cmd_response *)cqe;
 	spin_lock_bh(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn,
 				 resp_cqe->itt & ISCSI_CMD_RESPONSE_INDEX);
 	if (!task)
@@ -1443,6 +1444,7 @@ static int bnx2i_process_login_resp(struct iscsi_session *session,
 
 	login = (struct bnx2i_login_response *) cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn,
 				 login->itt & ISCSI_LOGIN_RESPONSE_INDEX);
 	if (!task)
@@ -1511,6 +1513,7 @@ static int bnx2i_process_text_resp(struct iscsi_session *session,
 
 	text = (struct bnx2i_text_response *) cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn, text->itt & ISCSI_LOGIN_RESPONSE_INDEX);
 	if (!task)
 		goto done;
@@ -1570,6 +1573,7 @@ static int bnx2i_process_tmf_resp(struct iscsi_session *session,
 
 	tmf_cqe = (struct bnx2i_tmf_response *)cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn,
 				 tmf_cqe->itt & ISCSI_TMF_RESPONSE_INDEX);
 	if (!task)
@@ -1609,6 +1613,7 @@ static int bnx2i_process_logout_resp(struct iscsi_session *session,
 
 	logout = (struct bnx2i_logout_response *) cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn,
 				 logout->itt & ISCSI_LOGOUT_RESPONSE_INDEX);
 	if (!task)
@@ -1698,6 +1703,7 @@ static int bnx2i_process_nopin_mesg(struct iscsi_session *session,
 	nop_in = (struct bnx2i_nop_in_msg *)cqe;
 
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	hdr = (struct iscsi_nopin *)&bnx2i_conn->gen_pdu.resp_hdr;
 	memset(hdr, 0, sizeof(struct iscsi_hdr));
 	hdr->opcode = nop_in->op_code;
@@ -1758,6 +1764,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
 	}
 
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	resp_hdr = (struct iscsi_async *) &bnx2i_conn->gen_pdu.resp_hdr;
 	memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
 	resp_hdr->opcode = async_cqe->op_code;
@@ -1803,6 +1810,7 @@ static void bnx2i_process_reject_mesg(struct iscsi_session *session,
 		bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
 
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr;
 	memset(hdr, 0, sizeof(struct iscsi_hdr));
 	hdr->opcode = reject->op_code;
drivers/scsi/bnx2i/bnx2i_iscsi.c +1 -0
index 6c80e5b514fd..c868eada72c3 100644
@@ -1154,6 +1154,7 @@ static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd)
 }
 
 static void bnx2i_cleanup_task(struct iscsi_task *task)
+	__must_hold(&task->conn->session->back_lock)
 {
 	struct iscsi_conn *conn = task->conn;
 	struct bnx2i_conn *bnx2i_conn = conn->dd_data;
#29 lore raw

Re: [PATCH v3 21/57] scsi: bnx2i: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] bnx2i_cleanup_task() calls wait_for_completion_timeout() while libiscsi holds session->frwd_lock, causing a scheduling while atomic bug.
- [High] Unbounded copy in bnx2i_process_reject_mesg allows a malicious iSCSI target to cause a kernel heap buffer overflow.
- [High] Missing bounds check in bnx2i_process_login_resp and bnx2i_process_text_resp leads to out-of-bounds writes of padding zeros. --

--- Patch [21]: [PATCH v3 21/57] scsi: bnx2i: Enable lock context analysis --- commit ba1d4ba847bd9f8639c38b0ed041d30d68619de6
Author: Bart Van Assche <[email protected]>

scsi: bnx2i: Enable lock context analysis

Document locking requirements with __must_hold(). Use __assume_ctx_lock() to inform the compiler about aliases for synchronization objects.

Quoted 4 lines
drivers/scsi/bnx2i/bnx2i_hwi.c +0 -0
index 4fb68ec8e9b08..5d927880d2971 100644

[ ... ]

drivers/scsi/bnx2i/bnx2i_hwi.c static int bnx2i_process_login_resp(struct iscsi_session *session, · line 1450

Quoted 8 lines
drivers/scsi/bnx2i/bnx2i_hwi.c +1 -0
@@ -1443,6 +1444,7 @@ static int bnx2i_process_login_resp(struct iscsi_session *session,
 
 	login = (struct bnx2i_login_response *) cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn,
 				 login->itt & ISCSI_LOGIN_RESPONSE_INDEX);
 	if (!task)

[Severity: High]
This isn't a bug introduced by this patch, but there appears to be a missing bounds check when processing login responses.

In bnx2i_process_login_resp(), the payload length is added to resp_buf to compute resp_wr_ptr:

	bnx2i_conn->gen_pdu.resp_wr_ptr =
					bnx2i_conn->gen_pdu.resp_buf + pld_len;

Could an untrusted iSCSI target send an oversized LOGIN response PDU where pld_len exceeds the 8192 byte capacity of gen_pdu.resp_buf? If so, could this loop write out-of-bounds padding zeros?

	if (pad_len) {
		int i = 0;
		for (i = 0; i < pad_len; i++) {
			bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0;
			bnx2i_conn->gen_pdu.resp_wr_ptr++;
		}
	}

[ ... ]

drivers/scsi/bnx2i/bnx2i_hwi.c static int bnx2i_process_text_resp(struct iscsi_session *session, · line 1519

Quoted 8 lines
drivers/scsi/bnx2i/bnx2i_hwi.c +1 -0
@@ -1511,6 +1513,7 @@ static int bnx2i_process_text_resp(struct iscsi_session *session,
 
 	text = (struct bnx2i_text_response *) cqe;
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	task = iscsi_itt_to_task(conn, text->itt & ISCSI_LOGIN_RESPONSE_INDEX);
 	if (!task)
 		goto done;

[Severity: High]
This is a pre-existing issue, but similar to the login response processing, there seems to be a missing bounds check here in bnx2i_process_text_resp().

If the payload length exceeds the buffer size, could the padding logic write past the buffer?

	bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf +
					  pld_len;
[ ... ]
	if (pad_len) {
		int i = 0;
		for (i = 0; i < pad_len; i++) {
			bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0;
			bnx2i_conn->gen_pdu.resp_wr_ptr++;
		}
	}

[ ... ]

drivers/scsi/bnx2i/bnx2i_hwi.c static void bnx2i_process_reject_mesg(struct iscsi_session *session, · line 1816

Quoted 8 lines
drivers/scsi/bnx2i/bnx2i_hwi.c +1 -0
@@ -1803,6 +1810,7 @@ static void bnx2i_process_reject_mesg(struct iscsi_session *session,
 		bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
 
 	spin_lock(&session->back_lock);
+	__assume_ctx_lock(&conn->session->back_lock);
 	hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr;
 	memset(hdr, 0, sizeof(struct iscsi_hdr));
 	hdr->opcode = reject->op_code;

[Severity: High]
This is a pre-existing issue, but is there a risk of a kernel heap buffer overflow in bnx2i_process_reject_mesg()?

When receiving an iSCSI REJECT message, the CQE's data_length is passed directly to bnx2i_get_rq_buf():

	if (reject->data_length) {
		bnx2i_get_rq_buf(bnx2i_conn, conn->data, reject->data_length);
		bnx2i_put_rq_buf(bnx2i_conn, 1);
	}

In bnx2i_get_rq_buf(), this executes a memcpy into the statically sized conn->data buffer:

	memcpy(ptr, (u8 *) bnx2i_conn->ep->qp.rq_cons_qe, len);

If an untrusted iSCSI target sends an oversized REJECT PDU, will this write past the bounds of the 8192 byte buffer?

drivers/scsi/bnx2i/bnx2i_iscsi.c static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd) · line 1160

Quoted 12 lines
drivers/scsi/bnx2i/bnx2i_iscsi.c +1 -0
index 6c80e5b514fda..c868eada72c3e 100644
@@ -1154,6 +1154,7 @@ static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd)
 }
 
 static void bnx2i_cleanup_task(struct iscsi_task *task)
+	__must_hold(&task->conn->session->back_lock)
 {
 	struct iscsi_conn *conn = task->conn;
 	struct bnx2i_conn *bnx2i_conn = conn->dd_data;

[Severity: High]
This isn't a bug introduced by this patch, but does bnx2i_cleanup_task() sleep while atomic?

During error handling, such as in iscsi_conn_stop() or iscsi_eh_device_reset(), libiscsi acquires the session->frwd_lock and calls fail_scsi_tasks().

If fail_scsi_tasks() drops the task reference count to 0 via __iscsi_put_task(), it synchronously invokes iscsi_free_task(), which calls bnx2i_cleanup_task().

While bnx2i_cleanup_task() drops the back_lock, the implicitly held frwd_lock remains locked when it waits for the completion:

	if (task->state == ISCSI_TASK_ABRT_TMF) {
		bnx2i_send_cmd_cleanup_req(hba, task->dd_data);

		spin_unlock_bh(&conn->session->back_lock);
		wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
				msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
		spin_lock_bh(&conn->session->back_lock);
	}

Could this cause a scheduling while atomic bug when wait_for_completion_timeout is called with frwd_lock still held?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=21
author reviewer
#30 lore raw

PATCH v3 22/57 scsi: csiostor: Enable lock context analysis

In reply to Bart Van Assche

Document lock context requirements with __must_hold().

---
 drivers/scsi/csiostor/Makefile     |  2 ++
 drivers/scsi/csiostor/csio_hw.c    | 12 ++++++++++++
 drivers/scsi/csiostor/csio_lnode.c |  3 +++
 drivers/scsi/csiostor/csio_rnode.c |  6 ++++++
 drivers/scsi/csiostor/csio_scsi.c  |  6 ++++++
 5 files changed, 29 insertions(+)
drivers/scsi/csiostor/Makefile +2 -0
index d047e22eac0d..a19031efa7c4 100644
@@ -4,6 +4,8 @@
 #
 ##
 
+CONTEXT_ANALYSIS := y
+
 ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4
 
 obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor.o
drivers/scsi/csiostor/csio_hw.c +12 -0
index df9f81f29950..4b4d3c42667f 100644
@@ -914,6 +914,7 @@ csio_hw_dev_ready(struct csio_hw *hw)
  */
 static int
 csio_do_hello(struct csio_hw *hw, enum csio_dev_state *state)
+	__must_hold(&hw->lock)
 {
 	struct csio_mb	*mbp;
 	int	rv = 0;
@@ -2050,6 +2051,7 @@ csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
  */
 static int
 csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
+	__must_hold(&hw->lock)
 {
 	struct csio_mb	*mbp = NULL;
 	struct fw_caps_config_cmd *caps_cmd;
@@ -2475,6 +2477,7 @@ static int csio_hw_check_fwver(struct csio_hw *hw)
  */
 static void
 csio_hw_configure(struct csio_hw *hw)
+	__must_hold(&hw->lock)
 {
 	int reset = 1;
 	int rv;
@@ -2604,6 +2607,7 @@ csio_hw_configure(struct csio_hw *hw)
  */
 static void
 csio_hw_initialize(struct csio_hw *hw)
+	__must_hold(&hw->lock)
 {
 	struct csio_mb	*mbp;
 	enum fw_retval retval;
@@ -2778,6 +2782,7 @@ csio_hw_fatal_err(struct csio_hw *hw)
  */
 static void
 csio_hws_uninit(struct csio_hw *hw, enum csio_hw_ev evt)
+	__must_hold(&hw->lock)
 {
 	hw->prev_evt = hw->cur_evt;
 	hw->cur_evt = evt;
@@ -2803,6 +2808,7 @@ csio_hws_uninit(struct csio_hw *hw, enum csio_hw_ev evt)
  */
 static void
 csio_hws_configuring(struct csio_hw *hw, enum csio_hw_ev evt)
+	__must_hold(&hw->lock)
 {
 	hw->prev_evt = hw->cur_evt;
 	hw->cur_evt = evt;
@@ -2988,6 +2994,7 @@ csio_hws_quiescing(struct csio_hw *hw, enum csio_hw_ev evt)
  */
 static void
 csio_hws_quiesced(struct csio_hw *hw, enum csio_hw_ev evt)
+	__must_hold(&hw->lock)
 {
 	hw->prev_evt = hw->cur_evt;
 	hw->cur_evt = evt;
@@ -3013,6 +3020,7 @@ csio_hws_quiesced(struct csio_hw *hw, enum csio_hw_ev evt)
  */
 static void
 csio_hws_resetting(struct csio_hw *hw, enum csio_hw_ev evt)
+	__must_hold(&hw->lock)
 {
 	hw->prev_evt = hw->cur_evt;
 	hw->cur_evt = evt;
@@ -3074,6 +3082,7 @@ csio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt)
  */
 static void
 csio_hws_pcierr(struct csio_hw *hw, enum csio_hw_ev evt)
+	__must_hold(&hw->lock)
 {
 	hw->prev_evt = hw->cur_evt;
 	hw->cur_evt = evt;
@@ -3763,6 +3772,7 @@ csio_hw_mb_timer(struct timer_list *t)
  */
 static void
 csio_hw_mbm_cleanup(struct csio_hw *hw)
+	__must_hold(&hw->lock)
 {
 	LIST_HEAD(cbfn_q);
 
@@ -3883,6 +3893,7 @@ csio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry)
 
 void
 csio_evtq_flush(struct csio_hw *hw)
+	__must_hold(&hw->lock)
 {
 	uint32_t count;
 	count = 30;
@@ -4142,6 +4153,7 @@ csio_mgmt_tmo_handler(struct timer_list *t)
 
 static void
 csio_mgmtm_cleanup(struct csio_mgmtm *mgmtm)
+	__must_hold(&mgmtm->hw->lock)
 {
 	struct csio_hw *hw = mgmtm->hw;
 	struct csio_ioreq *io_req;
drivers/scsi/csiostor/csio_lnode.c +3 -0
index 78d5ecd14f65..161973957976 100644
@@ -872,6 +872,7 @@ csio_ln_read_fcf_entry(struct csio_lnode *ln,
 static void
 csio_handle_link_up(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
 		    uint32_t vnpi)
+	__must_hold(&hw->lock)
 {
 	struct csio_lnode *ln = NULL;
 
@@ -1165,6 +1166,7 @@ csio_lns_uninit(struct csio_lnode *ln, enum csio_ln_ev evt)
  */
 static void
 csio_lns_online(struct csio_lnode *ln, enum csio_ln_ev evt)
+	__must_hold(&csio_lnode_to_hw(ln)->lock)
 {
 	struct csio_hw *hw = csio_lnode_to_hw(ln);
 
@@ -1216,6 +1218,7 @@ csio_lns_online(struct csio_lnode *ln, enum csio_ln_ev evt)
  */
 static void
 csio_lns_ready(struct csio_lnode *ln, enum csio_ln_ev evt)
+	__must_hold(&csio_lnode_to_hw(ln)->lock)
 {
 	struct csio_hw *hw = csio_lnode_to_hw(ln);
 
drivers/scsi/csiostor/csio_rnode.c +6 -0
index 713e13adf4dc..74ef1e858cf3 100644
@@ -546,6 +546,7 @@ csio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn,
 
 static void
 __csio_reg_rnode(struct csio_rnode *rn)
+	__must_hold(&csio_lnode_to_hw(csio_rnode_to_lnode(rn))->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	struct csio_hw *hw = csio_lnode_to_hw(ln);
@@ -563,6 +564,7 @@ __csio_reg_rnode(struct csio_rnode *rn)
 
 static void
 __csio_unreg_rnode(struct csio_rnode *rn)
+	__must_hold(&csio_lnode_to_hw(csio_rnode_to_lnode(rn))->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	struct csio_hw *hw = csio_lnode_to_hw(ln);
@@ -602,6 +604,7 @@ __csio_unreg_rnode(struct csio_rnode *rn)
  */
 static void
 csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt)
+	__must_hold(&csio_rnode_to_lnode(rn)->hwp->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	int ret = 0;
@@ -642,6 +645,7 @@ csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt)
  */
 static void
 csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt)
+	__must_hold(&csio_rnode_to_lnode(rn)->hwp->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	int ret = 0;
@@ -727,6 +731,7 @@ csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt)
  */
 static void
 csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt)
+	__must_hold(&csio_rnode_to_lnode(rn)->hwp->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	int ret = 0;
@@ -786,6 +791,7 @@ csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt)
  */
 static void
 csio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt)
+	__must_hold(&csio_rnode_to_lnode(rn)->hwp->lock)
 {
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	int ret = 0;
drivers/scsi/csiostor/csio_scsi.c +6 -0
index b1de615cf316..b74fed463640 100644
@@ -1157,6 +1157,7 @@ csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len,
  */
 void
 csio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q)
+	__must_hold(&scm->hw->lock)
 {
 	struct csio_hw *hw = scm->hw;
 	struct csio_ioreq *ioreq;
@@ -1231,6 +1232,7 @@ csio_abrt_cls(struct csio_ioreq *ioreq, struct scsi_cmnd *scmnd)
  */
 static int
 csio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo)
+	__must_hold(&scm->hw->lock)
 {
 	struct csio_hw *hw = scm->hw;
 	struct list_head *tmp, *next;
@@ -1271,6 +1273,7 @@ csio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo)
  */
 int
 csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
+	__must_hold(&scm->hw->lock)
 {
 	struct csio_hw *hw = scm->hw;
 	int rv = 0;
@@ -1316,6 +1319,7 @@ csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
  */
 int
 csio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln)
+	__must_hold(&scm->hw->lock)
 {
 	struct csio_hw *hw = scm->hw;
 	struct csio_scsi_level_data sld;
@@ -2192,6 +2196,8 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd)
 	 * completes, we gather pending I/Os after the LUN reset.
 	 */
 	spin_lock_irq(&hw->lock);
+	/* Tell the compiler that scsim->hw == hw. */
+	__assume_ctx_lock(&scsim->hw->lock);
 	csio_scsi_gather_active_ios(scsim, &sld, &local_q);
 
 	retval = csio_scsi_abort_io_q(scsim, &local_q, 30000);
#31 lore raw

Re: [PATCH v3 22/57] scsi: csiostor: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [Low] The lock context analysis annotations are incomplete and inconsistent, which will trigger false-positive lock warnings during compilation with CONTEXT_ANALYSIS := y.

Pre-existing issues:
- [Critical] I/O request structs (ioreq) are returned to the freelist on abort timeout, leading to use-after-free and data corruption if the firmware later completes the aborted I/O.
- [High] Unsafe use of list_for_each_safe over shared queues across lock drops, leading to infinite loops or use-after-free if an interrupt completes the 'next' I/O.
- [High] Self-deadlock in management request cleanup due to locking assumptions exposed by the newly added annotations.
- [High] Iterating over rnodes with list_for_each_safe while triggering state machine changes that drop the lock allows concurrent modification of the rnode list, invalidating the iterator. --

--- Patch [22]: [PATCH v3 22/57] scsi: csiostor: Enable lock context analysis --- commit 46f9827cf6cee915a530068e489202f662954317
Author: Bart Van Assche <[email protected]>

scsi: csiostor: Enable lock context analysis

This commit enables lock context analysis for the csiostor driver. It applies the __must_hold() annotation to various hardware, logical node, remote node, and SCSI functions that require specific locks to be held by the caller.

Quoted 4 lines
drivers/scsi/csiostor/csio_hw.c +0 -0
index df9f81f299505..4b4d3c42667fa 100644

[ ... ]

drivers/scsi/csiostor/csio_hw.c csio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry) · line 3897

Quoted 6 lines
drivers/scsi/csiostor/csio_hw.c +1 -0
@@ -3883,6 +3893,7 @@ csio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry)
 
 void
 csio_evtq_flush(struct csio_hw *hw)
+	__must_hold(&hw->lock)
 {

[Severity: Low]
Does adding this annotation cause static analysis warnings for unannotated callers? For example, csio_hws_ready() is not annotated but calls this function, which might trigger warnings when CONTEXT_ANALYSIS := y is enabled.

Quoted 2 lines
drivers/scsi/csiostor/csio_hw.c +0 -0
 	uint32_t count;
 	count = 30;

[ ... ]

drivers/scsi/csiostor/csio_hw.c csio_mgmt_tmo_handler(struct timer_list *t) · line 4157

Quoted 6 lines
drivers/scsi/csiostor/csio_hw.c +1 -0
@@ -4142,6 +4153,7 @@ csio_mgmt_tmo_handler(struct timer_list *t)
 
 static void
 csio_mgmtm_cleanup(struct csio_mgmtm *mgmtm)
+	__must_hold(&mgmtm->hw->lock)
 {

[Severity: High]
This is a pre-existing issue, but could this management request cleanup lead to a self-deadlock?

When csio_mgmtm_cleanup() iterates over mgmtm->active_q:

list_for_each(tmp, &mgmtm->active_q) {
        ...
        if (io_req->io_cbfn) {
                io_req->wr_status = -ETIMEDOUT;
                io_req->io_cbfn(mgmtm->hw, io_req);
        }

It invokes the completion callback with hw->lock held. If the callback is csio_ln_fdmi_rhba_cbfn(), it unconditionally attempts to acquire hw->lock:

spin_lock_irqsave(&hw->lock, flags);
if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_rhba_cbfn,

Will this cause the CPU to deadlock on the same lock?

Quoted 2 lines
drivers/scsi/csiostor/csio_hw.c +0 -0
 	struct csio_hw *hw = mgmtm->hw;
 	struct csio_ioreq *io_req;

[ ... ]

Quoted 4 lines
drivers/scsi/csiostor/csio_rnode.c +0 -0
index 713e13adf4dcc..74ef1e858cf39 100644

[ ... ]

drivers/scsi/csiostor/csio_rnode.c __csio_reg_rnode(struct csio_rnode *rn) · line 568

Quoted 6 lines
drivers/scsi/csiostor/csio_rnode.c +1 -0
@@ -563,6 +564,7 @@ __csio_reg_rnode(struct csio_rnode *rn)
 
 static void
 __csio_unreg_rnode(struct csio_rnode *rn)
+	__must_hold(&csio_lnode_to_hw(csio_rnode_to_lnode(rn))->lock)
 {

[Severity: Low]
Will this trigger a static analysis failure due to unresolved pointer aliasing? The lock being asserted here is csio_lnode_to_hw(csio_rnode_to_lnode(rn))->lock, but this function calls csio_scsi_cleanup_io_q() which expects scm->hw->lock. Does this need an __assume_ctx_lock() workaround to prove they are the same lock?

[Severity: High]
This is a pre-existing issue, but does dropping the lock in this function invalidate iterators in higher-level state machine handlers?

For example, csio_post_event_rns() iterates over rnodes using list_for_each_safe(). If an event like CSIO_RNFE_DOWN causes the state machine to call __csio_unreg_rnode(), the lock is temporarily dropped here:

spin_unlock_irq(&hw->lock);
csio_unreg_rnode(rn);
spin_lock_irq(&hw->lock);

During this window, if an incoming firmware event on another CPU (like a LOGO) transitions the 'next' rnode to an uninitialized state and frees its memory via csio_put_rnode(), will the list_for_each_safe() loop in csio_post_event_rns() access the freed 'next' pointer when it resumes?

Quoted 2 lines
drivers/scsi/csiostor/csio_rnode.c +0 -0
 	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
 	struct csio_hw *hw = csio_lnode_to_hw(ln);

[ ... ]

drivers/scsi/csiostor/csio_scsi.c csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len, · line 1161

Quoted 10 lines
drivers/scsi/csiostor/csio_scsi.c +1 -0
index b1de615cf3162..b74fed463640e 100644
@@ -1157,6 +1157,7 @@ csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len,
  */
 void
 csio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q)
+	__must_hold(&scm->hw->lock)
 {

[Severity: High]
This is a pre-existing issue, but can the list_for_each_safe() loop in this function lead to an infinite loop or use-after-free?

When iterating over the queue:

list_for_each_safe(tmp, next, q) {
        ioreq = (struct csio_ioreq *)tmp;
        csio_scsi_drvcleanup(ioreq);
        list_del_init(&ioreq->sm.sm_list);
        scmnd = csio_scsi_cmnd(ioreq);
        spin_unlock_irq(&hw->lock);

If a firmware completion interrupt occurs while hw->lock is dropped, can the ISR process the 'next' I/O request, remove it, and return it to the freelist, causing the loop to resume iterating through the freelist?

Quoted 2 lines
drivers/scsi/csiostor/csio_scsi.c +0 -0
 	struct csio_hw *hw = scm->hw;
 	struct csio_ioreq *ioreq;

[ ... ]

drivers/scsi/csiostor/csio_scsi.c csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort) · line 1323

Quoted 6 lines
drivers/scsi/csiostor/csio_scsi.c +1 -0
@@ -1316,6 +1319,7 @@ csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
  */
 int
 csio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln)
+	__must_hold(&scm->hw->lock)
 {

[Severity: Critical]
This is a pre-existing issue, but does cleaning up I/O queues after an abort timeout risk memory corruption and use-after-free?

If csio_scsi_abort_io_q() times out, it falls back to cleaning up:

rv = csio_scsi_abort_io_q(scm, &ln->cmpl_q, 30000);

if (rv != 0) {
        csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
        csio_scsi_cleanup_io_q(scm, &ln->cmpl_q);
}

This returns the pending ioreq structures to the driver's freelist. If the firmware later completes the aborted I/O, the ISR extracts the old ioreq pointer and overwrites its status:

ioreq = (struct csio_ioreq *)((uintptr_t)

                 (((struct fw_scsi_read_wr *)tempwr)->cookie));

ioreq->wr_status = status;

Could this overwrite memory if the ioreq was reallocated for a new SCSI command since generation counters do not appear to be validated?

Quoted 2 lines
drivers/scsi/csiostor/csio_scsi.c +0 -0
 	struct csio_hw *hw = scm->hw;
 	struct csio_scsi_level_data sld;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=22
author reviewer
#32 lore raw

PATCH v3 23/57 scsi: elx: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/elx/Makefile | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/elx/Makefile +1 -0
index a8537d7a2a6e..0ed508e379fb 100644
@@ -4,6 +4,7 @@
 # * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
 # */
 
+CONTEXT_ANALYSIS := y
 
 obj-$(CONFIG_SCSI_EFCT) := efct.o
 
author reviewer
#33 lore raw

PATCH v3 24/57 scsi: esas2r: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/esas2r/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/esas2r/Makefile +3 -0
index 279d9cb3ca69..325fa0e634fa 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_ESAS2R)	+= esas2r.o
 
 esas2r-objs := esas2r_log.o esas2r_disc.o esas2r_flash.o esas2r_init.o \
author reviewer
#34 lore raw

PATCH v3 25/57 scsi: fcoe: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/fcoe/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/fcoe/Makefile +3 -0
index 1183e80a09e7..5e7121231801 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_FCOE) += fcoe.o
 obj-$(CONFIG_LIBFCOE) += libfcoe.o
 
author reviewer
#35 lore raw

PATCH v3 26/57 scsi: fnic: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold(). Suppress complaints about conditional locking in fnic_device_reset() with __acquire() and __release().

---
 drivers/scsi/fnic/Makefile    |  3 ++
 drivers/scsi/fnic/fdls_disc.c | 52 +++++++++++++++++++++++++++++++++--
 drivers/scsi/fnic/fip.c       |  2 ++
 drivers/scsi/fnic/fnic_fcs.c  |  6 ++++
 drivers/scsi/fnic/fnic_scsi.c |  4 +++
 5 files changed, 65 insertions(+), 2 deletions(-)
drivers/scsi/fnic/Makefile +3 -0
index 48c5e0cb8ca6..22a463380dee 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_FCOE_FNIC) += fnic.o
 
 fnic-y	:= \
drivers/scsi/fnic/fdls_disc.c +50 -2

320 lines not on this page · raw message

drivers/scsi/fnic/fip.c +2 -0
index 21d08a710f0d..6bca0dd89ee1 100644
@@ -602,6 +602,7 @@ void fnic_common_fip_cleanup(struct fnic *fnic)
  * and clean up and restart the vlan discovery.
  */
 void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
+	__must_hold(&fnic->fnic_lock)
 {
 	struct fnic_iport_s *iport = &fnic->iport;
 	struct fip_cvl *cvl_msg = (struct fip_cvl *)fiph;
@@ -678,6 +679,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
  * @frame: Received ethernet frame
  */
 int fdls_fip_recv_frame(struct fnic *fnic, void *frame)
+	__must_hold(&fnic->fnic_lock)
 {
 	struct ethhdr *eth = (struct ethhdr *)frame;
 	struct fip_header *fiph;
drivers/scsi/fnic/fnic_fcs.c +6 -0
index b00672ef8b00..d1d860275b60 100644
@@ -944,6 +944,7 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
 void
 fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport,
 					unsigned long flags)
+	__must_hold(&iport->fnic->fnic_lock)
 {
 	struct fnic *fnic = iport->fnic;
 	struct fc_rport *rport;
@@ -983,6 +984,7 @@ fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport,
 void
 fnic_fdls_remove_tport(struct fnic_iport_s *iport,
 					   struct fnic_tport_s *tport, unsigned long flags)
+	__must_hold(&iport->fnic->fnic_lock)
 {
 	struct fnic *fnic = iport->fnic;
 	struct rport_dd_data_s *rdd_data;
@@ -1032,6 +1034,8 @@ void fnic_delete_fcp_tports(struct fnic *fnic)
 	unsigned long flags;
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	/* Tell the compiler that fnic->iport.fnic == fnic. */
+	__assume_ctx_lock(&fnic->iport.fnic->fnic_lock);
 	list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) {
 		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "removing fcp rport fcid: 0x%x", tport->fcid);
@@ -1059,6 +1063,8 @@ void fnic_tport_event_handler(struct work_struct *work)
 	struct fnic_tport_s *tport;
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	/* Tell the compiler that fnic->iport.fnic == fnic. */
+	__assume_ctx_lock(&fnic->iport.fnic->fnic_lock);
 	list_for_each_entry_safe(cur_evt, next, &fnic->tport_event_list, links) {
 		tport = cur_evt->arg1;
 		switch (cur_evt->event) {
drivers/scsi/fnic/fnic_scsi.c +4 -0
index 9607684bc610..afd6bf03b02d 100644
@@ -2641,6 +2641,8 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 		 * allocated by mid layer.
 		 */
 		mutex_lock(&fnic->sgreset_mutex);
+		/* Fake __release() to keep the lock context analyzer happy. */
+		__release(&fnic->sgreset_mutex);
 		mqtag = fnic->fnic_max_tag_id;
 		new_sc = 1;
 	}  else {
@@ -2826,6 +2828,8 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 
 	if (new_sc) {
 		fnic->sgreset_sc = NULL;
+		/* Fake __acquire() to keep the lock context analyzer happy. */
+		__acquire(&fnic->sgreset_mutex);
 		mutex_unlock(&fnic->sgreset_mutex);
 	}
 
#36 lore raw

Re: [PATCH v3 26/57] scsi: fnic: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] IRQ state corruption due to mixed use of shared and local variables for spinlock IRQ flags.
- [High] Use-After-Free when iterating over tport lists with list_for_each_entry_safe() and dropping the lock. --

--- Patch [26]: [PATCH v3 26/57] scsi: fnic: Enable lock context analysis --- commit b5089a03ad2ecce02a0412b046877b7375670d2d
Author: Bart Van Assche <[email protected]>

scsi: fnic: Enable lock context analysis

This commit documents locking requirements across the fnic driver by adding __must_hold(), __acquire(), and __release() annotations. This allows static analysis tools to verify that spinlocks and mutexes are held in the required contexts, suppressing false positives.

Quoted 4 lines

drivers/scsi/fnic/fdls_disc.c void fnic_del_fabric_timer_sync(struct fnic *fnic) · line 433

Quoted 10 lines
drivers/scsi/fnic/fdls_disc.c +2 -2
@@ -425,8 +426,8 @@ void fnic_del_fabric_timer_sync(struct fnic *fnic)
 	fnic->iport.fabric.del_timer_inprogress = 0;
 }
 
-void fnic_del_tport_timer_sync(struct fnic *fnic,
-						struct fnic_tport_s *tport)
+void fnic_del_tport_timer_sync(struct fnic *fnic, struct fnic_tport_s *tport)
+	__must_hold(&fnic->fnic_lock)
 {
 	tport->del_timer_inprogress = 1;
 	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);

[Severity: High]
This is a pre-existing issue, but does this pattern risk corrupting the IRQ state?

Functions like fdls_tport_timer_callback() and fnic_tport_event_handler() acquire the lock using a local stack variable:

    spin_lock_irqsave(&fnic->fnic_lock, flags);

If they subsequently call down into fnic_del_tport_timer_sync() (which drops the lock using the shared fnic->lock_flags), it restores a stale IRQ state belonging to a completely different thread, since the local acquisition never updated the shared fnic->lock_flags. Could this erroneously enable or disable IRQs prematurely?

[ ... ]

drivers/scsi/fnic/fdls_disc.c fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport) · line 1340

Quoted 8 lines
drivers/scsi/fnic/fdls_disc.c +1 -0
@@ -1322,6 +1334,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 }
 
 bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+	__must_hold(&iport->fnic->fnic_lock)
 {
 	struct fnic_tport_event_s *tport_del_evt;
 	struct fnic *fnic = iport->fnic;

[Severity: High]
This isn't a bug introduced by this patch, but does dropping the lock in this function expose the driver to a Use-After-Free during list iteration?

Functions such as fdls_process_gpn_ft_tgt_list() iterate over the iport->tport_list using list_for_each_entry_safe(tport, next, ...):

    list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
        ...
        fdls_delete_tport(iport, tport);
    }

Inside fdls_delete_tport(), the fnic_lock is temporarily dropped to call functions like fnic_rport_exch_reset(). While the lock is dropped, a concurrent thread (like an incoming ELS frame reject or a workqueue event) can acquire the lock and free the exact tport pointed to by the cached next pointer. When the original loop reacquires the lock and proceeds to the next iteration, wouldn't it dereference the freed next pointer?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=26
author reviewer
#37 lore raw

PATCH v3 27/57 scsi: hisi_sas: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/hisi_sas/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/hisi_sas/Makefile +3 -0
index 742e732cd51d..8576c78d4afe 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_HISI_SAS)		+= hisi_sas_main.o
 obj-$(CONFIG_SCSI_HISI_SAS)		+= hisi_sas_v1_hw.o hisi_sas_v2_hw.o
 obj-$(CONFIG_SCSI_HISI_SAS_PCI)		+= hisi_sas_v3_hw.o
#38 lore raw

Re: [PATCH v3 27/57] scsi: hisi_sas: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Calling synchronize_irq() inside an irqsave spinlock critical section causes a 'scheduling while atomic' bug. --

--- Patch [27]: [PATCH v3 27/57] scsi: hisi_sas: Enable lock context analysis --- commit 289c0dd12dd00005d6efe11e9c29f7fd715a2464
Author: Bart Van Assche <[email protected]>

scsi: hisi_sas: Enable lock context analysis

This commit enables lock context analysis for the hisi_sas driver via the Makefile.

drivers/scsi/hisi_sas/Makefile line 3

Quoted 8 lines
drivers/scsi/hisi_sas/Makefile +2 -0
index 742e732cd51d5..8576c78d4afe3 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y

[Severity: High]
This is a pre-existing issue, but enabling context analysis here might immediately trigger warnings or panics due to a scheduling while atomic bug.

When hardware timeouts or aborts trigger SCSI error handling, and the task is already complete, hisi_sas_abort_task() calls hisi_sas_sync_cq() while holding a spinlock with interrupts disabled:

drivers/scsi/hisi_sas/hisi_sas_main.c:hisi_sas_abort_task() {
    ...
    spin_lock_irqsave(&task->task_state_lock, flags);
    if (task->task_state_flags & SAS_TASK_STATE_DONE) {
        struct hisi_sas_cq *cq;

        if (slot) {
            cq = &hisi_hba->cq[slot->dlvry_queue];
            hisi_sas_sync_cq(cq);
        }
        spin_unlock_irqrestore(&task->task_state_lock, flags);
    ...
}

The hisi_sas_sync_cq() wrapper then directly calls synchronize_irq() for non-poll queues:

drivers/scsi/hisi_sas/hisi_sas_main.c:hisi_sas_sync_cq() {
    if (hisi_sas_queue_is_poll(cq))
        hisi_sas_sync_poll_cq(cq);
    else
        synchronize_irq(cq->irq_no);
}

Will this cause a panic or deadlock when synchronize_irq() calls wait_event() and attempts to sleep while the CPU is holding the task_state_lock spinlock?

Quoted 4 lines
drivers/scsi/hisi_sas/Makefile +1 -0
+
 obj-$(CONFIG_SCSI_HISI_SAS)		+= hisi_sas_main.o
 obj-$(CONFIG_SCSI_HISI_SAS)		+= hisi_sas_v1_hw.o hisi_sas_v2_hw.o
 obj-$(CONFIG_SCSI_HISI_SAS_PCI)		+= hisi_sas_v3_hw.o
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=27
author reviewer
#39 lore raw

PATCH v3 28/57 scsi: hpsa: Prepare for enabling lock context analysis

In reply to Bart Van Assche

mutex_lock_interruptible() returns a negative value upon failure or zero upon success. Since the Clang thread-safety analyzer only supports == 0 and != 0 tests for functions that perform conditional locking, change the == -EINTR test into != 0. This change does not modify the behavior of hpsa_do_reset().

---
 drivers/scsi/hpsa.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/hpsa.c +1 -1
index a1b116cd4723..1b3595c6e036 100644
@@ -3163,7 +3163,7 @@ static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
 	int rc = 0;
 
 	/* We can really only handle one reset at a time */
-	if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) {
+	if (mutex_lock_interruptible(&h->reset_mutex)) {
 		dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n");
 		return -EINTR;
 	}
#40 lore raw

Re: [PATCH v3 28/57] scsi: hpsa: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] If hpsa_send_reset fails with a SCSI error after being submitted to the hardware, dev->commands_outstanding is leaked.
- [High] Concurrent device reset requests cause premature clearing of mutual exclusion flags (reset_in_progress and in_reset), leaving the controller vulnerable to rescans and new OS commands while a reset is actively in progress. --

--- Patch [28]: [PATCH v3 28/57] scsi: hpsa: Prepare for enabling lock context analysis --- commit 9b4522a3e2cfce6e65e269b286bd08359ef94086
Author: Bart Van Assche <[email protected]>

scsi: hpsa: Prepare for enabling lock context analysis

mutex_lock_interruptible() returns a negative value upon failure or zero upon success. Since the Clang thread-safety analyzer only supports == 0 and != 0 tests for functions that perform conditional locking, change the == -EINTR test into != 0. This change does not modify the behavior of hpsa_do_reset().

drivers/scsi/hpsa.c static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev, · line 3166

Quoted 10 lines
drivers/scsi/hpsa.c +1 -1
index 8edad1830abe4..3f38030d6a8d1 100644
@@ -3163,7 +3163,7 @@ static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
 	int rc = 0;
 
 	/* We can really only handle one reset at a time */
-	if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) {
+	if (mutex_lock_interruptible(&h->reset_mutex)) {

[Severity: High]
This is a pre-existing issue, but concurrent device reset requests cause premature clearing of mutual exclusion flags (reset_in_progress and in_reset), leaving the controller vulnerable to rescans and new OS commands while a reset is actively in progress.

If Thread 1 is performing a reset and Thread 2 is blocked on mutex_lock_interruptible(&h->reset_mutex), interrupting Thread 2 (e.g., via Ctrl-C) causes it to exit hpsa_do_reset() and unconditionally clear h->reset_in_progress and dev->in_reset in hpsa_eh_device_reset_handler():

drivers/scsi/hpsa.c:hpsa_eh_device_reset_handler() {
    ...
return_reset_status:
	spin_lock_irqsave(&h->reset_lock, flags);
	h->reset_in_progress = 0;
	if (dev)
		dev->in_reset = false;
	spin_unlock_irqrestore(&h->reset_lock, flags);
}

Does this strip the mutual exclusion protection from Thread 1, exposing the hardware to concurrent commands?

[Severity: High]
This isn't a bug introduced by this patch, but if hpsa_send_reset() fails with a SCSI error after being submitted to the hardware, does it leak dev->commands_outstanding?

If hpsa_send_reset() successfully enqueues the command (incrementing the counter) but the hardware later fails the command (e.g., CMD_TARGET_STATUS), hpsa_send_reset() returns a non-zero error code.

hpsa_do_reset() sees this non-zero rc and skips
atomic_dec(&dev->commands_outstanding):

drivers/scsi/hpsa.c:hpsa_do_reset() {
    ...
	rc = hpsa_send_reset(h, dev, reset_type, reply_queue);
	if (!rc) {
		/* incremented by sending the reset request */
		atomic_dec(&dev->commands_outstanding);
    ...
}

Because CMD_IOCTL_PEND commands do not decrement this counter in their interrupt completion handler, finish_cmd():

drivers/scsi/hpsa.c:finish_cmd() {
    ...
	else if (c->cmd_type == CMD_IOCTL_PEND || c->cmd_type == IOACCEL2_TMF)
		complete(c->waiting);
    ...
}

Is the count permanently leaked, causing an infinite hang on the next reset attempt for the device because wait_event() will block forever?

Quoted 3 lines
drivers/scsi/hpsa.c +0 -0
 		dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n");
 		return -EINTR;
 	}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=28
author reviewer
#41 lore raw

PATCH v3 29/57 scsi: ibmvscsi: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/ibmvscsi/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/ibmvscsi/Makefile +3 -0
index 9408c7f4cdee..de6ed9e64b63 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o
 
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi.o
author reviewer
#42 lore raw

PATCH v3 30/57 scsi: ibmvscsi_tgt: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/ibmvscsi_tgt/Makefile       | 3 +++
 drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 7 +++++++
 2 files changed, 10 insertions(+)
drivers/scsi/ibmvscsi_tgt/Makefile +3 -0
index cc7a8256dcf8..7ae12ee9a347 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvscsis.o
 
 ibmvscsis-y := libsrp.o ibmvscsi_tgt.o
drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +7 -0
index 61f682800765..8e532a195bf9 100644
@@ -251,6 +251,7 @@ static void ibmvscsis_delete_client_info(struct scsi_info *vscsi,
  *	Process level, interrupt lock is held
  */
 static long ibmvscsis_free_command_q(struct scsi_info *vscsi)
+	__must_hold(&vscsi->intr_lock)
 {
 	int bytes;
 	u32 flags_under_lock;
@@ -874,6 +875,7 @@ static long ibmvscsis_establish_new_q(struct scsi_info *vscsi)
  *	Process environment, called with interrupt lock held
  */
 static void ibmvscsis_reset_queue(struct scsi_info *vscsi)
+	__must_hold(&vscsi->intr_lock)
 {
 	int bytes;
 	long rc = ADAPT_SUCCESS;
@@ -974,6 +976,7 @@ static void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi,
  *	Process or interrupt environment called with interrupt lock held
  */
 static long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
+	__must_hold(&vscsi->intr_lock)
 {
 	long rc = 0;
 	struct viosrp_crq *crq;
@@ -1030,6 +1033,7 @@ static long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
  */
 static long ibmvscsis_trans_event(struct scsi_info *vscsi,
 				  struct viosrp_crq *crq)
+	__must_hold(&vscsi->intr_lock)
 {
 	long rc = ADAPT_SUCCESS;
 
@@ -1166,6 +1170,7 @@ static long ibmvscsis_trans_event(struct scsi_info *vscsi,
  *	intr_lock must be held
  */
 static void ibmvscsis_poll_cmd_q(struct scsi_info *vscsi)
+	__must_hold(&vscsi->intr_lock)
 {
 	struct viosrp_crq *crq;
 	long rc;
@@ -1310,6 +1315,7 @@ static struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
  *	Process environment called with interrupt lock held
  */
 static void ibmvscsis_adapter_idle(struct scsi_info *vscsi)
+	__must_hold(&vscsi->intr_lock)
 {
 	int free_qs = false;
 	long rc = 0;
@@ -2520,6 +2526,7 @@ static long ibmvscsis_ping_response(struct scsi_info *vscsi)
  */
 static long ibmvscsis_parse_command(struct scsi_info *vscsi,
 				    struct viosrp_crq *crq)
+	__must_hold(&vscsi->intr_lock)
 {
 	long rc = ADAPT_SUCCESS;
 
#43 lore raw

Re: [PATCH v3 30/57] scsi: ibmvscsi_tgt: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Low] Incomplete implementation of __must_hold locking annotations.

Pre-existing issues:
- [High] Race condition during queue teardown leading to interrupt storm and h_free_crq collision. --

--- Patch [30]: [PATCH v3 30/57] scsi: ibmvscsi_tgt: Enable lock context analysis --- commit 3c00251f51d9e551b9d5359138425cfc65cd2cef
Author: Bart Van Assche <[email protected]>

scsi: ibmvscsi_tgt: Enable lock context analysis

This commit enables lock context analysis for the ibmvscsi_tgt driver. It documents locking requirements by adding the __must_hold() annotation to several functions.

drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c static void ibmvscsis_delete_client_info(struct scsi_info *vscsi, · line 255

Quoted 10 lines
drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +1 -0
index 61f6828007654..8e532a195bf95 100644
@@ -251,6 +251,7 @@ static void ibmvscsis_delete_client_info(struct scsi_info *vscsi,
  *	Process level, interrupt lock is held
  */
 static long ibmvscsis_free_command_q(struct scsi_info *vscsi)
+	__must_hold(&vscsi->intr_lock)
 {

[Severity: High]
This is a pre-existing issue, but could this sequence lead to an interrupt storm or hypervisor faults due to concurrent CRQ access?

Looking at ibmvscsis_free_command_q(), it disables hardware interrupts and then drops the lock:

    vio_disable_interrupts(vscsi->dma_dev);
    // ...
    spin_unlock_bh(&vscsi->intr_lock);
    rc = ibmvscsis_unregister_command_q(vscsi);
    spin_lock_bh(&vscsi->intr_lock);

If a pending ibmvscsis_handle_crq() tasklet on another CPU acquires the lock while it is dropped, it evaluates TARGET_STOP(vscsi) as true and unconditionally re-enables interrupts:

    if (TARGET_STOP(vscsi)) {
        vio_enable_interrupts(vscsi->dma_dev);
        // ...
        spin_unlock_bh(&vscsi->intr_lock);
        return;
    }

Would this cause ibmvscsis_unregister_command_q() to execute concurrently with hardware interrupts actively firing?

[Severity: Low]
Are there other functions that explicitly require the interrupt lock but were missed by this commit?

The commit message claims to "Document locking requirements with __must_hold()", but several other functions in this file contain explicit comments stating they must be called with the lock held, yet lack the __must_hold() annotation.

For example, ibmvscsis_establish_new_q():

/*
* Must be called with interrupt lock held.
*/
static long ibmvscsis_establish_new_q(struct scsi_info *vscsi)

And ibmvscsis_post_disconnect():

/*
* PRECONDITION:
* interrupt lock is held
*/
static void ibmvscsis_post_disconnect(struct scsi_info *vscsi,
uint new_state, uint flag_bits)

Could these also be updated to ensure complete static analysis coverage?

Quoted 2 lines
drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +0 -0
 	int bytes;
 	u32 flags_under_lock;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=30
author reviewer
#44 lore raw

PATCH v3 31/57 scsi: ipr: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/ipr.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/scsi/ipr.c +11 -0
index d207e5e81afe..51092834876c 100644
@@ -1016,6 +1016,7 @@ static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr,
 static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
 				  void (*timeout_func) (struct timer_list *),
 				  u32 timeout)
+	__must_hold(ipr_cmd->ioa_cfg->host->host_lock)
 {
 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
 
@@ -5012,6 +5013,7 @@ static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
  **/
 static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
 			    struct ipr_resource_entry *res)
+	__must_hold(ioa_cfg->host->host_lock)
 {
 	struct ipr_cmnd *ipr_cmd;
 	struct ipr_ioarcb *ioarcb;
@@ -5020,6 +5022,8 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
 
 	ENTER;
 	ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+	/* Tell the compiler that ipr_cmd->ioa_cfg == ioa_cfg. */
+	__assume_ctx_lock(ipr_cmd->ioa_cfg->host->host_lock);
 	ioarcb = &ipr_cmd->ioarcb;
 	cmd_pkt = &ioarcb->cmd_pkt;
 
@@ -5050,6 +5054,7 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
  *	SUCCESS / FAILED
  **/
 static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
+	__must_hold(scsi_cmd->device->host->host_lock)
 {
 	struct ipr_ioa_cfg *ioa_cfg;
 	struct ipr_resource_entry *res;
@@ -5069,6 +5074,9 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
 	if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
 		return FAILED;
 
+	/* Tell the compiler that ioa_cfg->host == scsi_cmd->device->host. */
+	__assume_ctx_lock(ioa_cfg->host->host_lock);
+
 	res->resetting_device = 1;
 	scmd_printk(KERN_ERR, scsi_cmd, "Resetting device\n");
 
@@ -5189,6 +5197,7 @@ static void ipr_abort_timeout(struct timer_list *t)
  *	SUCCESS / FAILED
  **/
 static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
+	__must_hold(scsi_cmd->device->host->host_lock)
 {
 	struct ipr_cmnd *ipr_cmd;
 	struct ipr_ioa_cfg *ioa_cfg;
@@ -5239,6 +5248,8 @@ static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
 		return SUCCESS;
 
 	ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+	/* Tell the compiler that ipr_cmd->ioa_cfg == ioa_cfg. */
+	__assume_ctx_lock(ipr_cmd->ioa_cfg->host->host_lock);
 	ipr_cmd->ioarcb.res_handle = res->res_handle;
 	cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
 	cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
#45 lore raw

Re: [PATCH v3 31/57] scsi: ipr: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Command timers are cancelled asynchronously during adapter removal, which can lead to a use-after-free if the timer callback executes concurrently while the underlying memory is freed. --

--- Patch [31]: [PATCH v3 31/57] scsi: ipr: Prepare for enabling lock context analysis --- commit 2f00fc44ee9f9f3037991b9e96f4f40f0505e35f
Author: Bart Van Assche <[email protected]>

scsi: ipr: Prepare for enabling lock context analysis

Document locking requirements with __must_hold().

drivers/scsi/ipr.c static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr, · line 1021

Quoted 11 lines
drivers/scsi/ipr.c +1 -0
index d207e5e81afe0..51092834876cb 100644
@@ -1016,6 +1016,7 @@ static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr,
 static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
 				  void (*timeout_func) (struct timer_list *),
 				  u32 timeout)
+	__must_hold(ipr_cmd->ioa_cfg->host->host_lock)
 {
 	struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;

[Severity: High]
This is a pre-existing issue, but can the asynchronous timer cancellation during adapter removal lead to a use-after-free?

In drivers/scsi/ipr.c:ipr_fail_all_ops(), pending command timers are cancelled using timer_delete():

drivers/scsi/ipr.c:ipr_fail_all_ops() {
    ...
    timer_delete(&ipr_cmd->timer);
    ...
}

Since this is non-synchronous, if a timer callback like ipr_timeout() is already running on another CPU, it will spin waiting for the lock:

ioa_cfg->host->host_lock

Meanwhile, the removal path in __ipr_remove() initiates the bringdown and eventually calls ipr_free_all_resources(). This frees the command blocks via dma_pool_free() in ipr_free_cmd_blks().

When the delayed timer callback finally acquires the lock, could it dereference the freed ipr_cmd and ioa_cfg structures?

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=31
author reviewer
#46 lore raw

PATCH v3 32/57 scsi: ips: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Annotate ips_next() with __no_context_analysis because it performs conditional locking.

---
 drivers/scsi/ips.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/ips.c +1 -0
index 41ed73966a48..5ef70f058ecd 100644
@@ -2506,6 +2506,7 @@ ips_hainit(ips_ha_t * ha)
 /****************************************************************************/
 static void
 ips_next(ips_ha_t * ha, int intr)
+	__context_unsafe(conditional locking)
 {
 	ips_scb_t *scb;
 	struct scsi_cmnd *SC;
#47 lore raw

Re: [PATCH v3 32/57] scsi: ips: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

Pre-existing issues:
- [High] Calling scsi_done() before unmapping DMA causes a potential use-after-free of the scsi_cmnd structure.
- [High] Incorrect waitlist traversal causes premature loop termination and a potential use-after-free on struct scsi_cmnd if command submission fails.
- [High] Memory allocations use GFP_KERNEL in atomic contexts (while holding a spinlock or in a hardirq handler), which can sleep and cause a kernel panic.
- [High] Process-context functions acquire the SCSI host_lock using spin_lock() instead of spin_lock_irqsave(), leaving them vulnerable to deadlocks from the interrupt handler.
- [High] ips_wait() busy-waits with udelay(1000) for up to 60 seconds with hardware interrupts disabled, leading to a hard lockup.
- [High] Using BUG_ON() for scsi_dma_map() failures causes an unnecessary kernel panic.
--

--- Patch [32]: [PATCH v3 32/57] scsi: ips: Prepare for enabling lock context analysis --- commit 3f51682bbdf047060a964b17e4ca87b13e0de2ad
Author: Bart Van Assche <[email protected]>
scsi: ips: Prepare for enabling lock context analysis

This commit annotates ips_next() with __context_unsafe to indicate it performs conditional locking. This prepares the driver for context lock analysis.

drivers/scsi/ips.c ips_hainit(ips_ha_t * ha) · line 2511

Quoted 11 lines
drivers/scsi/ips.c +1 -0
index 41ed73966a48c..5ef70f058ecd6 100644
@@ -2506,6 +2506,7 @@ ips_hainit(ips_ha_t * ha)
 /****************************************************************************/
 static void
 ips_next(ips_ha_t * ha, int intr)
+	__context_unsafe(conditional locking)
 {
 	ips_scb_t *scb;

[Severity: High]
This isn't a bug introduced by this patch, but does calling scsi_done() before unmapping the DMA cause a potential use-after-free?

In ips_next() and ips_done(), the command is completed before the SCB is freed:

drivers/scsi/ips.c:ips_next() {
    ...
    case IPS_FAILURE:
        if (scb->scsi_cmd) {
            scb->scsi_cmd->result = DID_ERROR << 16;
            scsi_done(scb->scsi_cmd);
        }

        ips_freescb(ha, scb);
        break;
    ...
}

Because scsi_done() hands ownership of the command to the block layer, the block layer might asynchronously complete and free or recycle the struct scsi_cmnd. Could ips_freescb() then dereference a freed pointer when it subsequently calls scsi_dma_unmap(scb->scsi_cmd)?

[Severity: High]
This is a pre-existing issue, but does the waitlist traversal in ips_next() prematurely terminate the loop and risk a use-after-free?

The list linkage is destroyed by explicitly clearing host_scribble:

drivers/scsi/ips.c:ips_next() {
    ...
    SC = ips_removeq_wait(&ha->scb_waitlist, q);
    ...
    SC->host_scribble = NULL;
    ...
}

If command submission fails or completes synchronously, scsi_done() is invoked on the command, passing ownership of the command (aliased by p) to the block layer. Could the loop then execute the following iteration step using potentially freed memory?

drivers/scsi/ips.c:ips_next() {
    ...
    p = (struct scsi_cmnd *) p->host_scribble;
    ...
}

Furthermore, since host_scribble was set to NULL, would p become NULL and terminate the dispatch loop early, starving any remaining queued commands?

[Severity: High]
This isn't a bug introduced by this patch, but do the memory allocations use GFP_KERNEL in atomic contexts?

When ips_next() processes passthru commands, it holds the host_lock spinlock. If called from ips_queue_lck via IPS_INTR_IORL, it runs with interrupts disabled and the lock is not dropped.

During this path, functions like ips_alloc_passthru_buffer() and ips_flash_copperhead() perform allocations using GFP_KERNEL:

drivers/scsi/ips.c:ips_alloc_passthru_buffer() {
    ...
    bigger_buf = dma_alloc_coherent(&ha->pcidev->dev, length,
                                    &dma_busaddr, GFP_KERNEL);
    ...
}

Since GFP_KERNEL allows blocking, can this sleep in an atomic context and trigger a kernel panic?

[Severity: High]
This is a pre-existing issue, but is there a deadlock vulnerability between process-context functions and the interrupt handler?

The hardware interrupt handler do_ipsintr() runs in hardirq context and acquires the lock using spin_lock(). Meanwhile, process-context functions like ips_eh_abort() also acquire it without disabling local interrupts:

drivers/scsi/ips.c:ips_eh_abort() {
    ...
    spin_lock(host->host_lock);
    ...
}

If an interrupt fires on the same CPU while the scsi_eh thread holds the lock, will the interrupt handler attempt to acquire the lock and cause an AB-BA self-deadlock?

[Severity: High]
This isn't a bug introduced by this patch, but does ips_wait() busy-wait for too long with hardware interrupts disabled?

In ips_eh_reset(), hardware interrupts are completely disabled on the local CPU:

drivers/scsi/ips.c:ips_eh_reset() {
    ...
    spin_lock_irq(SC->device->host->host_lock);
    rc = __ips_eh_reset(SC);
    ...
}

This triggers a flush that eventually falls back to a polling loop in ips_wait(), which calls udelay(1000) up to 60,000 times:

drivers/scsi/ips.c:ips_wait() {
    ...
    /* This looks like a very evil loop, but it only does this during start-up */
    udelay(1000);
    time--;
    ...
}

Could looping for up to 60 seconds with interrupts disabled violate latency requirements and trigger the kernel's hard lockup watchdog?

[Severity: High]
This is a pre-existing issue, but does using BUG_ON() for DMA mapping failures cause an unnecessary kernel panic?

In ips_next(), a mapping failure triggers a system panic:

drivers/scsi/ips.c:ips_next() {
    ...
    scb->sg_count = scsi_dma_map(SC);
    BUG_ON(scb->sg_count < 0);
    ...
}

Since scsi_dma_map() can return negative values like -ENOMEM when IOMMU mappings or DMA resources are exhausted, would it be better to gracefully fail the command or propagate the error rather than intentionally crashing the system?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=32
author reviewer
#48 lore raw

PATCH v3 33/57 scsi: isci: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/isci/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/isci/Makefile +3 -0
index da6f04cae272..00fd5c1a8d39 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_ISCI) += isci.o
 isci-objs := init.o phy.o request.o \
 	     remote_device.o port.o \
author reviewer
#49 lore raw

PATCH v3 34/57 scsi: libfc: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/libfc/Makefile  | 2 ++
 drivers/scsi/libfc/fc_disc.c | 6 +++++-
 drivers/scsi/libfc/fc_exch.c | 6 ++++++
 drivers/scsi/libfc/fc_fcp.c  | 4 ++++
 4 files changed, 17 insertions(+), 1 deletion(-)
drivers/scsi/libfc/Makefile +2 -0
index 65396f86c307..b265cf2a84a2 100644
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # $Id: Makefile
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_LIBFC) += libfc.o
 
 libfc-objs := \
drivers/scsi/libfc/fc_disc.c +5 -1
index 7792724d5b97..862516dcd324 100644
@@ -37,7 +37,7 @@
 #define FC_DISC_RETRY_LIMIT	3	/* max retries */
 #define FC_DISC_RETRY_DELAY	500UL	/* (msecs) delay */
 
-static void fc_disc_gpn_ft_req(struct fc_disc *);
+static void fc_disc_gpn_ft_req(struct fc_disc *disc) __must_hold(&disc->disc_mutex);
 static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
 static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
 static void fc_disc_timeout(struct work_struct *);
@@ -200,6 +200,7 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
  * @disc: The discovery object to be restarted
  */
 static void fc_disc_restart(struct fc_disc *disc)
+	__must_hold(&disc->disc_mutex)
 {
 	lockdep_assert_held(&disc->disc_mutex);
 
@@ -250,6 +251,7 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
  * @event: The discovery completion status
  */
 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
+	__must_hold(&disc->disc_mutex)
 {
 	struct fc_lport *lport = fc_disc_lport(disc);
 	struct fc_rport_priv *rdata;
@@ -294,6 +296,7 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
  * @fp:	  The error code encoded as a frame pointer
  */
 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
+	__must_hold(&disc->disc_mutex)
 {
 	struct fc_lport *lport = fc_disc_lport(disc);
 	unsigned long delay = 0;
@@ -374,6 +377,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
  * Goes through the list of IDs and names resulting from a request.
  */
 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
+	__must_hold(&disc->disc_mutex)
 {
 	struct fc_lport *lport;
 	struct fc_gpn_ft_resp *np;
drivers/scsi/libfc/fc_exch.c +6 -0
index 9183a0e9568a..9057a95d8612 100644
@@ -811,6 +811,7 @@ static void fc_exch_timeout(struct work_struct *work)
  */
 static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
 					struct fc_exch_mgr *mp)
+	__context_unsafe(conditionally acquires &_res->ex_lock)
 {
 	struct fc_exch *ep;
 	unsigned int cpu;
@@ -904,6 +905,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
  */
 static struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
 				     struct fc_frame *fp)
+	__context_unsafe(conditionally acquires &_res->ex_lock)
 {
 	struct fc_exch_mgr_anchor *ema;
 	struct fc_exch *ep;
@@ -996,6 +998,8 @@ static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
 
 	ep = fc_exch_alloc(lport, fp);
 	if (ep) {
+		/* Acquired by fc_exch_alloc(). */
+		__acquire(&ep->ex_lock);
 		ep->class = fc_frame_class(fp);
 
 		/*
@@ -2191,6 +2195,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
 		fc_frame_free(fp);
 		return NULL;
 	}
+	/* Acquired by fc_exch_alloc(). */
+	__acquire(&ep->ex_lock);
 	ep->esb_stat |= ESB_ST_SEQ_INIT;
 	fh = fc_frame_header_get(fp);
 	fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
drivers/scsi/libfc/fc_fcp.c +4 -0
index a5139e43ca4c..9ba31a8a5250 100644
@@ -207,6 +207,7 @@ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
  * needed.
  */
 static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
+	__cond_acquires(0, &fsp->scsi_pkt_lock)
 {
 	spin_lock_bh(&fsp->scsi_pkt_lock);
 	if (fsp->state & FC_SRB_COMPL) {
@@ -224,6 +225,7 @@ static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
  * @fsp: The FCP packet to be unlocked and decremented
  */
 static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
+	__releases(&fsp->scsi_pkt_lock)
 {
 	spin_unlock_bh(&fsp->scsi_pkt_lock);
 	fc_fcp_pkt_release(fsp);
@@ -1242,6 +1244,7 @@ static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
  * Called to send an abort and then wait for abort completion
  */
 static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
+	__must_hold(&fsp->scsi_pkt_lock)
 {
 	int rc = FAILED;
 	unsigned long ticks_left;
@@ -1950,6 +1953,7 @@ EXPORT_SYMBOL(fc_queuecommand);
  * The fcp packet lock must be held when calling.
  */
 static void fc_io_compl(struct fc_fcp_pkt *fsp)
+	__must_hold(&fsp->scsi_pkt_lock)
 {
 	struct fc_fcp_internal *si;
 	struct scsi_cmnd *sc_cmd;
#50 lore raw

Re: [PATCH v3 34/57] scsi: libfc: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Incomplete lock context annotations for intermediate and explicitly locked functions, triggering…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Incomplete lock context annotations for intermediate and explicitly locked functions, triggering static analysis warnings. --

--- Patch [34]: [PATCH v3 34/57] scsi: libfc: Enable lock context analysis --- commit 713891d004459388a5a60d9452963a9f124605ff
Author: Bart Van Assche <[email protected]>

scsi: libfc: Enable lock context analysis

Document locking requirements with __must_hold().

drivers/scsi/libfc/Makefile line 5

Quoted 9 lines
drivers/scsi/libfc/Makefile +2 -0
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # $Id: Makefile
 
+CONTEXT_ANALYSIS := y
+

[Severity: Low]
Does enabling context analysis here trigger compiler warnings for other functions that are missing annotations?

For example, shouldn't fc_disc_stop_rports() and fc_exch_done_locked() be annotated? They both explicitly require locks but appear to be missing the __must_hold() annotations:

drivers/scsi/libfc/fc_disc.c:fc_disc_stop_rports() {
    ...
    lockdep_assert_held(&disc->disc_mutex);
    ...
}

[ ... ]

drivers/scsi/libfc/fc_disc.c static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp) · line 204

Quoted 9 lines
drivers/scsi/libfc/fc_disc.c +1 -0
@@ -200,6 +200,7 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
  * @disc: The discovery object to be restarted
  */
 static void fc_disc_restart(struct fc_disc *disc)
+	__must_hold(&disc->disc_mutex)
 {

[Severity: Low]
Does fc_disc_recv_rscn_req() also need the __must_hold() annotation?

It calls this newly annotated fc_disc_restart() function, so without its own annotation, could it break the analysis chain for its callers?

[ ... ]

drivers/scsi/libfc/fc_fcp.c EXPORT_SYMBOL(fc_queuecommand); · line 1957

Quoted 9 lines
drivers/scsi/libfc/fc_fcp.c +1 -0
@@ -1950,6 +1953,7 @@ EXPORT_SYMBOL(fc_queuecommand);
  * The fcp packet lock must be held when calling.
  */
 static void fc_io_compl(struct fc_fcp_pkt *fsp)
+	__must_hold(&fsp->scsi_pkt_lock)
 {

[Severity: Low]
Does fc_fcp_complete_locked() also need to be annotated?

It calls fc_io_compl(), so lacking a __must_hold() annotation might cause a context imbalance warning during analysis.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=34
author reviewer
#51 lore raw

PATCH v3 35/57 scsi: libiscsi: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/libiscsi.c | 19 ++++++++++++++++++-
 include/scsi/libiscsi.h |  5 +++--
 2 files changed, 21 insertions(+), 3 deletions(-)
drivers/scsi/libiscsi.c +18 -1
index 160f02f2f51d..c6abaeb79d9c 100644
@@ -1088,6 +1088,7 @@ static int iscsi_nop_out_rsp(struct iscsi_task *task,
 
 static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 			       char *data, int datalen)
+	__must_hold(&conn->session->back_lock)
 {
 	struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
 	struct iscsi_hdr rejected_pdu;
@@ -1500,6 +1501,7 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
 
 static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task,
 			   bool was_requeue)
+	__must_hold(&conn->session->frwd_lock)
 {
 	int rc;
 
@@ -1915,7 +1917,8 @@ static void iscsi_tmf_timedout(struct timer_list *t)
 static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
 				   struct iscsi_tm *hdr, int age,
 				   int timeout)
-	__must_hold(&session->frwd_lock)
+	__must_hold(&conn->session->frwd_lock)
+	__must_hold(&conn->session->eh_mutex)
 {
 	struct iscsi_session *session = conn->session;
 
@@ -1962,6 +1965,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
  * Fail commands. session frwd lock held and xmit thread flushed.
  */
 static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
+	__must_hold(&conn->session->frwd_lock)
 {
 	struct iscsi_session *session = conn->session;
 	struct iscsi_task *task;
@@ -2408,6 +2412,9 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
 
 	ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
 	conn = session->leadconn;
+	/* Tell the compiler that conn->session == session. */
+	__assume_ctx_lock(&conn->session->eh_mutex);
+	__assume_ctx_lock(&conn->session->frwd_lock);
 	iscsi_get_conn(conn->cls_conn);
 	conn->eh_abort_cnt++;
 	age = session->age;
@@ -2531,6 +2538,9 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
 	if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
 		goto unlock;
 	conn = session->leadconn;
+	/* Tell the compiler that conn->session == session. */
+	__assume_ctx_lock(&conn->session->frwd_lock);
+	__assume_ctx_lock(&conn->session->eh_mutex);
 
 	/* only have one tmf outstanding at a time */
 	if (session->tmf_state != TMF_INITIAL)
@@ -2564,6 +2574,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
 	iscsi_suspend_tx(conn);
 
 	spin_lock_bh(&session->frwd_lock);
+	/* Tell the compiler that conn->session == session. */
+	__assume_ctx_lock(&conn->session->frwd_lock);
 	memset(hdr, 0, sizeof(*hdr));
 	fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
 	session->tmf_state = TMF_INITIAL;
@@ -2693,6 +2705,9 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
 	if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
 		goto unlock;
 	conn = session->leadconn;
+	/* Tell the compiler that conn->session == session. */
+	__assume_ctx_lock(&conn->session->eh_mutex);
+	__assume_ctx_lock(&conn->session->frwd_lock);
 
 	/* only have one tmf outstanding at a time */
 	if (session->tmf_state != TMF_INITIAL)
@@ -2726,6 +2741,8 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
 	iscsi_suspend_tx(conn);
 
 	spin_lock_bh(&session->frwd_lock);
+	/* Tell the compiler that conn->session == session. */
+	__assume_ctx_lock(&conn->session->frwd_lock);
 	memset(hdr, 0, sizeof(*hdr));
 	fail_scsi_tasks(conn, -1, DID_ERROR);
 	session->tmf_state = TMF_INITIAL;
include/scsi/libiscsi.h +3 -2
index 3d765c77bcd9..d39b5b268e6f 100644
@@ -474,8 +474,9 @@ extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
 				char *, uint32_t);
 extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
 			      char *, int);
-extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
-				char *, int);
+extern int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *,
+				char *, int)
+		__must_hold(&conn->session->back_lock);
 extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
 extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
 extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t);
#52 lore raw

Re: [PATCH v3 35/57] scsi: libiscsi: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Low] Missing __must_hold() annotations for core locking functions.
- [Low] Missing __must_hold annotation on __iscsi_complete_pdu() definition.

Pre-existing issues:
- [High] Deadlock due to iscsi_complete_pdu() acquiring session->frwd_lock without disabling softirqs.
- [High] Hard deadlock due to qla4xxx acquiring session->back_lock in a hardware interrupt context while libiscsi core leaves hardirqs enabled. --

--- Patch [35]: [PATCH v3 35/57] scsi: libiscsi: Prepare for enabling lock context analysis --- commit 052b7ed83fc5a572597a78bb52886b250ac80aa2
Author: Bart Van Assche <[email protected]>

scsi: libiscsi: Prepare for enabling lock context analysis

This commit prepares libiscsi for lock context analysis by documenting locking requirements using __must_hold().

Quoted 3 lines
drivers/scsi/libiscsi.c +0 -0

[ ... ]

drivers/scsi/libiscsi.c static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, · line 1969

Quoted 6 lines
drivers/scsi/libiscsi.c +1 -0
@@ -1962,6 +1965,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
  * Fail commands. session frwd lock held and xmit thread flushed.
  */
 static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
+	__must_hold(&conn->session->frwd_lock)
 {

[Severity: Low]
Should we also add these annotations to the core locking functions like fail_scsi_task(), __fail_scsi_task(), and iscsi_send_nopout()?

Functions like __fail_scsi_task() manipulate sequence numbers and queues that explicitly require both back_lock and frwd_lock to be held. Similarly, iscsi_send_nopout() requires frwd_lock. Leaving these unannotated leaves portions of the subsystem's logic unanalyzed by the context checker.

[ ... ]

include/scsi/libiscsi.h extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *, · line 479

Quoted 12 lines
include/scsi/libiscsi.h +3 -2
@@ -474,8 +474,9 @@ extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
 				char *, uint32_t);
 extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
 			      char *, int);
-extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
-				char *, int);
+extern int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *,
+				char *, int)
+		__must_hold(&conn->session->back_lock);

[Severity: Low]
Does the function definition in drivers/scsi/libiscsi.c also need this annotation?

The __must_hold() attribute was added to the declaration here in the header, but it seems omitted from the definition:

int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, int datalen)

{
    ...

While some static analyzers inherit attributes from the header, ensuring locking annotations on declarations mirror their definitions provides local consistency across translation units.

[Severity: High]
This isn't a bug introduced by this patch, but is there a potential softirq deadlock when acquiring frwd_lock during pdu completion?

The generic API iscsi_complete_pdu() uses spin_lock() to acquire the back_lock, which leaves softirqs enabled. Inside __iscsi_complete_pdu(), processing an ISCSI_OP_NOOP_IN temporarily drops back_lock and acquires frwd_lock using spin_lock():

__iscsi_complete_pdu()

    spin_unlock(&session->back_lock);
    spin_lock(&session->frwd_lock);
    iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);

When called from a process context, such as a workqueue handler, softirqs remain enabled:

qla4xxx_task_work()
-> iscsi_complete_pdu()

     spin_lock(&conn->session->back_lock);

If a timer softirq interrupts this process and attempts to acquire frwd_lock, it could spin forever waiting for the interrupted process to release it.

[Severity: High]
This is a pre-existing issue, but could this back_lock usage lead to a hardirq deadlock with the qla4xxx driver?

The libiscsi core routinely acquires back_lock using spin_lock_bh() or spin_lock() in functions like fail_scsi_tasks(), leaving hardware interrupts enabled:

fail_scsi_tasks()

    spin_lock_bh(&session->back_lock);

However, the qla4xxx driver acquires back_lock from a hardware interrupt context:

qla4_83xx_interrupt_service_routine()
-> qla4xxx_process_response_queue()

    -> qla4xxx_passthru_status_entry()
       spin_lock(&conn->session->back_lock);

If a hardware interrupt fires on a CPU currently executing a libiscsi core function that is holding back_lock, the interrupt handler will attempt to acquire the lock and cause a hard deadlock.

Quoted 3 lines
include/scsi/libiscsi.h +0 -0
 extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
 extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
 extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=35
author reviewer
#53 lore raw

PATCH v3 36/57 scsi: libsas: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Since Clang requires that lock context annotations only refer to variables that are visible, modify a __must_hold() annotation.

---
 drivers/scsi/libsas/sas_ata.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/libsas/sas_ata.c +1 -1
index 61368e55bf86..2340790c9f6b 100644
@@ -153,7 +153,7 @@ static void sas_ata_task_done(struct sas_task *task)
 }
 
 static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
-	__must_hold(ap->lock)
+	__must_hold(qc->ap->lock)
 {
 	struct sas_task *task;
 	struct scatterlist *sg;
author reviewer
#54 lore raw

PATCH v3 37/57 scsi: libsas: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/libsas/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/libsas/Makefile +2 -0
index 9dc32736cf21..6f6f1336b438 100644
@@ -6,6 +6,8 @@
 # Copyright (C) 2005 Luben Tuikov <[email protected]>
 #
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o
 libsas-y +=  sas_init.o     \
 		sas_phy.o      \
author reviewer
#55 lore raw

PATCH v3 38/57 scsi: lpfc: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Annotate the functions that perform conditional locking with __context_unsafe().

---
 drivers/scsi/lpfc/lpfc_els.c       | 2 ++
 drivers/scsi/lpfc/lpfc_nportdisc.c | 1 +
 drivers/scsi/lpfc/lpfc_scsi.c      | 1 +
 drivers/scsi/lpfc/lpfc_sli.c       | 2 ++
 4 files changed, 6 insertions(+)
drivers/scsi/lpfc/lpfc_els.c +2 -0
index 52fc5058976d..45e7f147b163 100644
@@ -9597,6 +9597,7 @@ lpfc_els_timeout(struct timer_list *t)
  **/
 void
 lpfc_els_timeout_handler(struct lpfc_vport *vport)
+	__context_unsafe(conditional locking)
 {
 	struct lpfc_hba  *phba = vport->phba;
 	struct lpfc_sli_ring *pring;
@@ -9719,6 +9720,7 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
  **/
 void
 lpfc_els_flush_cmd(struct lpfc_vport *vport)
+	__context_unsafe(conditional locking)
 {
 	LIST_HEAD(abort_list);
 	LIST_HEAD(cancel_list);
drivers/scsi/lpfc/lpfc_nportdisc.c +1 -0
index 9c449055a55e..79e5e876c879 100644
@@ -220,6 +220,7 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
  */
 void
 lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+	__context_unsafe(conditional locking)
 {
 	LIST_HEAD(abort_list);
 	LIST_HEAD(drv_cmpl_list);
drivers/scsi/lpfc/lpfc_scsi.c +1 -0
index f2cab134af7f..6c3fd3c33c53 100644
@@ -5520,6 +5520,7 @@ void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport)
  **/
 static int
 lpfc_abort_handler(struct scsi_cmnd *cmnd)
+	__context_unsafe(conditional locking)
 {
 	struct Scsi_Host  *shost = cmnd->device->host;
 	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
drivers/scsi/lpfc/lpfc_sli.c +2 -0
index 62a30a92b792..c76d62e8907c 100644
@@ -3721,6 +3721,7 @@ lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba,
 static int
 lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 			  struct lpfc_iocbq *saveq)
+	__context_unsafe(conditional locking)
 {
 	struct lpfc_iocbq *cmdiocbp;
 	unsigned long iflag;
@@ -12871,6 +12872,7 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, u16 tgt_id, u64 lun_id,
 int
 lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
 			uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd)
+	__context_unsafe(conditional locking)
 {
 	struct lpfc_hba *phba = vport->phba;
 	struct lpfc_io_buf *lpfc_cmd;
#56 lore raw

Re: [PATCH v3 38/57] scsi: lpfc: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [High] An AB-BA lock inversion exists between lpfc_cmd->buf_lock and phba->hbalock, which can cause a hard deadlock.
- [High] Race condition in timer callback lpfc_els_timeout_handler leading to use-after-free on module unload.
- [High] List corruption due to missing lock protection for SLI ring lists in lpfc_els_abort and lpfc_els_flush_cmd.
- [High] Concurrent FCP abort requests will hang for the full timeout duration due to unshared wait queues. --

--- Patch [38]: [PATCH v3 38/57] scsi: lpfc: Prepare for enabling lock context analysis --- commit 7e811dc5ed08a4e45843970fc90d7859310944e2
Author: Bart Van Assche <[email protected]>

scsi: lpfc: Prepare for enabling lock context analysis

Annotate the functions that perform conditional locking with __context_unsafe().

drivers/scsi/lpfc/lpfc_els.c lpfc_els_timeout(struct timer_list *t) · line 9602

Quoted 11 lines
drivers/scsi/lpfc/lpfc_els.c +1 -0
index 52fc5058976d9..45e7f147b1636 100644
@@ -9597,6 +9597,7 @@ lpfc_els_timeout(struct timer_list *t)
  **/
 void
 lpfc_els_timeout_handler(struct lpfc_vport *vport)
+	__context_unsafe(conditional locking)
 {
 	struct lpfc_hba  *phba = vport->phba;

[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition here during module unload?

Looking at the bottom of lpfc_els_timeout_handler(), the code checks for FC_UNLOADING before calling mod_timer():

    if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
        mod_timer(&vport->els_tmofunc, ...);

If the teardown thread sets the FC_UNLOADING flag immediately after this check is evaluated, the callback will re-arm the timer. Even if timer_delete_sync() is used during teardown, the callback re-arms the timer before exiting, which can cause the timer to fire on freed memory and lead to a use-after-free crash.

drivers/scsi/lpfc/lpfc_els.c lpfc_els_timeout_handler(struct lpfc_vport *vport) · line 9725

Quoted 7 lines
drivers/scsi/lpfc/lpfc_els.c +1 -0
@@ -9719,6 +9720,7 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
  **/
 void
 lpfc_els_flush_cmd(struct lpfc_vport *vport)
+	__context_unsafe(conditional locking)
 {
 	LIST_HEAD(abort_list);

[Severity: High]
This is a pre-existing issue, but can list corruption occur here during ELS command flushes?

In lpfc_els_flush_cmd(), this code moves piocb->list from txcmplq to cancel_list:

    spin_lock_irqsave(&phba->hbalock, iflags);
    list_del_init(&piocb->dlist);
    if (mbx_tmo_err || !(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
        list_move_tail(&piocb->list, &cancel_list);

For SLI4, is holding only hbalock sufficient, or does it also require pring->ring_lock to safely modify the txcmplq, preventing kernel panics during linked list traversal?

drivers/scsi/lpfc/lpfc_nportdisc.c lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, · line 225

Quoted 11 lines
drivers/scsi/lpfc/lpfc_nportdisc.c +1 -0
index 9c449055a55e6..79e5e876c879e 100644
@@ -220,6 +220,7 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
  */
 void
 lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+	__context_unsafe(conditional locking)
 {
 	LIST_HEAD(abort_list);

[Severity: High]
This isn't a bug introduced by this patch, but does this list modification lack lock protection?

In lpfc_els_abort(), list_del_init() modifies the active txcmplq without holding any locks:

    if (retval && test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
        list_del_init(&iocb->list);
        list_add_tail(&iocb->list, &drv_cmpl_list);
    }

Could this lead to list corruption during error recovery scenarios?

drivers/scsi/lpfc/lpfc_scsi.c void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport) · line 5525

Quoted 11 lines
drivers/scsi/lpfc/lpfc_scsi.c +1 -0
index f2cab134af7f1..6c3fd3c33c535 100644
@@ -5520,6 +5520,7 @@ void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport)
  **/
 static int
 lpfc_abort_handler(struct scsi_cmnd *cmnd)
+	__context_unsafe(conditional locking)
 {
 	struct Scsi_Host  *shost = cmnd->device->host;

[Severity: High]
This is a pre-existing issue, but does this locking order create an AB-BA deadlock?

In lpfc_abort_handler(), lpfc_cmd->buf_lock is acquired followed by phba->hbalock:

    spin_lock_irqsave(&lpfc_cmd->buf_lock, flags);
    spin_lock(&phba->hbalock);

In lpfc_sli_abort_taskmgmt(), the thread acquires phba->hbalock first, iterates over commands, and attempts to acquire lpfc_cmd->buf_lock. Could concurrent SCSI aborts and device resets freeze storage operations?

[Severity: High]
This is also a pre-existing issue, but can concurrent FCP abort requests hang here?

In lpfc_abort_handler(), if a second thread attempts to abort the same command, it detects LPFC_DRIVER_ABORTED and jumps to wait_for_cmpl:

wait_for_cmpl:

    wait_event_timeout(waitq, (lpfc_cmd->pCmd != cmnd),
                       secs_to_jiffies(2*vport->cfg_devloss_tmo));

It then sleeps on its private, on-stack waitq. Because the completion handler only knows about the first thread's wait queue, won't the second thread block unconditionally for the full duration of the timeout?

Quoted 4 lines
drivers/scsi/lpfc/lpfc_sli.c +0 -0
index 62a30a92b7927..c76d62e8907c1 100644

[ ... ]

drivers/scsi/lpfc/lpfc_sli.c lpfc_sli_abort_iocb(struct lpfc_vport *vport, u16 tgt_id, u64 lun_id, · line 12877

Quoted 7 lines
drivers/scsi/lpfc/lpfc_sli.c +1 -0
@@ -12871,6 +12872,7 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, u16 tgt_id, u64 lun_id,
 int
 lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
 			uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd)
+	__context_unsafe(conditional locking)
 {
 	struct lpfc_hba *phba = vport->phba;

[Severity: High]
This isn't a bug introduced by this patch, but as mentioned above, does this lock ordering conflict with lpfc_abort_handler()?

In lpfc_sli_abort_taskmgmt(), phba->hbalock is acquired before lpfc_cmd->buf_lock:

    spin_lock_irqsave(&phba->hbalock, iflags);
    ...
    lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
    spin_lock(&lpfc_cmd->buf_lock);

This is the reverse of the order in lpfc_abort_handler(). Can this lead to a hard deadlock during concurrent I/O timeouts?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=38
author reviewer
#57 lore raw

PATCH v3 39/57 scsi: megaraid_sas: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/megaraid/megaraid_sas.h        |  9 ++++++---
 drivers/scsi/megaraid/megaraid_sas_base.c   | 15 ++++++++++++---
 drivers/scsi/megaraid/megaraid_sas_fusion.c |  2 ++
 3 files changed, 20 insertions(+), 6 deletions(-)
drivers/scsi/megaraid/megaraid_sas.h +6 -3
index 8ee2bfe47571..67713173793a 100644
@@ -2696,15 +2696,18 @@ __le16 get_updated_dev_handle(struct megasas_instance *instance,
 			      struct MR_DRV_RAID_MAP_ALL *drv_map);
 void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
 	struct LD_LOAD_BALANCE_INFO *lbInfo);
-int megasas_get_ctrl_info(struct megasas_instance *instance);
+int megasas_get_ctrl_info(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex);
 /* PD sequence */
 int
 megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend);
 void megasas_set_dynamic_target_properties(struct scsi_device *sdev,
 		struct queue_limits *lim, bool is_target_prop);
 int megasas_get_target_prop(struct megasas_instance *instance,
-			    struct scsi_device *sdev);
-void megasas_get_snapdump_properties(struct megasas_instance *instance);
+			    struct scsi_device *sdev)
+	__must_hold(&instance->reset_mutex);
+void megasas_get_snapdump_properties(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex);
 
 int megasas_set_crash_dump_params(struct megasas_instance *instance,
 	u8 crash_buf_state);
drivers/scsi/megaraid/megaraid_sas_base.c +12 -3
index ecd365d78ae3..ccefe5841a17 100644
@@ -132,14 +132,16 @@ MODULE_AUTHOR("[email protected]");
 MODULE_DESCRIPTION("Broadcom MegaRAID SAS Driver");
 
 int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
-static int megasas_get_pd_list(struct megasas_instance *instance);
+static int megasas_get_pd_list(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex);
 static int megasas_ld_list_query(struct megasas_instance *instance,
 				 u8 query_type);
 static int megasas_issue_init_mfi(struct megasas_instance *instance);
 static int megasas_register_aen(struct megasas_instance *instance,
 				u32 seq_num, u32 class_locale_word);
 static void megasas_get_pd_info(struct megasas_instance *instance,
-				struct scsi_device *sdev);
+				struct scsi_device *sdev)
+	__must_hold(&instance->reset_mutex);
 static void
 megasas_set_ld_removed_by_fw(struct megasas_instance *instance);
 
@@ -229,7 +231,8 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
 		       struct megasas_register_set __iomem *reg_set);
 static irqreturn_t megasas_isr(int irq, void *devp);
 static u32
-megasas_init_adapter_mfi(struct megasas_instance *instance);
+megasas_init_adapter_mfi(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex);
 u32
 megasas_build_and_issue_cmd(struct megasas_instance *instance,
 			    struct scsi_cmnd *scmd);
@@ -4754,6 +4757,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
  */
 static int
 megasas_get_ld_list(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex)
 {
 	int ret = 0, ld_index = 0, ids = 0;
 	struct megasas_cmd *cmd;
@@ -4871,6 +4875,7 @@ megasas_get_ld_list(struct megasas_instance *instance)
  */
 static int
 megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
+	__must_hold(&instance->reset_mutex)
 {
 	int ret = 0, ld_index = 0, ids = 0;
 	struct megasas_cmd *cmd;
@@ -4993,6 +4998,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
 static int
 megasas_host_device_list_query(struct megasas_instance *instance,
 			       bool is_probe)
+	__must_hold(&instance->reset_mutex)
 {
 	int ret, i, target_id;
 	struct megasas_cmd *cmd;
@@ -5874,6 +5880,7 @@ static void megasas_setup_reply_map(struct megasas_instance *instance)
  */
 static
 int megasas_get_device_list(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex)
 {
 	if (instance->enable_fw_dev_list) {
 		if (megasas_host_device_list_query(instance, true))
@@ -7789,6 +7796,7 @@ megasas_suspend(struct device *dev)
  */
 static int __maybe_unused
 megasas_resume(struct device *dev)
+	__must_hold(&((struct megasas_instance *)dev_get_drvdata(dev))->reset_mutex)
 {
 	int rval;
 	struct Scsi_Host *host;
@@ -8765,6 +8773,7 @@ static inline void megasas_remove_scsi_device(struct scsi_device *sdev)
 static
 int megasas_update_device_list(struct megasas_instance *instance,
 			       int event_type)
+	__must_hold(&instance->reset_mutex)
 {
 	int dcmd_ret;
 
drivers/scsi/megaraid/megaraid_sas_fusion.c +2 -0
index 056cbe50e19e..a55094a31c82 100644
@@ -1792,6 +1792,7 @@ static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance)
  */
 static u32
 megasas_init_adapter_fusion(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex)
 {
 	struct fusion_context *fusion;
 	u32 scratch_pad_1;
@@ -4539,6 +4540,7 @@ static int
 megasas_issue_tm(struct megasas_instance *instance, u16 device_handle,
 	uint channel, uint id, u16 smid_task, u8 type,
 	struct MR_PRIV_DEVICE *mr_device_priv_data)
+	__must_hold(&instance->reset_mutex)
 {
 	struct MR_TASK_MANAGE_REQUEST *mr_request;
 	struct MPI2_SCSI_TASK_MANAGE_REQUEST *mpi_request;
#58 lore raw

Re: [PATCH v3 39/57] scsi: megaraid_sas: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Incorrect `__must_hold` annotations misrepresent API contracts and mask missing lock…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incorrect __must_hold annotations misrepresent API contracts and mask missing lock acquisitions --

--- Patch [39]: [PATCH v3 39/57] scsi: megaraid_sas: Prepare for enabling lock context analysis --- commit e0b92da41c18eebd6dc6b3dd8f685d565a0c3f5f
Author: Bart Van Assche <[email protected]>

scsi: megaraid_sas: Prepare for enabling lock context analysis

This commit prepares the megaraid_sas driver for lock context analysis by documenting locking requirements with __must_hold().

drivers/scsi/megaraid/megaraid_sas.h __le16 get_updated_dev_handle(struct megasas_instance *instance, · line 2700

Quoted 11 lines
drivers/scsi/megaraid/megaraid_sas.h +2 -1
index 8ee2bfe475715..67713173793a3 100644
@@ -2696,15 +2696,18 @@ __le16 get_updated_dev_handle(struct megasas_instance *instance,
 			      struct MR_DRV_RAID_MAP_ALL *drv_map);
 void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
 	struct LD_LOAD_BALANCE_INFO *lbInfo);
-int megasas_get_ctrl_info(struct megasas_instance *instance);
+int megasas_get_ctrl_info(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex);

[Severity: Medium]
Is this annotation accurate for all callers?

megasas_get_ctrl_info() is called by un-annotated and unprotected paths like megasas_init_fw(). Adding __must_hold() here implies the caller is responsible for holding the lock, but the caller doesn't currently acquire it. Will this cause false positive warnings when lock context analysis is enabled?

[ ... ]

Quoted 4 lines
drivers/scsi/megaraid/megaraid_sas_base.c +0 -0
index ecd365d78ae3c..ccefe5841a17b 100644

[ ... ]

drivers/scsi/megaraid/megaraid_sas_base.c megasas_suspend(struct device *dev) · line 7802

Quoted 8 lines
drivers/scsi/megaraid/megaraid_sas_base.c +1 -0
@@ -7789,6 +7796,7 @@ megasas_suspend(struct device *dev)
  */
 static int __maybe_unused
 megasas_resume(struct device *dev)
+	__must_hold(&((struct megasas_instance *)dev_get_drvdata(dev))->reset_mutex)
 {
 	int rval;
 	struct Scsi_Host *host;

[Severity: Medium]
Does the generic PM core actually hold this driver-private mutex when invoking the resume callback?

Since megasas_resume() is used as a device power management callback, its callers outside the driver do not know about or hold reset_mutex. Will this annotation lead to static analysis errors on the PM core invocation path?

[ ... ]

drivers/scsi/megaraid/megaraid_sas_fusion.c static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance) · line 1798

Quoted 12 lines
drivers/scsi/megaraid/megaraid_sas_fusion.c +1 -0
index 056cbe50e19ed..a55094a31c829 100644
@@ -1792,6 +1792,7 @@ static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance)
  */
 static u32
 megasas_init_adapter_fusion(struct megasas_instance *instance)
+	__must_hold(&instance->reset_mutex)
 {
 	struct fusion_context *fusion;
 	u32 scratch_pad_1;

[Severity: Medium]
Will this annotation trigger analysis errors during adapter initialization?

megasas_init_adapter_fusion() is invoked via the init_adapter function pointer in megasas_init_fw(). The caller does not appear to hold reset_mutex before invoking the pointer. Should the lock acquisition be added to the callers, or is this annotation overly strict here?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=39
author reviewer
#59 lore raw

PATCH v3 40/57 scsi: megaraid: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/megaraid/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/megaraid/Makefile +3 -0
index 12177e4cae65..9a6976a4dc22 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_MEGARAID_MM)	+= megaraid_mm.o
 obj-$(CONFIG_MEGARAID_MAILBOX)	+= megaraid_mbox.o
 obj-$(CONFIG_MEGARAID_SAS)	+= megaraid_sas.o
#60 lore raw

Re: [PATCH v3 40/57] scsi: megaraid: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Incorrect `__must_hold` annotations hide lockless execution paths for `reset_mutex` and defeat…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incorrect __must_hold annotations hide lockless execution paths for reset_mutex and defeat lock context analysis. --

--- Patch [40]: [PATCH v3 40/57] scsi: megaraid: Enable lock context analysis --- commit 40d3b019568a4b229e7033f10c3257b3f094d748
Author: Bart Van Assche <[email protected]>

scsi: megaraid: Enable lock context analysis

This commit enables lock context analysis for the megaraid driver by setting CONTEXT_ANALYSIS to y in the Makefile.

drivers/scsi/megaraid/Makefile line 4

Quoted 9 lines
drivers/scsi/megaraid/Makefile +3 -0
index 12177e4cae653..9a6976a4dc22b 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+

[Severity: Medium]
Will enabling context analysis here be effective given the current annotations on the driver's lifecycle functions?

The megasas_resume() function is annotated as holding the lock:

drivers/scsi/megaraid/megaraid_sas_base.c:
static int __maybe_unused
megasas_resume(struct device *dev)
__must_hold(&((struct megasas_instance *)dev_get_drvdata(dev))->reset_mutex)

However, the PM core invokes this resume callback without holding reset_mutex.

Similarly, during driver probe, megasas_init_adapter_mfi() and megasas_init_adapter_fusion() are called via the init_adapter function pointer without reset_mutex held, but they are also annotated as requiring it:

drivers/scsi/megaraid/megaraid_sas_fusion.c:
static u32
megasas_init_adapter_fusion(struct megasas_instance *instance)
__must_hold(&instance->reset_mutex)

Could these __must_hold annotations suppress valid static analysis warnings for these execution paths, defeating the lock context analysis being enabled here?

Quoted 1 line
drivers/scsi/megaraid/Makefile +0 -0
 obj-$(CONFIG_MEGARAID_MM)	+= megaraid_mm.o

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=40
author reviewer
#61 lore raw

PATCH v3 41/57 scsi: mpt3sas: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/mpt3sas/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/mpt3sas/Makefile +3 -0
index 18e2d87eb4a2..c698a106f3e3 100644
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # mpt3sas makefile
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o
 mpt3sas-y +=  mpt3sas_base.o     \
 		mpt3sas_config.o \
author reviewer
#62 lore raw

PATCH v3 42/57 scsi: mvsas: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold(). Annotate functions that perform conditional locking with __no_context_analysis.

---
 drivers/scsi/mvsas/Makefile | 2 ++
 drivers/scsi/mvsas/mv_sas.c | 7 +++++++
 2 files changed, 9 insertions(+)
drivers/scsi/mvsas/Makefile +2 -0
index 75849258e898..7f45cca38127 100644
@@ -7,6 +7,8 @@
 # Copyright 2009-2011 Marvell. <[email protected]>
 #
 
+CONTEXT_ANALYSIS := y
+
 ccflags-$(CONFIG_SCSI_MVSAS_DEBUG) := -DMV_DEBUG
 
 obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
drivers/scsi/mvsas/mv_sas.c +7 -0
index 359226e80eae..f63e0dc1abd2 100644
@@ -1055,6 +1055,7 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
 }
 
 static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
+	__context_unsafe(conditional locking)
 {
 	struct sas_ha_struct *sas_ha = sas_phy->ha;
 	struct mvs_info *mvi = NULL; int i = 0, hi;
@@ -1153,6 +1154,7 @@ static void mvs_free_dev(struct mvs_device *mvi_dev)
 }
 
 static int mvs_dev_found_notify(struct domain_device *dev, int lock)
+	__context_unsafe(conditional locking)
 {
 	unsigned long flags = 0;
 	int res = 0;
@@ -1517,6 +1519,7 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
 }
 
 int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
+	__must_hold(&mvi->lock)
 {
 	u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
 	struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
@@ -1644,6 +1647,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
 
 void mvs_do_release_task(struct mvs_info *mvi,
 		int phy_no, struct domain_device *dev)
+	__must_hold(&mvi->lock)
 {
 	u32 slot_idx;
 	struct mvs_phy *phy;
@@ -1677,6 +1681,7 @@ void mvs_do_release_task(struct mvs_info *mvi,
 
 void mvs_release_task(struct mvs_info *mvi,
 		      struct domain_device *dev)
+	__must_hold(&mvi->lock)
 {
 	int i, phyno[WIDE_PORT_MAX_PHY], num;
 	num = mvs_find_dev_phyno(dev, phyno);
@@ -1769,6 +1774,7 @@ static void mvs_sig_time_out(struct timer_list *t)
 }
 
 void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
+	__must_hold(&mvi->lock)
 {
 	u32 tmp;
 	struct mvs_phy *phy = &mvi->phy[phy_no];
@@ -1862,6 +1868,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
 }
 
 int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
+	__must_hold(&mvi->lock)
 {
 	u32 rx_prod_idx, rx_desc;
 	bool attn = false;
#63 lore raw

Re: [PATCH v3 42/57] scsi: mvsas: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Dropping the spinlock inside a list_for_each_entry_safe loop in mvs_do_release_task allows list modification, causing an infinite loop.
- [Critical] Dropping the spinlock inside mvs_int_rx allows concurrent execution, causing double-completion of tasks and potential Use-After-Free.
- [High] The function mvs_port_notify_deformed fails to acquire mvi->lock and was missed by the new locking annotations, causing a lock imbalance.
- [High] Using a global PHY index instead of a local PHY index to write to hardware registers in mvs_port_notify_formed.
--

--- Patch [42]: [PATCH v3 42/57] scsi: mvsas: Enable lock context analysis --- commit c111f35a79c7e4e1741700306a7a9dfd2b59f919
Author: Bart Van Assche <[email protected]>

scsi: mvsas: Enable lock context analysis

Document locking requirements with __must_hold(). Annotate functions that perform conditional locking with __no_context_analysis.

drivers/scsi/mvsas/mv_sas.c void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st) · line 1061

Quoted 12 lines
drivers/scsi/mvsas/mv_sas.c +1 -0
index 359226e80eae8..f63e0dc1abd23 100644
@@ -1055,6 +1055,7 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
 }
 
 static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
+	__context_unsafe(conditional locking)
 {
 	struct sas_ha_struct *sas_ha = sas_phy->ha;
 	struct mvs_info *mvi = NULL; int i = 0, hi;

[Severity: High]
This is a pre-existing issue, but does mvs_port_notify_formed() write to the wrong hardware offset when multiple HBAs are present?

It uses the global index i instead of the local PHY index sas_phy->id:

drivers/scsi/mvsas/mv_sas.c:mvs_port_notify_formed() {
    ...
    if (phy->att_dev_info & PORT_SSP_TRGT_MASK) {
        MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
        MVS_CHIP_DISP->write_port_cfg_data(mvi, i, 0x04);
    }
    ...
}

When the global index is greater than or equal to mvi->chip->n_phy, could passing it to write_port_cfg_addr() cause out-of-bounds memory-mapped IO writes?

[ ... ]

drivers/scsi/mvsas/mv_sas.c int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) · line 1653

Quoted 8 lines
drivers/scsi/mvsas/mv_sas.c +1 -0
@@ -1644,6 +1647,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
 
 void mvs_do_release_task(struct mvs_info *mvi,
 		int phy_no, struct domain_device *dev)
+	__must_hold(&mvi->lock)
 {
 	u32 slot_idx;
 	struct mvs_phy *phy;

[Severity: Critical]
This is a pre-existing issue, but is it safe to use list_for_each_entry_safe() here if the lock is dropped inside mvs_slot_complete()?

mvs_do_release_task() loops over the port list:

drivers/scsi/mvsas/mv_sas.c:mvs_do_release_task() {
    ...
    list_for_each_entry_safe(slot, slot2, &port->list, entry) {
        ...
        mvs_slot_complete(mvi, slot_idx, 1);
    }
    ...
}

When mvs_slot_complete() temporarily releases mvi->lock, a concurrent interrupt could complete the cached next element (slot2) and remove it from the list.

Would this leave slot2->entry.next pointing to itself, causing mvs_do_release_task() to spin forever in an infinite loop when it resumes?

[Severity: High]
Also a pre-existing issue, but with the new __must_hold annotation here, what happens when this is called from mvs_port_notify_deformed()?

mvs_port_notify_deformed() ignores its lock parameter and directly calls mvs_do_release_task() without holding mvi->lock:

drivers/scsi/mvsas/mv_sas.c:mvs_port_notify_deformed() {
    ...
    list_for_each_entry(dev, &port->dev_list, dev_list_node)
        mvs_do_release_task(phy->mvi, phy_no, dev);
    ...
}

Will this cause a lock imbalance when the downstream mvs_slot_complete() call unconditionally executes spin_unlock(&mvi->lock)?

[ ... ]

drivers/scsi/mvsas/mv_sas.c void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) · line 1874

Quoted 8 lines
drivers/scsi/mvsas/mv_sas.c +1 -0
@@ -1862,6 +1868,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
 }
 
 int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
+	__must_hold(&mvi->lock)
 {
 	u32 rx_prod_idx, rx_desc;
 	bool attn = false;

[Severity: Critical]
This is a pre-existing issue, but does dropping the lock inside mvs_int_rx() expose mvi->rx_cons to concurrent modification?

mvs_int_rx() loops while evaluating the shared mvi->rx_cons:

drivers/scsi/mvsas/mv_sas.c:mvs_int_rx() {
    ...
    while (mvi->rx_cons != rx_prod_idx) {
        ...
        if (likely(rx_desc & RXQ_DONE))
            mvs_slot_complete(mvi, rx_desc, 0);
        ...
    }
    ...
}

Inside the loop, mvs_slot_complete() drops mvi->lock. If a concurrent interrupt on another CPU acquires the lock, processes the ring, and frees the tasks, could the first CPU wake up, read old descriptors based on the updated mvi->rx_cons, and erroneously complete new reused tasks?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=42
author reviewer
#64 lore raw

PATCH v3 43/57 scsi: pcmcia: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/pcmcia/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/pcmcia/Makefile +2 -0
index 02f5b44a2685..06ea2bfcd42a 100644
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
+CONTEXT_ANALYSIS := y
+
 ccflags-y		:= -I $(srctree)/drivers/scsi
 
 # 16-bit client drivers
author reviewer
#65 lore raw

PATCH v3 44/57 scsi: pm8001: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/pm8001/Makefile     | 1 +
 drivers/scsi/pm8001/pm80xx_hwi.c | 2 ++
 2 files changed, 3 insertions(+)
drivers/scsi/pm8001/Makefile +1 -0
index bbb51b7312f1..b236359810fe 100644
@@ -4,6 +4,7 @@
 #
 # Copyright (C) 2008-2009  USI Co., Ltd.
 
+CONTEXT_ANALYSIS := y
 
 obj-$(CONFIG_SCSI_PM8001) += pm80xx.o
 
drivers/scsi/pm8001/pm80xx_hwi.c +2 -0
index 7a348f0194bd..ff6fdb761a70 100644
@@ -2294,6 +2294,7 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
 static void
 mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
 		struct outbound_queue_table *circularQ, void *piomb)
+	__must_hold(&circularQ->oq_lock)
 {
 	struct sas_task *t;
 	struct pm8001_ccb_info *ccb;
@@ -3858,6 +3859,7 @@ static int ssp_coalesced_comp_resp(struct pm8001_hba_info *pm8001_ha,
  */
 static void process_one_iomb(struct pm8001_hba_info *pm8001_ha,
 		struct outbound_queue_table *circularQ, void *piomb)
+	__must_hold(&circularQ->oq_lock)
 {
 	__le32 pHeader = *(__le32 *)piomb;
 	u32 opc = (u32)((le32_to_cpu(pHeader)) & 0xFFF);
#66 lore raw

Re: [PATCH v3 44/57] scsi: pm8001: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Early returns in mpi_sata_completion for internal tasks bypass the SAS_TASK_STATE_ABORTED check, leading to a Use-After-Free if the task was concurrently aborted. --

--- Patch [44]: [PATCH v3 44/57] scsi: pm8001: Enable lock context analysis --- commit 33ee7fcaec7784e599b990132a4a4b4063d0bddb
Author: Bart Van Assche <[email protected]>

scsi: pm8001: Enable lock context analysis

This commit enables lock context analysis for the pm8001 driver. It also documents locking requirements with __must_hold() for mpi_sata_completion() and process_one_iomb().

drivers/scsi/pm8001/pm80xx_hwi.c static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb) · line 2300

Quoted 12 lines
drivers/scsi/pm8001/pm80xx_hwi.c +1 -0
index 2c0fa7ab33d22..11f6fa5c9771e 100644
@@ -2294,6 +2294,7 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
 static void
 mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
 		struct outbound_queue_table *circularQ, void *piomb)
+	__must_hold(&circularQ->oq_lock)
 {
 	struct sas_task *t;
 	struct pm8001_ccb_info *ccb;

[Severity: High]
This is a pre-existing issue, but does this function have a use-after-free if an internal task is concurrently aborted?

If a SATA internal task (where !t->uldd_task) times out, the libsas EH handler can set SAS_TASK_STATE_ABORTED under the task_state_lock, complete the task, and free it.

If the device concurrently completes the task with an error, mpi_sata_completion() checks !t->uldd_task and returns early after calling pm8001_ccb_task_free_done().

This early return bypasses the task_state_lock synchronization and the check for SAS_TASK_STATE_ABORTED at the end of the function, which blindly executes task->task_done(task) on a potentially freed task.

This issue also appears to exist in mpi_sata_completion() in drivers/scsi/pm8001/pm8001_hwi.c.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=44
maintainer Acked-by on v3 44/57
#67 lore raw

Re: [PATCH v3 44/57] scsi: pm8001: Enable lock context analysis

In reply to Bart Van Assche
Quoted 4 lines · Bart Van Assche

Document locking requirements with __must_hold().

Quoted 37 lines
---
 drivers/scsi/pm8001/Makefile     | 1 +
 drivers/scsi/pm8001/pm80xx_hwi.c | 2 ++
 2 files changed, 3 insertions(+)
drivers/scsi/pm8001/Makefile +1 -0
index bbb51b7312f1..b236359810fe 100644
@@ -4,6 +4,7 @@
 #
 # Copyright (C) 2008-2009  USI Co., Ltd.
 
+CONTEXT_ANALYSIS := y
 
 obj-$(CONFIG_SCSI_PM8001) += pm80xx.o
 
drivers/scsi/pm8001/pm80xx_hwi.c +2 -0
index 7a348f0194bd..ff6fdb761a70 100644
@@ -2294,6 +2294,7 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
 static void
 mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
                struct outbound_queue_table *circularQ, void *piomb)
+       __must_hold(&circularQ->oq_lock)
 {
        struct sas_task *t;
        struct pm8001_ccb_info *ccb;
@@ -3858,6 +3859,7 @@ static int ssp_coalesced_comp_resp(struct pm8001_hba_info *pm8001_ha,
  */
 static void process_one_iomb(struct pm8001_hba_info *pm8001_ha,
                struct outbound_queue_table *circularQ, void *piomb)
+       __must_hold(&circularQ->oq_lock)
 {
        __le32 pHeader = *(__le32 *)piomb;
        u32 opc = (u32)((le32_to_cpu(pHeader)) & 0xFFF);
author reviewer
#68 lore raw

PATCH v3 45/57 scsi: qedf: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/qedf/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/qedf/Makefile +3 -0
index c46287826fb8..465982ba5dae 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_QEDF) := qedf.o
 qedf-y = qedf_dbg.o qedf_main.o qedf_io.o qedf_fip.o \
 	 qedf_attr.o qedf_els.o drv_scsi_fw_funcs.o drv_fcoe_fw_funcs.o
author reviewer
#69 lore raw

PATCH v3 46/57 scsi: qedi: Enable lock context analysis

In reply to Bart Van Assche

Annotate qedi_cleanup_all_io() with __no_context_analysis since it performs conditional locking.

---
 drivers/scsi/qedi/Makefile  | 3 +++
 drivers/scsi/qedi/qedi_fw.c | 1 +
 2 files changed, 4 insertions(+)
drivers/scsi/qedi/Makefile +3 -0
index d84eedfd031b..f081d047b6eb 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_QEDI) := qedi.o
 qedi-y := qedi_main.o qedi_iscsi.o qedi_fw.o qedi_sysfs.o \
 	    qedi_dbg.o qedi_fw_api.o
drivers/scsi/qedi/qedi_fw.c +1 -0
index 854efa4f61d8..e162061ca49c 100644
@@ -1134,6 +1134,7 @@ int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn,
 
 int qedi_cleanup_all_io(struct qedi_ctx *qedi, struct qedi_conn *qedi_conn,
 			struct iscsi_task *task, bool in_recovery)
+	__context_unsafe(conditional locking)
 {
 	int rval;
 	struct iscsi_task *ctask;
#70 lore raw

Re: [PATCH v3 46/57] scsi: qedi: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Deadlock vulnerability due to acquiring session->back_lock without disabling softirqs in CQE processing paths. --

--- Patch [46]: [PATCH v3 46/57] scsi: qedi: Enable lock context analysis --- commit c592a3970ebc316b3aad74beecefeffd2a24b31b
Author: Bart Van Assche <[email protected]>

scsi: qedi: Enable lock context analysis

Annotate qedi_cleanup_all_io() with __no_context_analysis since it performs conditional locking.

[ ... ]

drivers/scsi/qedi/qedi_fw.c int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn, · line 1140

Quoted 12 lines
drivers/scsi/qedi/qedi_fw.c +1 -0
index 854efa4f61d87..e162061ca49c9 100644
@@ -1134,6 +1134,7 @@ int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn,
 
 int qedi_cleanup_all_io(struct qedi_ctx *qedi, struct qedi_conn *qedi_conn,
 			struct iscsi_task *task, bool in_recovery)
+	__context_unsafe(conditional locking)
 {
 	int rval;
 	struct iscsi_task *ctask;

[Severity: High]
This is a pre-existing issue, but could the locking elsewhere in this file lead to a deadlock?

Looking at qedi_process_tmf_resp() (and similar response handlers like qedi_process_text_resp(), qedi_process_logout_resp(), and qedi_process_login_resp()), they acquire session->back_lock without disabling softirqs:

qedi_process_tmf_resp()

    spin_lock(&session->back_lock);

These handlers execute asynchronously in a kernel thread (qedi_percpu_io_thread) with softirqs enabled.

If a timer interrupt fires on the same CPU while this lock is held, the libiscsi transport timer (iscsi_check_transport_timeouts) could preempt the thread in TIMER_SOFTIRQ context.

If that timer logic attempts to send a ping (iscsi_send_nopout()) and fails due to an allocation failure or full queue, its cleanup path calls iscsi_put_task().

iscsi_put_task() unconditionally attempts to acquire the lock using spin_lock_bh(&session->back_lock).

Would this cause a softirq-preemption deadlock where the timer spins forever waiting for the preempted thread to release session->back_lock? Should these CQE processing paths use spin_lock_bh() instead?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=46
author reviewer
#71 lore raw

PATCH v3 47/57 scsi: qla1280: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/qla1280.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
drivers/scsi/qla1280.c +18 -0
index cdd6fe002c32..ebc878068684 100644
@@ -737,6 +737,7 @@ static void qla1280_mailbox_timeout(struct timer_list *t)
 static int
 _qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp,
 				 struct completion *wait)
+	__must_hold(ha->host->host_lock)
 {
 	int	status = FAILED;
 	struct scsi_cmnd *cmd = sp->cmd;
@@ -754,6 +755,7 @@ _qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp,
 
 static int
 qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp)
+	__must_hold(ha->host->host_lock)
 {
 	DECLARE_COMPLETION_ONSTACK(wait);
 
@@ -763,6 +765,7 @@ qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp)
 
 static int
 qla1280_wait_for_pending_commands(struct scsi_qla_host *ha, int bus, int target)
+	__must_hold(ha->host->host_lock)
 {
 	int		cnt;
 	int		status;
@@ -809,6 +812,7 @@ qla1280_wait_for_pending_commands(struct scsi_qla_host *ha, int bus, int target)
  **************************************************************************/
 static int
 qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
+	__must_hold(cmd->device->host->host_lock)
 {
 	struct scsi_qla_host *ha;
 	int bus, target, lun;
@@ -822,6 +826,10 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
 	ENTER("qla1280_error_action");
 
 	ha = (struct scsi_qla_host *)(CMD_HOST(cmd)->hostdata);
+
+	/* Tell the compiler that ha->host == cmd->device->host. */
+	__assume_ctx_lock(ha->host->host_lock);
+
 	sp = scsi_cmd_priv(cmd);
 	bus = SCSI_BUS_32(cmd);
 	target = SCSI_TCN_32(cmd);
@@ -1490,6 +1498,7 @@ qla1280_initialize_adapter(struct scsi_qla_host *ha)
  */
 static const struct firmware *
 qla1280_request_firmware(struct scsi_qla_host *ha)
+	__must_hold(ha->host->host_lock)
 {
 	const struct firmware *fw;
 	int err;
@@ -1655,6 +1664,7 @@ qla1280_chip_diag(struct scsi_qla_host *ha)
 
 static int
 qla1280_load_firmware_pio(struct scsi_qla_host *ha)
+	__must_hold(ha->host->host_lock)
 {
 	/* enter with host_lock acquired */
 
@@ -1705,6 +1715,7 @@ qla1280_load_firmware_pio(struct scsi_qla_host *ha)
 #define DUMP_IT_BACK 0		/* for debug of RISC loading */
 static int
 qla1280_load_firmware_dma(struct scsi_qla_host *ha)
+	__must_hold(ha->host->host_lock)
 {
 	/* enter with host_lock acquired */
 	const struct firmware *fw;
@@ -1844,6 +1855,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha)
 
 static int
 qla1280_load_firmware(struct scsi_qla_host *ha)
+	__must_hold(ha->host->host_lock)
 {
 	/* enter with host_lock taken */
 	int err;
@@ -2413,6 +2425,7 @@ qla1280_nv_write(struct scsi_qla_host *ha, uint16_t data)
  */
 static int
 qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
+	__must_hold(ha->host->host_lock)
 {
 	struct device_reg __iomem *reg = ha->iobase;
 	int status = 0;
@@ -2538,6 +2551,7 @@ qla1280_poll(struct scsi_qla_host *ha)
  */
 static int
 qla1280_bus_reset(struct scsi_qla_host *ha, int bus)
+	__must_hold(ha->host->host_lock)
 {
 	uint16_t mb[MAILBOX_REGISTER_COUNT];
 	uint16_t reset_delay;
@@ -2598,6 +2612,7 @@ qla1280_bus_reset(struct scsi_qla_host *ha, int bus)
  */
 static int
 qla1280_device_reset(struct scsi_qla_host *ha, int bus, int target)
+	__must_hold(ha->host->host_lock)
 {
 	uint16_t mb[MAILBOX_REGISTER_COUNT];
 	int status;
@@ -2632,6 +2647,7 @@ qla1280_device_reset(struct scsi_qla_host *ha, int bus, int target)
  */
 static int
 qla1280_abort_command(struct scsi_qla_host *ha, struct srb * sp, int handle)
+	__must_hold(ha->host->host_lock)
 {
 	uint16_t mb[MAILBOX_REGISTER_COUNT];
 	unsigned int bus, target, lun;
@@ -3749,6 +3765,7 @@ qla1280_error_entry(struct scsi_qla_host *ha, struct response *pkt,
  */
 static int
 qla1280_abort_isp(struct scsi_qla_host *ha)
+	__must_hold(ha->host->host_lock)
 {
 	struct device_reg __iomem *reg = ha->iobase;
 	struct srb *sp;
@@ -3881,6 +3898,7 @@ qla1280_check_for_dead_scsi_bus(struct scsi_qla_host *ha, unsigned int bus)
 static void
 qla1280_get_target_parameters(struct scsi_qla_host *ha,
 			      struct scsi_device *device)
+	__must_hold(ha->host->host_lock)
 {
 	uint16_t mb[MAILBOX_REGISTER_COUNT];
 	int bus, target, lun;
#72 lore raw

Re: [PATCH v3 47/57] scsi: qla1280: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] Incomplete __must_hold annotations on intermediate functions calling qla1280_mailbox_command.

Pre-existing issues:
- [High] Lost wakeups and stack pointer leak in qla1280_poll(). --

--- Patch [47]: [PATCH v3 47/57] scsi: qla1280: Prepare for enabling lock context analysis --- commit 1c9b404ec1f5e106acf48d1ff8e578d2279ecab4
Author: Bart Van Assche <[email protected]>

scsi: qla1280: Prepare for enabling lock context analysis

Document locking requirements with __must_hold().

Quoted 4 lines
drivers/scsi/qla1280.c +0 -0
index cdd6fe002c324..ebc878068684b 100644

[ ... ]

drivers/scsi/qla1280.c qla1280_nv_write(struct scsi_qla_host *ha, uint16_t data) · line 2429

Quoted 6 lines
drivers/scsi/qla1280.c +1 -0
@@ -2413,6 +2425,7 @@ qla1280_nv_write(struct scsi_qla_host *ha, uint16_t data)
  */
 static int
 qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
+	__must_hold(ha->host->host_lock)
 {

[Severity: Medium]
Will static analysis tools emit false-positive lock context warnings for other functions that call qla1280_mailbox_command() but lack the __must_hold() annotation?

For example, intermediate functions like qla1280_set_target_parameters(), qla1280_chip_diag(), qla1280_start_firmware(), qla1280_init_rings(), and qla1280_nvram_config() call qla1280_mailbox_command() while the lock is already held, but they are missing the annotation added to other callers in this patch.

[ ... ]

drivers/scsi/qla1280.c qla1280_poll(struct scsi_qla_host *ha)

Quoted 1 line
drivers/scsi/qla1280.c +0 -0
@@ -2538,6 +2551,7 @@ qla1280_poll(struct scsi_qla_host *ha)

[Severity: High]
This is a pre-existing issue, but does qla1280_poll() leak completed SCSI commands on its stack-allocated done_q?

Looking at the qla1280_poll() implementation nearby, it allocates a local list:

qla1280_poll(struct scsi_qla_host *ha)

{
    LIST_HEAD(done_q);
    ...
    if (data & RISC_INT)
        qla1280_isr(ha, &done_q);

    if (!list_empty(&done_q))
        qla1280_done(ha);
}

If qla1280_isr() populates this local done_q with completed commands, they appear to be ignored when qla1280_done() is called, because qla1280_done() only processes ha->done_q. When qla1280_poll() returns, the stack frame is destroyed, permanently leaking these commands and leaving their list node pointers aimed at invalid stack memory.

Quoted 5 lines
drivers/scsi/qla1280.c +1 -0
  */
 static int
 qla1280_bus_reset(struct scsi_qla_host *ha, int bus)
+	__must_hold(ha->host->host_lock)
 {
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=47
author reviewer
#73 lore raw

PATCH v3 48/57 scsi: qla2xxx: Enable lock context analysis

In reply to Bart Van Assche

Make several code blocks with conditional locking compatible with thread-safety analysis. Annotate functions that perform conditional locking with __no_context_analysis. No functionality has been changed.

---
 drivers/scsi/qla2xxx/Makefile     |  3 +++
 drivers/scsi/qla2xxx/qla_nx.c     |  2 ++
 drivers/scsi/qla2xxx/qla_target.c | 29 ++++++++++++++++++-----------
 drivers/scsi/qla2xxx/qla_tmpl.c   |  1 +
 4 files changed, 24 insertions(+), 11 deletions(-)
drivers/scsi/qla2xxx/Makefile +3 -0
index cbc1303e761e..095657510cc3 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
 		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
 		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o \
drivers/scsi/qla2xxx/qla_nx.c +2 -0
index 298c060c1292..1950583c5734 100644
@@ -431,6 +431,7 @@ static int qla82xx_crb_win_lock(struct qla_hw_data *ha)
 
 int
 qla82xx_wr_32(struct qla_hw_data *ha, ulong off_in, u32 data)
+	__context_unsafe(conditional locking)
 {
 	void __iomem *off;
 	unsigned long flags = 0;
@@ -461,6 +462,7 @@ qla82xx_wr_32(struct qla_hw_data *ha, ulong off_in, u32 data)
 
 int
 qla82xx_rd_32(struct qla_hw_data *ha, ulong off_in)
+	__context_unsafe(conditional locking)
 {
 	void __iomem *off;
 	unsigned long flags = 0;
drivers/scsi/qla2xxx/qla_target.c +18 -11
index e47da45e93a0..687ffd4bfdce 100644
@@ -383,11 +383,13 @@ static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha,
 			    vha->vp_idx, entry->vp_index);
 			break;
 		}
-		if (!ha_locked)
+		if (!ha_locked) {
 			spin_lock_irqsave(&host->hw->hardware_lock, flags);
-		qlt_24xx_handle_abts(host, (struct abts_recv_from_24xx *)atio);
-		if (!ha_locked)
+			qlt_24xx_handle_abts(host, (struct abts_recv_from_24xx *)atio);
 			spin_unlock_irqrestore(&host->hw->hardware_lock, flags);
+		} else {
+			qlt_24xx_handle_abts(host, (struct abts_recv_from_24xx *)atio);
+		}
 		break;
 	}
 
@@ -3774,6 +3776,7 @@ static int __qlt_send_term_exchange(struct qla_qpair *qpair,
  */
 void qlt_send_term_exchange(struct qla_qpair *qpair,
 	struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked)
+	__context_unsafe(conditional locking)
 {
 	struct scsi_qla_host *vha;
 	unsigned long flags = 0;
@@ -6727,11 +6730,13 @@ qlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair,
 	if (ha->tgt.num_pend_cmds < Q_FULL_THRESH_HOLD(ha))
 		return 0;
 
-	if (!ha_locked)
+	if (!ha_locked) {
 		spin_lock_irqsave(&ha->hardware_lock, flags);
-	qlt_send_busy(qpair, atio, qla_sam_status);
-	if (!ha_locked)
+		qlt_send_busy(qpair, atio, qla_sam_status);
 		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	} else {
+		qlt_send_busy(qpair, atio, qla_sam_status);
+	}
 
 	return 1;
 }
@@ -6740,6 +6745,7 @@ qlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair,
 /* called via callback from qla2xxx */
 static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha,
 	struct atio_from_isp *atio, uint8_t ha_locked)
+	__context_unsafe(conditional locking)
 {
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
@@ -6766,12 +6772,13 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha,
 			    "qla_target(%d): ATIO_TYPE7 "
 			    "received with UNKNOWN exchange address, "
 			    "sending QUEUE_FULL\n", vha->vp_idx);
-			if (!ha_locked)
+			if (!ha_locked) {
 				spin_lock_irqsave(&ha->hardware_lock, flags);
-			qlt_send_busy(ha->base_qpair, atio, qla_sam_status);
-			if (!ha_locked)
-				spin_unlock_irqrestore(&ha->hardware_lock,
-				    flags);
+				qlt_send_busy(ha->base_qpair, atio, qla_sam_status);
+				spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			} else {
+				qlt_send_busy(ha->base_qpair, atio, qla_sam_status);
+			}
 			break;
 		}
 
drivers/scsi/qla2xxx/qla_tmpl.c +1 -0
index b0a74b036cf4..f31bd7aeb8dc 100644
@@ -1004,6 +1004,7 @@ qla27xx_fwdt_template_valid(void *p)
 
 void
 qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked)
+	__context_unsafe(conditional locking)
 {
 	ulong flags = 0;
 
author reviewer
#74 lore raw

PATCH v3 49/57 scsi: qla4xxx: Enable lock context analysis

In reply to Bart Van Assche

Annotate the functions that perform conditional locking with __no_context_analysis.

---
 drivers/scsi/qla4xxx/Makefile | 3 +++
 drivers/scsi/qla4xxx/ql4_nx.c | 2 ++
 2 files changed, 5 insertions(+)
drivers/scsi/qla4xxx/Makefile +3 -0
index 1f8a9096c744..2178d139f3ca 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+
+CONTEXT_ANALYSIS := y
+
 qla4xxx-y := ql4_os.o ql4_init.o ql4_mbx.o ql4_iocb.o ql4_isr.o \
 		ql4_nx.o ql4_nvram.o ql4_dbg.o ql4_attr.o ql4_bsg.o ql4_83xx.o
 
drivers/scsi/qla4xxx/ql4_nx.c +2 -0
index f7340cfc990a..513e9e95c888 100644
@@ -406,6 +406,7 @@ void qla4_82xx_crb_win_unlock(struct scsi_qla_host *ha)
 
 void
 qla4_82xx_wr_32(struct scsi_qla_host *ha, ulong off, u32 data)
+	__context_unsafe(conditional locking)
 {
 	unsigned long flags = 0;
 	int rv;
@@ -429,6 +430,7 @@ qla4_82xx_wr_32(struct scsi_qla_host *ha, ulong off, u32 data)
 }
 
 uint32_t qla4_82xx_rd_32(struct scsi_qla_host *ha, ulong off)
+	__context_unsafe(conditional locking)
 {
 	unsigned long flags = 0;
 	int rv;
#75 lore raw

Re: [PATCH v3 49/57] scsi: qla4xxx: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [High] The return value of the hardware semaphore lock acquisition qla4_82xx_crb_win_lock() is completely ignored, leading to unprotected concurrent modification of the CRB window.
- [High] A potentially 16-minute long busy-wait loop in qla4_82xx_crb_win_lock() runs with local IRQs disabled, leading to a hard lockup panic.
- [High] The driver fails to check for timeout errors when acquiring hardware semaphore locks via qla4_82xx_rom_lock() and qla4_82xx_idc_lock().
- [High] The hardware window register is modified, but the protective hardware lock is dropped before performing I/O against the window, opening a TOCTOU race condition. --

--- Patch [49]: [PATCH v3 49/57] scsi: qla4xxx: Enable lock context analysis --- commit f4615e07e950d925c6341ed2210c4fa827b136dc
Author: Bart Van Assche <[email protected]>

scsi: qla4xxx: Enable lock context analysis

This commit annotates the functions that perform conditional locking with __no_context_analysis.

drivers/scsi/qla4xxx/ql4_nx.c void qla4_82xx_crb_win_unlock(struct scsi_qla_host *ha) · line 412

Quoted 11 lines
drivers/scsi/qla4xxx/ql4_nx.c +1 -0
@@ -406,6 +406,7 @@ void qla4_82xx_crb_win_unlock(struct scsi_qla_host *ha)
 
 void
 qla4_82xx_wr_32(struct scsi_qla_host *ha, ulong off, u32 data)
+	__context_unsafe(conditional locking)
 {
 	unsigned long flags = 0;
 	int rv;

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the locking in this function, I noticed that the return value of qla4_82xx_crb_win_lock() is completely ignored:

	write_lock_irqsave(&ha->hw_lock, flags);
	qla4_82xx_crb_win_lock(ha);
	qla4_82xx_pci_set_crbwindow_2M(ha, &off);

If another PCI function holds the hardware semaphore and the lock request times out, qla4_82xx_crb_win_lock() returns -1. Does unconditionally proceeding to reprogram the shared hardware CRB window defeat the mutual exclusion mechanism and lead to uncoordinated window switching and hardware state corruption?

[Severity: High]
This is also a pre-existing issue on this same path, but is there a risk of a hard lockup panic here?

The write_lock_irqsave() call disables local interrupts just prior to entering qla4_82xx_crb_win_lock(). Inside the lock acquisition routine, a spinloop bounded by CRB_WIN_LOCK_TIMEOUT (100,000,000 iterations) calls udelay(10):

drivers/scsi/qla4xxx/ql4_nx.c:qla4_82xx_crb_win_lock() {
	...
		if (timeout >= CRB_WIN_LOCK_TIMEOUT)
			return -1;

		timeout++;
		udelay(10);
	...
}

If the hardware is unresponsive or highly contended, this massive spin loop could run with IRQs disabled for up to 1,000 seconds. Will this invariably trigger the kernel's hard lockup detector (NMI watchdog) and crash the system?

drivers/scsi/qla4xxx/ql4_nx.c qla4_82xx_wr_32(struct scsi_qla_host *ha, ulong off, u32 data) · line 436

Quoted 8 lines
drivers/scsi/qla4xxx/ql4_nx.c +1 -0
@@ -429,6 +430,7 @@ qla4_82xx_wr_32(struct scsi_qla_host *ha, ulong off, u32 data)
 }
 
 uint32_t qla4_82xx_rd_32(struct scsi_qla_host *ha, ulong off)
+	__context_unsafe(conditional locking)
 {
 	unsigned long flags = 0;
 	int rv;

[Severity: High]
This isn't a bug introduced by this patch, but while looking at the locking primitives in this file, I noticed other places where hardware lock timeouts are ignored.

For example, in qla4_82xx_pinit_from_rom():

	/* Halt all the indiviual PEGs and other blocks of the ISP */
	qla4_82xx_rom_lock(ha);

	/* disable all I2Q */
	qla4_82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x10, 0x0);

And similarly in qla4_82xx_isp_reset():

	qla4_82xx_idc_lock(ha);
	dev_state = qla4_82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);

If the lock is legitimately held by another host or PCI function and times out, does blindly proceeding to execute hardware halt and reset operations risk severe hardware state corruption?

[Severity: High]
This is another pre-existing issue in the same file's lock handling, but is there a TOCTOU race condition when the hardware lock is temporarily dropped?

In qla4_82xx_pci_mem_read_direct() and qla4_82xx_pci_mem_write_direct(), the protective hardware lock is dropped before performing I/O:

	addr = qla4_8xxx_pci_base_offsetfset(ha, start);
	if (!addr) {
		write_unlock_irqrestore(&ha->hw_lock, flags);
		mem_base = pci_resource_start(ha->pdev, 0);

While the lock is dropped, could another thread concurrently acquire ha->hw_lock and reprogram the hardware window to a completely different address? When the original thread re-acquires the lock and proceeds to execute the I/O using the PCI pointer, will the hardware device forward the access to the newly modified window, resulting in corrupted read/writes?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=49
author reviewer
#76 lore raw

PATCH v3 50/57 scsi: ufs: Enable lock context analysis

In reply to Bart Van Assche

Annotate functions that modify the state of a synchronization object. Remove the struct semaphore annotations because lock context annotations are not supported for semaphores.

---
 drivers/ufs/core/Makefile      |  2 ++
 drivers/ufs/core/ufs-debugfs.c |  8 ++++++--
 drivers/ufs/core/ufshcd.c      | 14 ++++++++++++++
 drivers/ufs/host/Makefile      |  2 ++
 4 files changed, 24 insertions(+), 2 deletions(-)
drivers/ufs/core/Makefile +2 -0
index ce7d16d2cf35..67ab9ffbdf5d 100644
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_UFSHCD)		+= ufshcd-core.o
 ufshcd-core-y				+= ufshcd.o ufs-sysfs.o ufs-mcq.o ufs-txeq.o
 ufshcd-core-$(CONFIG_RPMB)		+= ufs-rpmb.o
drivers/ufs/core/ufs-debugfs.c +6 -2
index e3dd81d6fe82..814816810388 100644
@@ -65,8 +65,10 @@ static int ee_usr_mask_get(void *data, u64 *val)
 	return 0;
 }
 
+token_context_lock(ufs_debugfs);
+
 static int ufs_debugfs_get_user_access(struct ufs_hba *hba)
-__acquires(&hba->host_sem)
+	__cond_acquires(0, ufs_debugfs)
 {
 	down(&hba->host_sem);
 	if (!ufshcd_is_user_access_allowed(hba)) {
@@ -74,14 +76,16 @@ __acquires(&hba->host_sem)
 		return -EBUSY;
 	}
 	ufshcd_rpm_get_sync(hba);
+	__acquire(ufs_debugfs);
 	return 0;
 }
 
 static void ufs_debugfs_put_user_access(struct ufs_hba *hba)
-__releases(&hba->host_sem)
+	__releases(ufs_debugfs)
 {
 	ufshcd_rpm_put_sync(hba);
 	up(&hba->host_sem);
+	__release(ufs_debugfs);
 }
 
 static int ee_usr_mask_set(void *data, u64 val)
drivers/ufs/core/ufshcd.c +14 -0
index 1a5a851d43b2..17fff36222cf 100644
@@ -1353,6 +1353,8 @@ static int ufshcd_wait_for_pending_cmds(struct ufs_hba *hba,
  * On failure, all acquired locks are released and the tagset is unquiesced.
  */
 int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us)
+	__cond_acquires(0, &hba->host->scan_mutex)
+	__cond_acquires(0, &hba->clk_scaling_lock)
 {
 	int ret = 0;
 
@@ -1377,6 +1379,8 @@ int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us)
  * This function resumes command submissions.
  */
 void ufshcd_resume_command_processing(struct ufs_hba *hba)
+	__releases(&hba->clk_scaling_lock)
+	__releases(&hba->host->scan_mutex)
 {
 	up_write(&hba->clk_scaling_lock);
 	blk_mq_unquiesce_tagset(&hba->host->tag_set);
@@ -1446,6 +1450,9 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, u32 target_gear, bool scale_up
  * Return: 0 upon success; -EBUSY upon timeout.
  */
 static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba, u64 timeout_us)
+	__cond_acquires(0, &hba->host->scan_mutex)
+	__cond_acquires(0, &hba->wb_mutex)
+	__cond_acquires(0, &hba->clk_scaling_lock)
 {
 	int ret = 0;
 	/*
@@ -1475,6 +1482,9 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba, u64 timeout_us)
 }
 
 static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err)
+	__releases(&hba->clk_scaling_lock)
+	__releases(&hba->wb_mutex)
+	__releases(&hba->host->scan_mutex)
 {
 	up_write(&hba->clk_scaling_lock);
 	mutex_unlock(&hba->wb_mutex);
@@ -3282,6 +3292,8 @@ ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 }
 
 static void ufshcd_dev_man_lock(struct ufs_hba *hba)
+	__acquires(&hba->dev_cmd.lock)
+	__acquires_shared(&hba->clk_scaling_lock)
 {
 	ufshcd_hold(hba);
 	mutex_lock(&hba->dev_cmd.lock);
@@ -3289,6 +3301,8 @@ static void ufshcd_dev_man_lock(struct ufs_hba *hba)
 }
 
 static void ufshcd_dev_man_unlock(struct ufs_hba *hba)
+	__releases_shared(&hba->clk_scaling_lock)
+	__releases(&hba->dev_cmd.lock)
 {
 	up_read(&hba->clk_scaling_lock);
 	mutex_unlock(&hba->dev_cmd.lock);
drivers/ufs/host/Makefile +2 -0
index 65d8bb23ab7b..7d8db67eb23c 100644
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o
#77 lore raw

Re: [PATCH v3 50/57] scsi: ufs: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] ufshcd_wait_for_pending_cmds() uses io_schedule_timeout() without setting current->state, causing a busy loop instead of sleeping. --

--- Patch [50]: [PATCH v3 50/57] scsi: ufs: Enable lock context analysis --- commit 8185dd4bd8104528b9c4d31b079a63ef3f632638
Author: Bart Van Assche <[email protected]>

scsi: ufs: Enable lock context analysis

This commit annotates functions that modify the state of a synchronization object to enable lock context analysis. It also removes struct semaphore annotations because lock context annotations are not supported for semaphores.

drivers/ufs/core/ufshcd.c static int ufshcd_wait_for_pending_cmds(struct ufs_hba *hba,

Quoted 5 lines
drivers/ufs/core/ufshcd.c +0 -0
index f85582287d14d..22a5f8e9af913 100644
@@ -1353,6 +1353,8 @@ static int ufshcd_wait_for_pending_cmds(struct ufs_hba *hba,

[Severity: High]
This is a pre-existing issue, but does ufshcd_wait_for_pending_cmds() busy loop here instead of sleeping?

Looking at ufshcd_wait_for_pending_cmds(), it calls io_schedule_timeout() without first setting the task state to TASK_UNINTERRUPTIBLE:

drivers/ufs/core/ufshcd.c:ufshcd_wait_for_pending_cmds() {
	...
	do {
		tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
		tr_pending = ufshcd_pending_cmds(hba);
		if (!tm_doorbell && !tr_pending) {
			timeout = false;
			break;
		} else if (do_last_check) {
			break;
		}

		io_schedule_timeout(msecs_to_jiffies(20));
		if (ktime_to_us(ktime_sub(ktime_get(), start)) >
		    wait_timeout_us) {
	...
}

Because the task state remains TASK_RUNNING, does the underlying schedule_timeout() function return immediately without yielding the CPU?

Could this cause severe performance degradation by hogging 100% of a CPU core and aggressively polling MMIO registers for up to 1 second during operations like UFS hardware clock scaling?

Quoted 7 lines
drivers/ufs/core/ufshcd.c +2 -0
  * On failure, all acquired locks are released and the tagset is unquiesced.
  */
 int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us)
+	__cond_acquires(0, &hba->host->scan_mutex)
+	__cond_acquires(0, &hba->clk_scaling_lock)
 {
 	int ret = 0;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=50
author reviewer
#78 lore raw

PATCH v3 51/57 scsi: iSCSI transport: Prepare for enabling lock context analysis

In reply to Bart Van Assche

Document locking requirements with __must_hold().

---
 drivers/scsi/scsi_transport_iscsi.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/scsi_transport_iscsi.c +1 -0
index 8aa76f813bcd..ade69c7b65b4 100644
@@ -2243,6 +2243,7 @@ static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active)
 static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn,
 					 struct iscsi_endpoint *ep,
 					 bool is_active)
+	__must_hold(&conn->ep_mutex)
 {
 	/* Check if this was a conn error and the kernel took ownership */
 	spin_lock_irq(&conn->lock);
#79 lore raw

Re: [PATCH v3 51/57] scsi: iSCSI transport: Prepare for enabling lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Dropping a lock without holding a reference allows concurrent teardown to free the connection object, leading to a Use-After-Free.
- [High] Pre-existing memory leak of iscsi_endpoint if userspace sends ISCSI_UEVENT_STOP_CONN before ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT during connection recovery. --

--- Patch [51]: [PATCH v3 51/57] scsi: iSCSI transport: Prepare for enabling lock context analysis --- commit f51c8db89420b5a402dacd38c1b68b13f9794c32
Author: Bart Van Assche <[email protected]>

scsi: iSCSI transport: Prepare for enabling lock context analysis

Document locking requirements with __must_hold().

drivers/scsi/scsi_transport_iscsi.c static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active) · line 2246

Quoted 9 lines
drivers/scsi/scsi_transport_iscsi.c +1 -0
index 8aa76f813bcdb..ade69c7b65b47 100644
@@ -2243,6 +2243,7 @@ static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active)
 static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn,
 					 struct iscsi_endpoint *ep,
 					 bool is_active)
+	__must_hold(&conn->ep_mutex)

[Severity: High]
This isn't a bug introduced by this patch, but could this lead to a memory leak of the iscsi_endpoint?

If userspace sends ISCSI_UEVENT_STOP_CONN during connection recovery, iscsi_if_stop_conn() unconditionally clears ISCSI_CLS_CONN_BIT_CLEANUP:

drivers/scsi/scsi_transport_iscsi.c:iscsi_if_stop_conn()

    ...
    clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags);
    ...

Then, when ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT is handled here in iscsi_if_disconnect_bound_ep(), the bit check fails:

drivers/scsi/scsi_transport_iscsi.c:iscsi_if_disconnect_bound_ep()

    ...
    if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
        spin_unlock_irq(&conn->lock);
        iscsi_ep_disconnect(conn, is_active);
    } else {
    ...

This bypasses the iscsi_put_endpoint() call in the else block, which appears to permanently leak the endpoint if the cleanup work previously took a reference.

[Severity: Critical]
This is a pre-existing issue, but does dropping this mutex allow a use-after-free of the connection object?

When the else block is taken in this function:

drivers/scsi/scsi_transport_iscsi.c:iscsi_if_disconnect_bound_ep()

    ...
    } else {
        spin_unlock_irq(&conn->lock);
        ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n");
        mutex_unlock(&conn->ep_mutex);

        flush_work(&conn->cleanup_work);
        /* ... */
        iscsi_put_endpoint(ep);
        mutex_lock(&conn->ep_mutex);
    }

iscsi_if_ep_disconnect() does not take a reference on conn (which is not refcounted by the endpoint). If a concurrent hardware teardown destroys the connection and drops its last reference during flush_work(), wouldn't mutex_lock(&conn->ep_mutex) re-acquire the lock on freed memory?

Quoted 3 lines
drivers/scsi/scsi_transport_iscsi.c +0 -0
 {
 	/* Check if this was a conn error and the kernel took ownership */
 	spin_lock_irq(&conn->lock);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=51
author reviewer
#80 lore raw

PATCH v3 52/57 scsi: smartpqi: Enable lock context analysis

In reply to Bart Van Assche

Document locking requirements with __acquires(), __releases() and __must_hold(). Annotate functions that perform conditional locking with __no_context_analysis.

---
 drivers/scsi/smartpqi/Makefile        |  3 ++
 drivers/scsi/smartpqi/smartpqi_init.c | 67 ++++++++++++++++++++++-----
 2 files changed, 59 insertions(+), 11 deletions(-)
drivers/scsi/smartpqi/Makefile +3 -0
index 28985e508b5c..71db5cd96284 100644
@@ -1,3 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi.o
 smartpqi-objs := smartpqi_init.o smartpqi_sis.o smartpqi_sas_transport.o
drivers/scsi/smartpqi/smartpqi_init.c +56 -11

188 lines not on this page · raw message

#81 lore raw

Re: [PATCH v3 52/57] scsi: smartpqi: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Medium] The patch introduces fake __acquire() directives to artificially balance the static analyzer's lock count, and the commit message incorrectly claims to use __must_hold() and __no_context_analysis() which are entirely absent.

Pre-existing issues:
- [High] The driver violates strict struct mutex ownership rules by acquiring mutexes in one execution context and releasing them in another, resulting in cross-task unlock violations.
- [High] Error paths in pqi_resume_or_restore return early without releasing scan_mutex and lun_reset_mutex, causing a permanent lock leak.
- [High] Blindly unlocking ofa_mutex during an OFA cancellation event in pqi_ofa_process_event() without verifying it is held causes undefined behavior. --

--- Patch [52]: [PATCH v3 52/57] scsi: smartpqi: Enable lock context analysis --- Please note that due to recitation restrictions, this review uses a free-form summary format rather than quoting the original patch diff directly.

commit 8564515b7a0cc93357264da233ee9448ed603eee
Author: Bart Van Assche <[email protected]>
scsi: smartpqi: Enable lock context analysis

This patch aims to document locking requirements in the smartpqi driver using sparse annotations like __acquires() and __releases() to enable lock context analysis.

[Severity: Medium]
The commit message states that this patch annotates functions with __must_hold() and __no_context_analysis, but neither of those seem to be used in the actual code.

Additionally, in functions like pqi_process_soft_reset() and pqi_update_device_list(), the patch adds fake __acquire() directives with comments asking what guarantees the mutexes are held. Does this misuse the sparse annotations by artificially balancing the lock count just to suppress warnings, instead of fixing or properly documenting the locking design?

[Severity: High]
This isn't a bug introduced by this patch, but the driver appears to violate strict struct mutex ownership rules. For example, pqi_ofa_memory_alloc_worker() acquires the ofa_mutex, but the lock is later released by a completely different worker thread in pqi_ofa_quiesce_worker() (via pqi_process_soft_reset()).

Similarly, pqi_suspend_or_freeze() locks scan_mutex and lun_reset_mutex prior to sleep, which are then released by resume operations, potentially across different async PM threads.

Since struct mutex requires the locking task to be the one that unlocks it, could this lead to DEBUG_LOCKS_WARN_ON, workqueue lock leak warnings, and potential internal mutex state corruption during firmware events or system PM operations?

[Severity: High]
This is a pre-existing issue, but in pqi_resume_or_restore(), if the driver encounters a disconnected device (ctrl_removal_state != PQI_CTRL_PRESENT) or if pqi_resume_ctrl() fails, the function returns early.

Since it returns without calling pqi_ctrl_unblock_device_reset() and pqi_ctrl_unblock_scan(), does this cause a permanent resource leak of scan_mutex and lun_reset_mutex? This could leave the driver deadlocked for any future operations requiring these locks. The patch seems to legitimize this leak to sparse by adding __cond_releases() annotations.

[Severity: High]
This is a pre-existing issue, but in pqi_ofa_process_event(), when handling the PQI_EVENT_OFA_CANCELED event, the code calls pqi_ctrl_ofa_done() which blindly unlocks ofa_mutex.

If a cancellation event is received from the controller firmware when no OFA memory allocation is in progress, or before the worker thread has had a chance to acquire the mutex, wouldn't unlocking an unheld mutex trigger a DEBUG_LOCKS_WARN_ON and corrupt the mutex state?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=52
author reviewer
#82 lore raw

PATCH v3 53/57 scsi: snic: Enable lock context analysis

In reply to Bart Van Assche

Set the CONTEXT_ANALYSIS variable in the Makefile.

---
 drivers/scsi/snic/Makefile | 3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/snic/Makefile +3 -0
index 41546e3cb701..b12563cb174d 100644
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_SNIC) += snic.o
 
 snic-y := \
author reviewer
#83 lore raw

PATCH v3 54/57 scsi: sym53c8xx_2: Enable lock context analysis

In reply to Bart Van Assche
---
 drivers/scsi/sym53c8xx_2/Makefile | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/sym53c8xx_2/Makefile +2 -0
index 0751e2a0cd82..c28fb2b65622 100644
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 # Makefile for the NCR/SYMBIOS/LSI 53C8XX PCI SCSI controllers driver.
 
+CONTEXT_ANALYSIS := y
+
 sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_nvram.o
 obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o
#84 lore raw

Re: [PATCH v3 54/57] scsi: sym53c8xx_2: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Unconditional NULL pointer dereference (Undefined Behavior) in bnx2fc_process_cq_compl due to…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unconditional NULL pointer dereference (Undefined Behavior) in bnx2fc_process_cq_compl due to __assume_ctx_lock on uninitialized cb_arg pointer. --

--- Patch [54]: [PATCH v3 54/57] scsi: sym53c8xx_2: Enable lock context analysis --- commit 914539bc4256a83053d649d7450d8f075fb16ba8
Author: Bart Van Assche <[email protected]>

scsi: bnx2fc: Enable lock context analysis

This commit adds lock context analysis to the bnx2fc driver. It documents locking requirements using __must_hold() and informs the compiler about aliases for synchronization objects with __assume_ctx_lock().

drivers/scsi/bnx2fc/bnx2fc_hwi.c void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe, · line 884

Quoted 10 lines
drivers/scsi/bnx2fc/bnx2fc_hwi.c +2 -0
index a5ecb87d5b2d7..a3670c48900ba 100644
@@ -880,6 +880,9 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe,
 		return;
 	}
 
+	/* Tell the compiler that there is an alias for tgt->tgt_lock. */
+	__assume_ctx_lock(&io_req->cb_arg->aborted_io_req->tgt->tgt_lock);

[Severity: High]
Does this code trigger a NULL pointer dereference for standard SCSI completions?

Looking at bnx2fc_process_cq_compl(), io_req->cb_arg is only allocated for ELS or sequence cleanup commands. For standard FCoE SCSI completions (BNX2FC_SCSI_CMD), io_req->cb_arg is NULL.

Since __assume_ctx_lock evaluates its argument to a pointer, evaluating this nested pointer chain requires fetching the aborted_io_req pointer from the memory addressed by cb_arg. Because cb_arg is NULL, this forces an invalid memory read.

Can this be unconditionally reached on every SCSI completion, causing a hard crash or triggering UndefinedBehaviorSanitizer warnings during normal I/O?

Quoted 3 lines
drivers/scsi/bnx2fc/bnx2fc_hwi.c +1 -0
+
 	/* Timestamp IO completion time */
 	cmd_type = io_req->cmd_type;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=54
author reviewer
#85 lore raw

PATCH v3 55/57 scsi: core: Pass the SCSI host pointer directly

In reply to Bart Van Assche

In the functions scsi_probe_and_add_lun(), scsi_sequential_lun_scan(), scsi_report_lun_scan() and __scsi_scan_target() the SCSI host pointer is derived from the SCSI target pointer. Pass the SCSI host pointer directly. This patch prepares for enabling lock context analysis. No functionality has been changed.

---
 drivers/scsi/scsi_scan.c | 46 ++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 21 deletions(-)
drivers/scsi/scsi_scan.c +25 -21
index e27da038603a..a8994c07d415 100644
@@ -1169,6 +1169,7 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq,
 
 /**
  * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it
+ * @shost:      SCSI host pointer
  * @starget:	pointer to target device structure
  * @lun:	LUN of target device
  * @bflagsp:	store bflags here if not NULL
@@ -1188,7 +1189,8 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq,
  *         attached at the LUN
  *   - SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized
  **/
-static int scsi_probe_and_add_lun(struct scsi_target *starget,
+static int scsi_probe_and_add_lun(struct Scsi_Host *shost,
+				  struct scsi_target *starget,
 				  u64 lun, blist_flags_t *bflagsp,
 				  struct scsi_device **sdevp,
 				  enum scsi_scan_mode rescan,
@@ -1198,7 +1200,6 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
 	unsigned char *result;
 	blist_flags_t bflags;
 	int res = SCSI_SCAN_NO_RESPONSE, result_len = 256;
-	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 
 	/*
 	 * The rescan flag is used as an optimization, the first scan of a
@@ -1334,6 +1335,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
 
 /**
  * scsi_sequential_lun_scan - sequentially scan a SCSI target
+ * @shost:      SCSI host pointer
  * @starget:	pointer to target structure to scan
  * @bflags:	black/white list flag for LUN 0
  * @scsi_level: Which version of the standard does this device adhere to
@@ -1346,13 +1348,13 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
  *
  *     Modifies sdevscan->lun.
  **/
-static void scsi_sequential_lun_scan(struct scsi_target *starget,
+static void scsi_sequential_lun_scan(struct Scsi_Host *shost,
+				     struct scsi_target *starget,
 				     blist_flags_t bflags, int scsi_level,
 				     enum scsi_scan_mode rescan)
 {
 	uint max_dev_lun;
 	u64 sparse_lun, lun;
-	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 
 	SCSI_LOG_SCAN_BUS(3, starget_printk(KERN_INFO, starget,
 		"scsi scan: Sequential scan\n"));
@@ -1412,8 +1414,8 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget,
 	 * sparse_lun.
 	 */
 	for (lun = 1; lun < max_dev_lun; ++lun)
-		if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
-					    NULL) != SCSI_SCAN_LUN_PRESENT) &&
+		if (scsi_probe_and_add_lun(shost, starget, lun, NULL, NULL,
+				rescan, NULL) != SCSI_SCAN_LUN_PRESENT &&
 		    !sparse_lun)
 			return;
 }
@@ -1438,8 +1440,9 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget,
  *     0: scan completed (or no memory, so further scanning is futile)
  *     1: could not scan with REPORT LUN
  **/
-static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflags,
-				enum scsi_scan_mode rescan)
+static int scsi_report_lun_scan(struct Scsi_Host *shost,
+			struct scsi_target *starget, blist_flags_t bflags,
+			enum scsi_scan_mode rescan)
 {
 	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
 	unsigned int length;
@@ -1448,7 +1451,6 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag
 	int result;
 	struct scsi_lun *lunp, *lun_data;
 	struct scsi_device *sdev;
-	struct Scsi_Host *shost = dev_to_shost(&starget->dev);
 	struct scsi_failure failure_defs[] = {
 		{
 			.sense = UNIT_ATTENTION,
@@ -1594,7 +1596,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag
 		} else {
 			int res;
 
-			res = scsi_probe_and_add_lun(starget,
+			res = scsi_probe_and_add_lun(shost, starget,
 				lun, NULL, NULL, rescan, NULL);
 			if (res == SCSI_SCAN_NO_RESPONSE) {
 				/*
@@ -1641,7 +1643,7 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
 		scsi_complete_async_scans();
 
 	if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
-		scsi_probe_and_add_lun(starget, lun, NULL, &sdev,
+		scsi_probe_and_add_lun(shost, starget, lun, NULL, &sdev,
 				       SCSI_SCAN_RESCAN, hostdata);
 		scsi_autopm_put_host(shost);
 	}
@@ -1763,10 +1765,10 @@ int scsi_rescan_device(struct scsi_device *sdev)
 }
 EXPORT_SYMBOL(scsi_rescan_device);
 
-static void __scsi_scan_target(struct device *parent, unsigned int channel,
-		unsigned int id, u64 lun, enum scsi_scan_mode rescan)
+static void __scsi_scan_target(struct Scsi_Host *shost, struct device *parent,
+			       unsigned int channel, unsigned int id, u64 lun,
+			       enum scsi_scan_mode rescan)
 {
-	struct Scsi_Host *shost = dev_to_shost(parent);
 	blist_flags_t bflags = 0;
 	int res;
 	struct scsi_target *starget;
@@ -1786,7 +1788,8 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
 		/*
 		 * Scan for a specific host/chan/id/lun.
 		 */
-		scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
+		scsi_probe_and_add_lun(shost, starget, lun, NULL, NULL, rescan,
+				       NULL);
 		goto out_reap;
 	}
 
@@ -1794,14 +1797,15 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
 	 * Scan LUN 0, if there is some response, scan further. Ideally, we
 	 * would not configure LUN 0 until all LUNs are scanned.
 	 */
-	res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL);
+	res = scsi_probe_and_add_lun(shost, starget, 0, &bflags, NULL, rescan,
+				     NULL);
 	if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) {
-		if (scsi_report_lun_scan(starget, bflags, rescan) != 0)
+		if (scsi_report_lun_scan(shost, starget, bflags, rescan) != 0)
 			/*
 			 * The REPORT LUN did not scan the target,
 			 * do a sequential scan.
 			 */
-			scsi_sequential_lun_scan(starget, bflags,
+			scsi_sequential_lun_scan(shost, starget, bflags,
 						 starget->scsi_level, rescan);
 	}
 
@@ -1851,7 +1855,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel,
 		scsi_complete_async_scans();
 
 	if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
-		__scsi_scan_target(parent, channel, id, lun, rescan);
+		__scsi_scan_target(shost, parent, channel, id, lun, rescan);
 		scsi_autopm_put_host(shost);
 	}
 	mutex_unlock(&shost->scan_mutex);
@@ -1882,11 +1886,11 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
 				order_id = shost->max_id - id - 1;
 			else
 				order_id = id;
-			__scsi_scan_target(&shost->shost_gendev, channel,
+			__scsi_scan_target(shost, &shost->shost_gendev, channel,
 					order_id, lun, rescan);
 		}
 	else
-		__scsi_scan_target(&shost->shost_gendev, channel,
+		__scsi_scan_target(shost, &shost->shost_gendev, channel,
 				id, lun, rescan);
 }
 
#86 lore raw

Re: [PATCH v3 55/57] scsi: core: Pass the SCSI host pointer directly

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] The kernel-doc block for `scsi_report_lun_scan()` was not updated to document the newly added…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The kernel-doc block for scsi_report_lun_scan() was not updated to document the newly added shost parameter. --

--- Patch [55]: [PATCH v3 55/57] scsi: core: Pass the SCSI host pointer directly --- commit 843d3f98c1bc1414a3f6276906221ddaef557d90
Author: Bart Van Assche <[email protected]>

scsi: core: Pass the SCSI host pointer directly

In several SCSI scanning functions, the SCSI host pointer is derived from the SCSI target pointer. This patch updates these functions to pass the SCSI host pointer directly, preparing for lock context analysis. No functionality is changed.

Quoted 4 lines
drivers/scsi/scsi_scan.c +0 -0
index e27da038603a2..a8994c07d4154 100644

[ ... ]

drivers/scsi/scsi_scan.c static void scsi_sequential_lun_scan(struct scsi_target *starget, · line 1445

Quoted 9 lines
drivers/scsi/scsi_scan.c +3 -2
@@ -1438,8 +1440,9 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget,
  *     0: scan completed (or no memory, so further scanning is futile)
  *     1: could not scan with REPORT LUN
  **/
-static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflags,
-				enum scsi_scan_mode rescan)
+static int scsi_report_lun_scan(struct Scsi_Host *shost,
+			struct scsi_target *starget, blist_flags_t bflags,
+			enum scsi_scan_mode rescan)

[Severity: Low]
Should the kernel-doc block for scsi_report_lun_scan() be updated to include the new @shost parameter?

It looks like the parameter was added to the function signature, but unlike scsi_probe_and_add_lun() and scsi_sequential_lun_scan(), the documentation wasn't updated here.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=55
author reviewer
#87 lore raw

PATCH v3 56/57 scsi: core: Enable lock context analysis

In reply to Bart Van Assche

Document which functions expect that shost->scan_mutex is held. Inform the compiler about synchronization object aliases with __assume_ctx_lock(). Enable lock context analysis for the SCSI core and also for all drivers in the drivers/scsi/ directory.

---
 drivers/scsi/Makefile                | 1 +
 drivers/scsi/device_handler/Makefile | 3 +++
 drivers/scsi/scsi_scan.c             | 5 +++++
 include/scsi/scsi_host.h             | 2 ++
 4 files changed, 11 insertions(+)
drivers/scsi/Makefile +1 -0
index 842c254bb226..a1fbdf4b0705 100644
@@ -14,6 +14,7 @@
 # satisfy certain initialization assumptions in the SCSI layer.
 # *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
 
+CONTEXT_ANALYSIS := y
 
 CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF
 
drivers/scsi/device_handler/Makefile +3 -0
index 0a603aefd2bb..5aa282a63e24 100644
@@ -2,6 +2,9 @@
 #
 # SCSI Device Handler
 #
+
+CONTEXT_ANALYSIS := y
+
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
 obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o
drivers/scsi/scsi_scan.c +5 -0
index a8994c07d415..8096ca51161b 100644
@@ -1195,6 +1195,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *shost,
 				  struct scsi_device **sdevp,
 				  enum scsi_scan_mode rescan,
 				  void *hostdata)
+	__must_hold(&shost->scan_mutex)
 {
 	struct scsi_device *sdev;
 	unsigned char *result;
@@ -1352,6 +1353,7 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost,
 				     struct scsi_target *starget,
 				     blist_flags_t bflags, int scsi_level,
 				     enum scsi_scan_mode rescan)
+	__must_hold(&shost->scan_mutex)
 {
 	uint max_dev_lun;
 	u64 sparse_lun, lun;
@@ -1443,6 +1445,7 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost,
 static int scsi_report_lun_scan(struct Scsi_Host *shost,
 			struct scsi_target *starget, blist_flags_t bflags,
 			enum scsi_scan_mode rescan)
+	__must_hold(&shost->scan_mutex)
 {
 	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
 	unsigned int length;
@@ -1768,6 +1771,7 @@ EXPORT_SYMBOL(scsi_rescan_device);
 static void __scsi_scan_target(struct Scsi_Host *shost, struct device *parent,
 			       unsigned int channel, unsigned int id, u64 lun,
 			       enum scsi_scan_mode rescan)
+	__must_hold(&shost->scan_mutex)
 {
 	blist_flags_t bflags = 0;
 	int res;
@@ -1865,6 +1869,7 @@ EXPORT_SYMBOL(scsi_scan_target);
 static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
 			      unsigned int id, u64 lun,
 			      enum scsi_scan_mode rescan)
+	__must_hold(&shost->scan_mutex)
 {
 	uint order_id;
 
include/scsi/scsi_host.h +2 -0
index 7e2011830ba4..2bbe7cb0060b 100644
@@ -534,6 +534,8 @@ struct scsi_host_template {
 		enum scsi_qc_status rc;					\
 									\
 		spin_lock_irqsave(shost->host_lock, irq_flags);		\
+		/* Tell the compiler that cmd->device->host == shost. */\
+		__assume_ctx_lock(cmd->device->host->host_lock);	\
 		rc = func_name##_lck(cmd);				\
 		spin_unlock_irqrestore(shost->host_lock, irq_flags);	\
 		return rc;						\
#88 lore raw

Re: [PATCH v3 56/57] scsi: core: Enable lock context analysis

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Unconditional evaluation of `io_req->cb_arg->aborted_io_req->tgt->tgt_lock` causes a NULL pointer…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Unconditional evaluation of io_req->cb_arg->aborted_io_req->tgt->tgt_lock causes a NULL pointer dereference for standard SCSI commands.
- [Medium] Falsely adding __acquire() macros to trick the static analyzer instead of correctly annotating the function contract. --

--- Patch [56]: [PATCH v3 56/57] scsi: core: Enable lock context analysis --- commit 107a7992c0ed289ceb2e27bff45ba1eb5b8101fd
Author: Bart Van Assche <[email protected]>

scsi: core: Enable lock context analysis

Document which functions expect that shost->scan_mutex is held. Inform the compiler about synchronization object aliases with __assume_ctx_lock(). Enable lock context analysis for the SCSI core and also for all drivers in the drivers/scsi/ directory.

drivers/scsi/Makefile line 17

Quoted 9 lines
drivers/scsi/Makefile +1 -0
index 842c254bb2269..a1fbdf4b0705a 100644
@@ -14,6 +14,7 @@
 # satisfy certain initialization assumptions in the SCSI layer.
 # *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
 
+CONTEXT_ANALYSIS := y

[Severity: High]
Does enabling context analysis here cause a NULL pointer dereference in bnx2fc_process_cq_compl()?

When context analysis is enabled, __assume_ctx_lock() forces the compiler to evaluate its argument. For standard SCSI commands where io_req->cb_arg is NULL, this expression in drivers/scsi/bnx2fc/bnx2fc_hwi.c will dereference a NULL pointer on the CQ completion fast path:

	/* Tell the compiler that there is an alias for tgt->tgt_lock. */
	__assume_ctx_lock(&io_req->cb_arg->aborted_io_req->tgt->tgt_lock);

[Severity: Medium]
Are the __acquire() annotations in pqi_process_soft_reset() masking an unbalanced unlock from the static analyzer?

By enabling context analysis, it looks like this exposes a mismatch in the contract for pqi_ofa_quiesce_worker() in drivers/scsi/smartpqi/smartpqi_init.c.

The worker is incorrectly annotated as returning with the scan_mutex and lun_reset_mutex held:

static void pqi_ofa_quiesce_worker(struct work_struct *work)

	__acquires(&quiesce_work_to_ctrl_info(work)->scan_mutex)
	__acquires(&quiesce_work_to_ctrl_info(work)->lun_reset_mutex)

However, the worker calls pqi_process_soft_reset(), which unconditionally drops these locks in all execution paths.

It looks like fake __acquire() macros were added to pqi_process_soft_reset() to trick the analyzer into ignoring the unbalanced unlocks:

	/* What guarantees that these mutexes are held here? */
	__acquire(&ctrl_info->lun_reset_mutex);
	__acquire(&ctrl_info->ofa_mutex);
	__acquire(&ctrl_info->scan_mutex);
	pqi_ctrl_ofa_done(ctrl_info);
	pqi_ofa_ctrl_unquiesce(ctrl_info);

Does this intentionally hide the dynamic lock release pattern from the static analyzer instead of correctly annotating the function contract?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=56
author reviewer
#89 lore raw

PATCH v3 57/57 scsi: core: Protect host state changes with the host lock

In reply to Bart Van Assche

Some but not all SCSI host state changes are protected with the SCSI host lock. Annotate the SCSI host state with __guarded_by(host_lock), protect all SCSI host state changes with the SCSI host lock and use READ_ONCE() for all SCSI host state reads. This patch prevents that KCSAN complains about data races when accessing the SCSI host state.

---
 drivers/scsi/hosts.c                      | 13 ++++++++-----
 drivers/scsi/megaraid/megaraid_sas_base.c |  2 +-
 drivers/scsi/mpt3sas/mpt3sas_scsih.c      |  2 +-
 drivers/scsi/qla4xxx/ql4_os.c             |  6 ++----
 drivers/scsi/scsi_lib.c                   |  3 +--
 drivers/scsi/scsi_sysfs.c                 |  7 ++++---
 include/scsi/scsi_host.h                  | 23 ++++++++++++++++-------
 7 files changed, 33 insertions(+), 23 deletions(-)
drivers/scsi/hosts.c +8 -5
index d512080268af..2b9fd76fb435 100644
@@ -276,7 +276,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 	if (error)
 		goto out_disable_runtime_pm;
 
-	scsi_host_set_state(shost, SHOST_RUNNING);
+	scoped_guard(spinlock_irq, shost->host_lock)
+		scsi_host_set_state(shost, SHOST_RUNNING);
 	get_device(shost->shost_gendev.parent);
 
 	device_enable_async_suspend(&shost->shost_dev);
@@ -350,6 +351,7 @@ EXPORT_SYMBOL(scsi_add_host_with_dma);
 static void scsi_host_dev_release(struct device *dev)
 {
 	struct Scsi_Host *shost = dev_to_shost(dev);
+	enum scsi_host_state host_state = scsi_get_host_state(shost);
 	struct device *parent = dev->parent;
 
 	/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
@@ -362,7 +364,7 @@ static void scsi_host_dev_release(struct device *dev)
 	if (shost->work_q)
 		destroy_workqueue(shost->work_q);
 
-	if (shost->shost_state == SHOST_CREATED) {
+	if (host_state == SHOST_CREATED) {
 		/*
 		 * Free the shost_dev device name and remove the proc host dir
 		 * here if scsi_host_{alloc,put}() have been called but neither
@@ -378,7 +380,7 @@ static void scsi_host_dev_release(struct device *dev)
 
 	ida_free(&host_index_ida, shost->host_no);
 
-	if (shost->shost_state != SHOST_CREATED)
+	if (host_state != SHOST_CREATED)
 		put_device(parent);
 	kfree(shost);
 }
@@ -412,7 +414,8 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
 
 	shost->host_lock = &shost->default_lock;
 	spin_lock_init(shost->host_lock);
-	shost->shost_state = SHOST_CREATED;
+	scoped_guard(spinlock_init, shost->host_lock)
+		shost->shost_state = SHOST_CREATED;
 	INIT_LIST_HEAD(&shost->__devices);
 	INIT_LIST_HEAD(&shost->__targets);
 	INIT_LIST_HEAD(&shost->eh_abort_list);
@@ -598,7 +601,7 @@ EXPORT_SYMBOL(scsi_host_lookup);
  **/
 struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
 {
-	if ((shost->shost_state == SHOST_DEL) ||
+	if (scsi_get_host_state(shost) == SHOST_DEL ||
 		!get_device(&shost->shost_gendev))
 		return NULL;
 	return shost;
drivers/scsi/megaraid/megaraid_sas_base.c +1 -1
index ccefe5841a17..97a81a86db82 100644
@@ -3075,7 +3075,7 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd)
 
 	scmd_printk(KERN_INFO, scmd,
 		"SCSI host state: %d  SCSI host busy: %d  FW outstanding: %d\n",
-		scmd->device->host->shost_state,
+		scsi_get_host_state(scmd->device->host),
 		scsi_host_busy(scmd->device->host),
 		atomic_read(&instance->fw_outstanding));
 	/*
drivers/scsi/mpt3sas/mpt3sas_scsih.c +1 -1
index dea78688cc9b..0e12009a87f6 100644
@@ -5472,7 +5472,7 @@ static enum scsi_qc_status scsih_qcmd(struct Scsi_Host *shost,
 	 * Avoid error handling escallation when device is disconnected
 	 */
 	if (handle == MPT3SAS_INVALID_DEVICE_HANDLE || sas_device_priv_data->block) {
-		if (scmd->device->host->shost_state == SHOST_RECOVERY &&
+		if (scsi_get_host_state(scmd->device->host) == SHOST_RECOVERY &&
 		    scmd->cmnd[0] == TEST_UNIT_READY) {
 			scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07);
 			scsi_done(scmd);
drivers/scsi/qla4xxx/ql4_os.c +2 -4
index d598ab4126f8..c9d9fc7c81fb 100644
@@ -9411,11 +9411,9 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
  * This routine finds that if reset host is called in EH
  * scenario or from some application like sg_reset
  **/
-static int qla4xxx_is_eh_active(struct Scsi_Host *shost)
+static bool qla4xxx_is_eh_active(struct Scsi_Host *shost)
 {
-	if (shost->shost_state == SHOST_RECOVERY)
-		return 1;
-	return 0;
+	return scsi_get_host_state(shost) == SHOST_RECOVERY;
 }
 
 /**
drivers/scsi/scsi_lib.c +1 -2
index 22e2e3223440..bd592abf1f39 100644
@@ -1661,10 +1661,9 @@ static enum scsi_qc_status scsi_dispatch_cmd(struct scsi_cmnd *cmd)
 		goto done;
 	}
 
-	if (unlikely(host->shost_state == SHOST_DEL)) {
+	if (scsi_get_host_state(host) == SHOST_DEL) {
 		cmd->result = (DID_NO_CONNECT << 16);
 		goto done;
-
 	}
 
 	trace_scsi_dispatch_cmd_start(cmd);
drivers/scsi/scsi_sysfs.c +4 -3
index dfc3559e7e04..9480432f650b 100644
@@ -214,8 +214,9 @@ store_shost_state(struct device *dev, struct device_attribute *attr,
 	if (!state)
 		return -EINVAL;
 
-	if (scsi_host_set_state(shost, state))
-		return -EINVAL;
+	scoped_guard(spinlock_irq, shost->host_lock)
+		if (scsi_host_set_state(shost, state))
+			return -EINVAL;
 	return count;
 }
 
@@ -223,7 +224,7 @@ static ssize_t
 show_shost_state(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct Scsi_Host *shost = class_to_shost(dev);
-	const char *name = scsi_host_state_name(shost->shost_state);
+	const char *name = scsi_host_state_name(scsi_get_host_state(shost));
 
 	if (!name)
 		return -EINVAL;
include/scsi/scsi_host.h +16 -7
index 2bbe7cb0060b..993628019916 100644
@@ -729,7 +729,7 @@ struct Scsi_Host {
 	unsigned int  irq;
 	
 
-	enum scsi_host_state shost_state;
+	enum scsi_host_state shost_state __guarded_by(host_lock);
 
 	/* ldm bits */
 	struct device		shost_gendev, shost_dev;
@@ -787,11 +787,18 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev)
 	return container_of(dev, struct Scsi_Host, shost_gendev);
 }
 
+static inline enum scsi_host_state scsi_get_host_state(struct Scsi_Host *shost)
+{
+	return context_unsafe(READ_ONCE(shost->shost_state));
+}
+
 static inline int scsi_host_in_recovery(struct Scsi_Host *shost)
 {
-	return shost->shost_state == SHOST_RECOVERY ||
-		shost->shost_state == SHOST_CANCEL_RECOVERY ||
-		shost->shost_state == SHOST_DEL_RECOVERY ||
+	enum scsi_host_state state = scsi_get_host_state(shost);
+
+	return state == SHOST_RECOVERY ||
+		state == SHOST_CANCEL_RECOVERY ||
+		state == SHOST_DEL_RECOVERY ||
 		shost->tmf_in_progress;
 }
 
@@ -837,8 +844,9 @@ static inline struct device *scsi_get_device(struct Scsi_Host *shost)
  **/
 static inline int scsi_host_scan_allowed(struct Scsi_Host *shost)
 {
-	return shost->shost_state == SHOST_RUNNING ||
-	       shost->shost_state == SHOST_RECOVERY;
+	enum scsi_host_state state = scsi_get_host_state(shost);
+
+	return state == SHOST_RUNNING || state == SHOST_RECOVERY;
 }
 
 extern void scsi_unblock_requests(struct Scsi_Host *);
@@ -942,6 +950,7 @@ static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost)
 	return shost->prot_guard_type;
 }
 
-extern int scsi_host_set_state(struct Scsi_Host *, enum scsi_host_state);
+int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
+	__must_hold(shost->host_lock);
 
 #endif /* _SCSI_SCSI_HOST_H */
#90 lore raw

Re: [PATCH v3 57/57] scsi: core: Protect host state changes with the host lock

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] The patch missed converting a direct read of `shost_state` in the `mpi3mr` driver to the new…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] The patch missed converting a direct read of shost_state in the mpi3mr driver to the new scsi_get_host_state() API.
- [Medium] Double initialization of host_lock via the spinlock_init guard overwrites the correct lockdep class name.
- [Medium] scsi_host_set_state() performs a plain write to shost_state, which races with the newly introduced READ_ONCE() lockless readers. --

--- Patch [57]: [PATCH v3 57/57] scsi: core: Protect host state changes with the host lock --- commit b02669cdb0e82751599147d300ebcdc8db8d8734
Author: Bart Van Assche <[email protected]>

scsi: core: Protect host state changes with the host lock

Some but not all SCSI host state changes are protected with the SCSI host lock. Annotate the SCSI host state with __guarded_by(host_lock), protect all SCSI host state changes with the SCSI host lock and use READ_ONCE() for all SCSI host state reads. This patch prevents that KCSAN complains about data races when accessing the SCSI host state.

Quoted 4 lines
drivers/scsi/hosts.c +0 -0
index 08ec144f7bf77..222baa7e10680 100644

[ ... ]

drivers/scsi/hosts.c struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv · line 419

Quoted 7 lines
drivers/scsi/hosts.c +2 -1
@@ -413,7 +415,8 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
 
 	shost->host_lock = &shost->default_lock;
 	spin_lock_init(shost->host_lock);
-	shost->shost_state = SHOST_CREATED;
+	scoped_guard(spinlock_init, shost->host_lock)
+		shost->shost_state = SHOST_CREATED;

[Severity: Medium]
Will this double-initialize the host_lock and overwrite the lockdep class name?

The previous line explicitly calls spin_lock_init(shost->host_lock) which assigns the correct static lockdep key. Using scoped_guard(spinlock_init, ...) immediately after calls the initialization macro again internally, overwriting the lockdep class with a generic macro variable name (_T->lock).

[ ... ]

Quoted 4 lines
include/scsi/scsi_host.h +0 -0
index c5f85af4ba072..d0eefefdc0be8 100644

[ ... ]

include/scsi/scsi_host.h static inline struct Scsi_Host *dev_to_shost(struct device *dev) · line 797

Quoted 9 lines
include/scsi/scsi_host.h +5 -0
@@ -790,11 +790,18 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev)
 	return container_of(dev, struct Scsi_Host, shost_gendev);
 }
 
+static inline enum scsi_host_state scsi_get_host_state(struct Scsi_Host *shost)
+{
+	return context_unsafe(READ_ONCE(shost->shost_state));
+}
+

[Severity: Medium]
Does the plain write in scsi_host_set_state() need to be updated to WRITE_ONCE() to match this new READ_ONCE()?

Since scsi_host_set_state() performs a plain write to shost->shost_state, concurrent lockless READ_ONCE() readers could still cause KCSAN to flag data races due to compiler tearing on the writer side. The LKMM requires WRITE_ONCE() on the writer side when there are concurrent lockless READ_ONCE() readers.

[ ... ]

include/scsi/scsi_host.h static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost) · line 959

Quoted 9 lines
include/scsi/scsi_host.h +2 -1
@@ -945,6 +953,7 @@ static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost)
 	return shost->prot_guard_type;
 }
 
-extern int scsi_host_set_state(struct Scsi_Host *, enum scsi_host_state);
+int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
+	__must_hold(shost->host_lock);
 
 #endif /* _SCSI_SCSI_HOST_H */

[Severity: Medium]
Was the mpi3mr driver missed during the tree-wide conversion to the new scsi_get_host_state() API?

It looks like mpi3mr_qcmd() in drivers/scsi/mpi3mr/mpi3mr_os.c still checks scmd->device->host->shost_state == SHOST_RECOVERY directly without using scsi_get_host_state() or holding the host_lock.

Will this trigger a new Sparse warning due to the newly added __guarded_by(host_lock) annotation on shost_state?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=57