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: 809eac54cdd6 ("iommu/dma: Implement scatterlist segment merging")
Cc: [email protected]
Assisted-by: LLM
Signed-off-by: Krzysztof Karas <[email protected]>
Reviewed-by: Robin Murphy <[email protected]>
---
This patch was previously part of "drivers: Improve memory
management for large object allocations when i915/shmem is used
with iommu" series, but was extracted and posted separately on
request of one of the reviewers (Andi Shyti). There have been no
functional changes since last version.

this version:
 * Added separating blank lines (Andi);
 * Added IOMMU maintainers suggested by get_maintainer.pl
   script to CC (Andi);

 drivers/iommu/dma-iommu.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd43..06edb4056dc1b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1477,6 +1477,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;
 
                /*
@@ -1493,7 +1498,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;
                }
 
-- 
2.34.1

Reply via email to