iommu/dma: Catch scatterlist length overflows

It is possible, when a very large mapping uses only one
scatterlist, that padding overflows scatterlist's length field.
This results in:
 1) silently wrapping the value
 2) smaller than desired mappings produced by iommu_map_sg
 3) leaving mapped bytes in memory (no iommu_unmap)

Address this issue by adding overflow detection for scatterlist
length field.

Fixes: 809eac54cd ("iommu/dma: Implement scatterlist segment merging")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Krzysztof Karas <krzysztof.karas@intel.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Reviewed-by: Sebastian Brzezinka <sebastian.brzezinka@intel.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
This commit is contained in:
Krzysztof Karas 2026-10-02 05:33:20 +00:00 committed by Joerg Roedel
parent 1ae140d0f2
commit 1c8f6308e5

View File

@ -1478,6 +1478,11 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
sg_dma_len(s) = s_length;
s->offset -= s_iova_off;
s_length = iova_align(iovad, s_length + s_iova_off);
if (overflows_type(s_length, s->length)) {
ret = -EOVERFLOW;
goto out_restore_sg;
}
s->length = s_length;
/*
@ -1494,7 +1499,20 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
* time through here (i.e. before it has a meaningful value).
*/
if (pad_len && pad_len < s_length - 1) {
prev->length += pad_len;
unsigned int new_prev_len;
/*
* For large mappings spanning multiple GBs we
* may not be able to fit all needed padding into
* sg->length.
*/
if (check_add_overflow(prev->length, pad_len, &new_prev_len)) {
ret = -EOVERFLOW;
goto out_restore_sg;
}
prev->length = new_prev_len;
iova_len += pad_len;
}