From e873c74132f0c5f1452816cd9bb26208f0bba1e1 Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Sat, 22 Aug 2026 19:22:05 +0000 Subject: dmaengine: fix use-after-free in dma_chan_put() and dma_release_channel() When dma_device_put() drops the last reference on chan->device->ref, dma_device_release() runs and may free the dma_device along with its channels. dma_chan_put() then still reads chan->device->owner via dma_chan_to_owner() for the trailing module_put(). KASAN catches it: slab-use-after-free in dma_chan_put+0x3e6/0x4c0 Read of size 8 by task insmod/6319 Freed by task 6319: kfree+0x225/0x470 dma_chan_put+0x395/0x4c0 dmaengine_put+0xf8/0x160 Cache the module owner in dma_chan_put() before the put so the trailing module_put() does not need chan->device. Fixes: 8ad342a86359 ("dmaengine: Add reference counting to dma_device struct") Suggested-by: Sashiko Link: https://sashiko.dev/#/patchset/20260518-dmaengine-kref-fix-v1-1-4d6125048fb7@amd.com Reviewed-by: Frank Li Reviewed-by: Logan Gunthorpe Signed-off-by: Shivank Garg Link: https://patch.msgid.link/20260822-dmaengine-kref-fix-v5-3-d4a4ee47d927@amd.com Signed-off-by: Vinod Koul --- sound/soc/sof/loader.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 sound/soc/sof/loader.c (limited to 'sound/soc/sof/loader.c') diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c new file mode 100644 index 000000000..0baf316b0 --- /dev/null +++ b/sound/soc/sof/loader.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018 Intel Corporation +// +// Author: Liam Girdwood +// +// Generic firmware loader. +// + +#include +#include "sof-priv.h" +#include "ops.h" + +int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const char *fw_filename; + ssize_t ext_man_size; + int ret; + + /* Don't request firmware again if firmware is already requested */ + if (sdev->basefw.fw) + return 0; + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + plat_data->fw_filename); + if (!fw_filename) + return -ENOMEM; + + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); + + if (ret < 0) { + dev_err(sdev->dev, + "error: sof firmware file is missing, you might need to\n"); + dev_err(sdev->dev, + " download it from https://github.com/thesofproject/sof-bin/\n"); + goto err; + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", + fw_filename); + } + + /* check for extended manifest */ + ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); + if (ext_man_size > 0) { + /* when no error occurred, drop extended manifest */ + sdev->basefw.payload_offset = ext_man_size; + } else if (!ext_man_size) { + /* No extended manifest, so nothing to skip during FW load */ + dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); + } else { + ret = ext_man_size; + dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", + fw_filename, ret); + } + +err: + kfree(fw_filename); + + return ret; +} +EXPORT_SYMBOL(snd_sof_load_firmware_raw); + +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) +{ + int ret; + + ret = snd_sof_load_firmware_raw(sdev); + if (ret < 0) + return ret; + + /* make sure the FW header and file is valid */ + ret = sdev->ipc->ops->fw_loader->validate(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW header\n"); + goto error; + } + + /* prepare the DSP for FW loading */ + ret = snd_sof_dsp_reset(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + goto error; + } + + /* parse and load firmware modules to DSP */ + if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { + ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); + if (ret < 0) { + dev_err(sdev->dev, "Firmware loading failed\n"); + goto error; + } + } + + return 0; + +error: + release_firmware(sdev->basefw.fw); + sdev->basefw.fw = NULL; + return ret; + +} +EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); + +int snd_sof_run_firmware(struct snd_sof_dev *sdev) +{ + int ret; + + init_waitqueue_head(&sdev->boot_wait); + + /* (re-)enable dsp dump */ + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + + /* create read-only fw_version debugfs to store boot version info */ + if (sdev->first_boot) { + ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, + sizeof(sdev->fw_version), + "fw_version", 0444); + /* errors are only due to memory allocation, not debugfs */ + if (ret < 0) { + dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); + return ret; + } + } + + /* perform pre fw run operations */ + ret = snd_sof_dsp_pre_fw_run(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed pre fw run op\n"); + return ret; + } + + dev_dbg(sdev->dev, "booting DSP firmware\n"); + + /* boot the firmware on the DSP */ + ret = snd_sof_dsp_run(sdev); + if (ret < 0) { + snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", + SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); + return ret; + } + + /* + * now wait for the DSP to boot. There are 3 possible outcomes: + * 1. Boot wait times out indicating FW boot failure. + * 2. FW boots successfully and fw_ready op succeeds. + * 3. FW boots but fw_ready op fails. + */ + ret = wait_event_timeout(sdev->boot_wait, + sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, + msecs_to_jiffies(sdev->boot_timeout)); + if (ret == 0) { + snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | + SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); + return -EIO; + } + + if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) + return -EIO; /* FW boots but fw_ready op failed */ + + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + + /* perform post fw run operations */ + ret = snd_sof_dsp_post_fw_run(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed post fw run op\n"); + return ret; + } + + if (sdev->ipc->ops->post_fw_boot) + return sdev->ipc->ops->post_fw_boot(sdev); + + return 0; +} +EXPORT_SYMBOL(snd_sof_run_firmware); + +void snd_sof_fw_unload(struct snd_sof_dev *sdev) +{ + /* TODO: support module unloading at runtime */ + release_firmware(sdev->basefw.fw); + sdev->basefw.fw = NULL; +} +EXPORT_SYMBOL(snd_sof_fw_unload); -- cgit v1.3.1