[PATCH OLK-6.6 0/2] Fix CVE-2026-80872
Fix CVE-2026-80872. Cássio Gabriel (2): firmware_loader: Add cancel helper for async requests ALSA: hda/tas2781: Cancel async firmware request at unbind drivers/base/firmware_loader/main.c | 69 ++++++++++++++++++++++++++--- include/linux/firmware.h | 10 +++++ sound/pci/hda/tas2781_hda_i2c.c | 3 ++ 3 files changed, 77 insertions(+), 5 deletions(-) -- 2.34.1
From: Cássio Gabriel <cassiogabrielcontato@gmail.com> mainline inclusion from mainline-v7.2-rc1 commit b9bdd68b8b979c7e9de58b2e7d21e1d7d932c755 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18716 CVE: CVE-2026-80872 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- request_firmware_nowait() keeps the callback module pinned and holds a device reference until the firmware work completes. Callers still have no way to cancel or synchronize the queued callback before tearing down their driver-private state. Track scheduled async firmware work in an internal list and add request_firmware_nowait_cancel(). The helper cancels work matching the device, callback context and callback function. It cancels work that has not started yet and waits for an already-running callback to return. If the request has already completed, it is a no-op. Keep the existing request_firmware_nowait() lifetime model manual. A devres-managed variant can be layered on top separately if needed. Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com> Reviewed-by: Takashi Iwai <tiwai@suse.de> Acked-by: Danilo Krummrich <dakr@kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-1... Conflicts: drivers/base/firmware_loader/main.c [The prerequisite patch 11c63e57404e ("firmware: add nowarn variant of request_firmware_nowait()") changed request_firmware_nowait to _request_firmware_nowait, thereby causing a conflict. This patch is intended to introduce a nowarn variant of this function and does not need to be backported.] Signed-off-by: Tengda Wu <wutengda2@huawei.com> --- drivers/base/firmware_loader/main.c | 69 ++++++++++++++++++++++++++--- include/linux/firmware.h | 10 +++++ 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 4f4614811d09..6be766f67cbb 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1171,6 +1171,7 @@ EXPORT_SYMBOL(release_firmware); /* Async support */ struct firmware_work { struct work_struct work; + struct list_head list; struct module *module; const char *name; struct device *device; @@ -1179,6 +1180,17 @@ struct firmware_work { u32 opt_flags; }; +static LIST_HEAD(firmware_work_list); +static DEFINE_SPINLOCK(firmware_work_lock); + +static void firmware_work_free(struct firmware_work *fw_work) +{ + put_device(fw_work->device); /* taken in request_firmware_nowait() */ + module_put(fw_work->module); + kfree_const(fw_work->name); + kfree(fw_work); +} + static void request_firmware_work_func(struct work_struct *work) { struct firmware_work *fw_work; @@ -1189,11 +1201,14 @@ static void request_firmware_work_func(struct work_struct *work) _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); /* taken in request_firmware_nowait() */ - - module_put(fw_work->module); - kfree_const(fw_work->name); - kfree(fw_work); + spin_lock_irq(&firmware_work_lock); + if (!list_empty(&fw_work->list)) { + list_del_init(&fw_work->list); + spin_unlock_irq(&firmware_work_lock); + firmware_work_free(fw_work); + return; + } + spin_unlock_irq(&firmware_work_lock); } /** @@ -1226,6 +1241,7 @@ request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context)) { struct firmware_work *fw_work; + unsigned long flags; fw_work = kzalloc(sizeof(struct firmware_work), gfp); if (!fw_work) @@ -1257,11 +1273,54 @@ request_firmware_nowait( get_device(fw_work->device); INIT_WORK(&fw_work->work, request_firmware_work_func); + + spin_lock_irqsave(&firmware_work_lock, flags); + list_add_tail(&fw_work->list, &firmware_work_list); schedule_work(&fw_work->work); + spin_unlock_irqrestore(&firmware_work_lock, flags); + return 0; } EXPORT_SYMBOL(request_firmware_nowait); +/** + * request_firmware_nowait_cancel() - cancel an async firmware request + * @device: device for which the firmware is being loaded + * @context: context passed to request_firmware_nowait() + * @cont: callback passed to request_firmware_nowait() + * + * Cancel a pending request_firmware_nowait() request for @device, @context + * and @cont. If the associated work has already started, this function waits + * until the callback has returned. If the callback has already completed, this + * function does nothing. + * + * This function may sleep. + */ +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ + struct firmware_work *fw_work = NULL; + struct firmware_work *tmp; + + spin_lock_irq(&firmware_work_lock); + list_for_each_entry_reverse(tmp, &firmware_work_list, list) { + if (tmp->device == device && tmp->context == context && + tmp->cont == cont) { + fw_work = tmp; + list_del_init(&fw_work->list); + break; + } + } + spin_unlock_irq(&firmware_work_lock); + + if (!fw_work) + return; + cancel_work_sync(&fw_work->work); + firmware_work_free(fw_work); +} +EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel); + #ifdef CONFIG_FW_CACHE static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 2035a0d9cc86..bc1d02980626 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -104,6 +104,9 @@ int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)); int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); int request_firmware_into_buf(const struct firmware **firmware_p, @@ -143,6 +146,13 @@ static inline int request_firmware_nowait( return -EINVAL; } +static inline void request_firmware_nowait_cancel(struct device *device, + void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ +} + static inline void release_firmware(const struct firmware *fw) { } -- 2.34.1
From: Cássio Gabriel <cassiogabrielcontato@gmail.com> mainline inclusion from mainline-v7.2-rc1 commit 5367e2ad14f0ae9350a7aaf2e77c87de39a43ae9 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18716 CVE: CVE-2026-80872 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- TAS2781 HDA I2C and SPI queue RCA firmware loading from component bind with request_firmware_nowait(). The firmware loader keeps the callback module pinned and holds a device reference, but the callback still uses driver-private HDA state. Component unbind removes controls and DSP state immediately. Later device removal tears down the TAS2781 private data, including codec_lock. If the async firmware callback runs after unbind has started, it can operate on state that is being torn down. Cancel or synchronize the async firmware request before removing controls and DSP state. A queued callback is cancelled, and an already-running callback is allowed to finish before unbind continues. Fixes: 5be27f1e3ec9 ("ALSA: hda/tas2781: Add tas2781 HDA driver") Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com> Reviewed-by: Takashi Iwai <tiwai@suse.de> Acked-by: Danilo Krummrich <dakr@kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-2... Conflicts: sound/pci/hda/tas2781_hda_i2c.c [The conflict arises from the directory of the tas2781_hda_i2c.c file being changed; the code within the file remains unchanged. The other file, tas2781_hda_spi.c (introduced in version v6.14), has not been merged into this version and is therefore not involved.] Signed-off-by: Tengda Wu <wutengda2@huawei.com> --- sound/pci/hda/tas2781_hda_i2c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 980e6104c2f3..e6ebb918e4a2 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -658,6 +658,9 @@ static void tas2781_hda_unbind(struct device *dev, comps->playback_hook = NULL; } + request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv, + tasdev_fw_ready); + tas2781_hda_remove_controls(tas_hda); tasdevice_config_info_remove(tas_hda->priv); -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/27351 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/5EQ... 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/27351 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/5EQ...
participants (2)
-
patchwork bot -
Tengda Wu