The qed iSCSI connection hash is accessed without a common lock. Operations first look up a qed_hash_iscsi_con wrapper and then keep dereferencing its embedded connection object while firmware commands are posted. Connection release removes the wrapper from the hash, releases the firmware connection, and frees the wrapper. If two endpoints hash to the same bucket, a release for one handle can race with lookup and object use for another handle. The race is reachable from qedi connection setup and teardown work: one endpoint can call release_conn() while another endpoint concurrently calls offload_conn(), update_conn(), clear_sq(), destroy_conn(), or change_mac(). The loop in qed_iscsi_get_hash() can also be traversing a bucket while a node in that bucket is unlinked and freed. This can cause a KASAN use-after-free, hash-list corruption, a stale connection pointer being passed to firmware, or a panic. The storage stop path also needs to see a stable empty/not-empty result before clearing the started state. Protect the hash and connection lifetime with a file-local mutex. Hold it across each complete connection operation, including firmware submission, hash insertion or removal, and wrapper free. The mutex also serializes connection acquisition with storage stop. Found during a static-analysis-assisted audit: candidate sites were surfaced by a Coccinelle rule, and the reader/writer paths were confirmed by LLM-assisted (GLM-5.3) code inspection. Due to a lack of hardware infrastructure, the issue was not observed at runtime. It is identified by code inspection only. Fixes: fc831825f99e ("qed: Add support for hardware offloaded iSCSI.") Assisted-by: GLM-5.3 coccinelle Signed-off-by: Binbin Deng <18983559317@163.com> --- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 34f1217227bc..f7b97883e4b9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,8 @@ struct qed_iscsi_conn { u8 abortive_dsconnect; }; +static DEFINE_MUTEX(qed_iscsi_conn_lock); + static int qed_iscsi_async_event(struct qed_hwfn *p_hwfn, u8 fw_event_code, __le16 echo, union event_ring_data *data, u8 fw_return_code) @@ -1080,6 +1083,7 @@ static int qed_iscsi_stop(struct qed_dev *cdev) { int rc; + guard(mutex)(&qed_iscsi_conn_lock); if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) { DP_NOTICE(cdev, "iscsi already stopped\n"); return 0; @@ -1158,6 +1162,7 @@ static int qed_iscsi_acquire_conn(struct qed_dev *cdev, struct qed_hash_iscsi_con *hash_con; int rc; + guard(mutex)(&qed_iscsi_conn_lock); /* Allocate a hashed connection */ hash_con = kzalloc_obj(*hash_con, GFP_ATOMIC); if (!hash_con) @@ -1188,6 +1193,7 @@ static int qed_iscsi_release_conn(struct qed_dev *cdev, u32 handle) { struct qed_hash_iscsi_con *hash_con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", @@ -1209,6 +1215,7 @@ static int qed_iscsi_offload_conn(struct qed_dev *cdev, struct qed_hash_iscsi_con *hash_con; struct qed_iscsi_conn *con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", @@ -1284,6 +1291,7 @@ static int qed_iscsi_update_conn(struct qed_dev *cdev, struct qed_hash_iscsi_con *hash_con; struct qed_iscsi_conn *con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", @@ -1308,6 +1316,7 @@ static int qed_iscsi_clear_conn_sq(struct qed_dev *cdev, u32 handle) { struct qed_hash_iscsi_con *hash_con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", @@ -1324,6 +1333,7 @@ static int qed_iscsi_destroy_conn(struct qed_dev *cdev, { struct qed_hash_iscsi_con *hash_con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", @@ -1354,6 +1364,7 @@ static int qed_iscsi_change_mac(struct qed_dev *cdev, { struct qed_hash_iscsi_con *hash_con; + guard(mutex)(&qed_iscsi_conn_lock); hash_con = qed_iscsi_get_hash(cdev, handle); if (!hash_con) { DP_NOTICE(cdev, "Failed to find connection for handle %d\n", -- 2.43.0