CVE-2026-68396 Zhou Minqiang (2): scsi: core: wake eh reliably when using scsi_schedule_eh scsi: core: Fix kabi breakage of struct Scsi_Host drivers/scsi/hosts.c | 13 +++++++++++++ drivers/scsi/scsi_error.c | 24 +++++++++++++++++++++++- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_host.h | 7 ++++++- 4 files changed, 43 insertions(+), 2 deletions(-) -- 2.52.0
mainline inclusion from mainline-v7.2-rc4 commit dccf3b1798b70f94e958b3d00b83010399e6fb05 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/17046 CVE: CVE-2026-68396 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Drivers which use the scsi_schedule_eh function to run the error handler currently risk the error handler thread never waking once all commands are timed out or inactive. There is no enforced memory order between setting the host into error recovery state and counting busy commands. This can result in a race with scsi_dec_host_busy where neither CPU sees both conditions of all commands inactive and the host error state to request waking the error handler. To fix this, run the scsi_schedule_eh's scsi_eh_wakeup from a new work item which will use rcu to ensure scsi_schedule_eh's call to scsi_host_busy will occur after the error state is globally visible and will be seen by any current scsi_dec_host_busy callers. Fixes: 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq") Signed-off-by: David Jeffery <djeffery@redhat.com> Link: https://patch.msgid.link/20260615174630.11492-1-djeffery@redhat.com Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Zhou Minqiang <zhouminqiang2@huawei.com> --- drivers/scsi/hosts.c | 2 ++ drivers/scsi/scsi_error.c | 22 +++++++++++++++++++++- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_host.h | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index be01e20e5930..a587ed5c3b95 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -325,6 +325,7 @@ static void scsi_host_dev_release(struct device *dev) /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ rcu_barrier(); + cancel_work_sync(&shost->eh_work); if (shost->tmf_work_q) destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) @@ -397,6 +398,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup); index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 90f275ac43c6..4da5b13bdafc 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -75,6 +75,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) } } +void scsi_rcu_eh_wakeup(struct work_struct *work) +{ + struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work); + unsigned long flags; + unsigned int busy; + + /* + * Ensure any running scsi_dec_host_busy has completed its rcu section + * so changes to host state and host_eh_scheduled are visible to all + * future calls of scsi_dec_host_busy + */ + synchronize_rcu(); + + busy = scsi_host_busy(shost); + + spin_lock_irqsave(shost->host_lock, flags); + scsi_eh_wakeup(shost, busy); + spin_unlock_irqrestore(shost->host_lock, flags); +} + /** * scsi_schedule_eh - schedule EH for SCSI host * @shost: SCSI host to invoke error handling on. @@ -90,7 +110,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - scsi_eh_wakeup(shost, scsi_host_busy(shost)); + queue_work(shost->tmf_work_q, &shost->eh_work); } spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index f2004e847822..dd7c8c775b26 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -76,6 +76,7 @@ extern enum blk_eh_timer_return scsi_times_out(struct request *req); extern int scsi_error_handler(void *host); extern int scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy); +extern void scsi_rcu_eh_wakeup(struct work_struct *work); extern void scsi_eh_scmd_add(struct scsi_cmnd *); void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 3a3cc3fe9a87..208a4a3e9bd9 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -698,6 +698,9 @@ struct Scsi_Host { */ struct device *dma_dev; + /* Used for an rcu-synchronizing eh wakeup */ + struct work_struct eh_work; + KABI_USE(1, bool is_builtin) KABI_REPLACE(_KABI_RESERVE(2); _KABI_RESERVE(3), struct list_head eh_abort_list) -- 2.52.0
Offering: HULK hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/17046 CVE: CVE-2026-68396 -------------------------------- Adding struct work_struct eh_work into struct Scsi_Host changes the structure size and breaks kABI for out-of-tree modules. Use KABI_USE(1, struct scsi_host_eh_work *) instead of embedding work_struct directly. The separately allocated scsi_host_eh_work contains a back-pointer to shost so the callback recovers the host via container_of(work, struct scsi_host_eh_work, work). Fixes: 29387fd2129a ("scsi: core: wake eh reliably when using scsi_schedule_eh") Signed-off-by: Zhou Minqiang <zhouminqiang2@huawei.com> --- drivers/scsi/hosts.c | 15 +++++++++++++-- drivers/scsi/scsi_error.c | 6 ++++-- include/scsi/scsi_host.h | 10 ++++++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index a587ed5c3b95..c8099a1a3f7b 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -325,7 +325,10 @@ static void scsi_host_dev_release(struct device *dev) /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ rcu_barrier(); - cancel_work_sync(&shost->eh_work); + if (shost->eh_work) { + cancel_work_sync(&shost->eh_work->work); + kfree(shost->eh_work); + } if (shost->tmf_work_q) destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) @@ -398,10 +401,18 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); - INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup); + + shost->eh_work = kzalloc(sizeof(*shost->eh_work), GFP_KERNEL); + if (!shost->eh_work) { + kfree(shost); + return NULL; + } + shost->eh_work->shost = shost; + INIT_WORK(&shost->eh_work->work, scsi_rcu_eh_wakeup); index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL); if (index < 0) { + kfree(shost->eh_work); kfree(shost); return NULL; } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 4da5b13bdafc..803fd7251346 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -77,7 +77,9 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) void scsi_rcu_eh_wakeup(struct work_struct *work) { - struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work); + struct scsi_host_eh_work *ehw = + container_of(work, struct scsi_host_eh_work, work); + struct Scsi_Host *shost = ehw->shost; unsigned long flags; unsigned int busy; @@ -110,7 +112,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - queue_work(shost->tmf_work_q, &shost->eh_work); + queue_work(shost->tmf_work_q, &shost->eh_work->work); } spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 208a4a3e9bd9..392fff5425c7 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -527,6 +527,11 @@ enum scsi_host_state { SHOST_DEL_RECOVERY, }; +struct scsi_host_eh_work { + struct Scsi_Host *shost; /* back-pointer for callback */ + struct work_struct work; +}; + struct Scsi_Host { /* * __devices is protected by the host_lock, but you should @@ -698,13 +703,10 @@ struct Scsi_Host { */ struct device *dma_dev; - /* Used for an rcu-synchronizing eh wakeup */ - struct work_struct eh_work; - KABI_USE(1, bool is_builtin) KABI_REPLACE(_KABI_RESERVE(2); _KABI_RESERVE(3), struct list_head eh_abort_list) - KABI_RESERVE(4) + KABI_USE(4, struct scsi_host_eh_work *eh_work) KABI_RESERVE(5) KABI_RESERVE(6) -- 2.52.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/27502 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/YII... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/27502 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/YII...
participants (2)
-
patchwork bot -
Zhou Minqiang