summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCássio Gabriel <cassiogabrielcontato@gmail.com>2026-05-11 01:36:37 -0300
committerTakashi Iwai <tiwai@suse.de>2026-05-15 08:51:46 +0200
commit814b2c9b30e56074e11fc0a6e5419b3fee0639bc (patch)
tree02625ee8ad4b983b26d4832b6dfe64db32c7449d
parentdd074f04e04648d89d9d10ae9846cd057c97b385 (diff)
ALSA: usb-audio: qcom: Check offload mapping failures
uaudio_transfer_buffer_setup() calls dma_get_sgtable() and then passes the sg_table to uaudio_iommu_map_xfer_buf() without checking whether sg table construction succeeded. If dma_get_sgtable() fails, the sg_table contents are not valid. uaudio_iommu_map_pa() also ignores iommu_map() failures for the event and transfer rings and still returns the allocated IOVA to the QMI response. That can expose an unmapped IOVA to the audio DSP. For transfer rings, the failed mapping also leaves the IOVA allocator state marked in use. Check both operations. Free the coherent transfer buffer when sg table construction fails, free the sg table when transfer-buffer IOMMU mapping fails, and release the transfer-ring IOVA if iommu_map() fails. Also return the existing event-ring IOVA when the event ring is already mapped, matching the pre-split helper behavior. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Fixes: 44499ecb4f28 ("ALSA: usb: qcom: Fix false-positive address space check") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com> Link: https://patch.msgid.link/20260511-alsa-usb-qcom-offload-map-errors-v1-1-6502695e58bc@gmail.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/qcom/qc_audio_offload.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
index 5f993b88448c..a0009503b2c5 100644
--- a/sound/usb/qcom/qc_audio_offload.c
+++ b/sound/usb/qcom/qc_audio_offload.c
@@ -565,6 +565,7 @@ static unsigned long uaudio_iommu_map_pa(enum mem_type mtype, bool dma_coherent,
unsigned long iova = 0;
bool map = true;
int prot = uaudio_iommu_map_prot(dma_coherent);
+ int ret;
switch (mtype) {
case MEM_EVENT_RING:
@@ -582,10 +583,24 @@ static unsigned long uaudio_iommu_map_pa(enum mem_type mtype, bool dma_coherent,
dev_err(uaudio_qdev->data->dev, "unknown mem type %d\n", mtype);
}
- if (!iova || !map)
+ if (!iova)
return 0;
- iommu_map(uaudio_qdev->data->domain, iova, pa, size, prot, GFP_KERNEL);
+ if (!map)
+ return iova;
+
+ ret = iommu_map(uaudio_qdev->data->domain, iova, pa, size, prot,
+ GFP_KERNEL);
+ if (ret) {
+ dev_err(uaudio_qdev->data->dev,
+ "failed to map %zu bytes at iova 0x%08lx: %d\n",
+ size, iova, ret);
+ if (mtype == MEM_XFER_RING)
+ uaudio_put_iova(iova, size,
+ &uaudio_qdev->xfer_ring_list,
+ &uaudio_qdev->xfer_ring_iova_size);
+ return 0;
+ }
return iova;
}
@@ -1054,15 +1069,17 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
if (!xfer_buf)
return -ENOMEM;
- dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf,
- xfer_buf_dma, len);
+ ret = dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf,
+ xfer_buf_dma, len);
+ if (ret)
+ goto free_xfer_buf;
/* map the physical buffer into sysdev as well */
xfer_buf_dma_sysdev = uaudio_iommu_map_xfer_buf(dma_coherent,
len, &xfer_buf_sgt);
if (!xfer_buf_dma_sysdev) {
ret = -ENOMEM;
- goto unmap_sync;
+ goto free_sgt;
}
mem_info->dma = xfer_buf_dma;
@@ -1073,7 +1090,9 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
return 0;
-unmap_sync:
+free_sgt:
+ sg_free_table(&xfer_buf_sgt);
+free_xfer_buf:
usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_dma);
return ret;