From: Kevin Grandemange <kevin.grandema...@allegrodvt.com>

[ Upstream commit 286c21de32b904131f8cf6a36ce40b8b0c9c5da3 ]

pageno is an int and the PAGE_SHIFT shift is done on an int,
overflowing if the memory is bigger than 2G

This can be reproduced using for example a reserved-memory of 4G

reserved-memory {
                    #address-cells = <2>;
                    #size-cells = <2>;
                    ranges;

                    reserved_dma: buffer@0 {
                        compatible = "shared-dma-pool";
                        no-map;
                        reg = <0x5 0x00000000 0x1 0x0>;
        };
};

Signed-off-by: Kevin Grandemange <kevin.grandema...@allegrodvt.com>
Signed-off-by: Christoph Hellwig <h...@lst.de>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 kernel/dma/coherent.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 551b0eb7028a3..2a0c4985f38e4 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -134,7 +134,7 @@ static void *__dma_alloc_from_coherent(struct device *dev,
 
        spin_lock_irqsave(&mem->spinlock, flags);
 
-       if (unlikely(size > (mem->size << PAGE_SHIFT)))
+       if (unlikely(size > ((dma_addr_t)mem->size << PAGE_SHIFT)))
                goto err;
 
        pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
@@ -144,8 +144,9 @@ static void *__dma_alloc_from_coherent(struct device *dev,
        /*
         * Memory was found in the coherent area.
         */
-       *dma_handle = dma_get_device_base(dev, mem) + (pageno << PAGE_SHIFT);
-       ret = mem->virt_base + (pageno << PAGE_SHIFT);
+       *dma_handle = dma_get_device_base(dev, mem) +
+                       ((dma_addr_t)pageno << PAGE_SHIFT);
+       ret = mem->virt_base + ((dma_addr_t)pageno << PAGE_SHIFT);
        spin_unlock_irqrestore(&mem->spinlock, flags);
        memset(ret, 0, size);
        return ret;
@@ -194,7 +195,7 @@ static int __dma_release_from_coherent(struct 
dma_coherent_mem *mem,
                                       int order, void *vaddr)
 {
        if (mem && vaddr >= mem->virt_base && vaddr <
-                  (mem->virt_base + (mem->size << PAGE_SHIFT))) {
+                  (mem->virt_base + ((dma_addr_t)mem->size << PAGE_SHIFT))) {
                int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
                unsigned long flags;
 
@@ -238,10 +239,10 @@ static int __dma_mmap_from_coherent(struct 
dma_coherent_mem *mem,
                struct vm_area_struct *vma, void *vaddr, size_t size, int *ret)
 {
        if (mem && vaddr >= mem->virt_base && vaddr + size <=
-                  (mem->virt_base + (mem->size << PAGE_SHIFT))) {
+                  (mem->virt_base + ((dma_addr_t)mem->size << PAGE_SHIFT))) {
                unsigned long off = vma->vm_pgoff;
                int start = (vaddr - mem->virt_base) >> PAGE_SHIFT;
-               int user_count = vma_pages(vma);
+               unsigned long user_count = vma_pages(vma);
                int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 
                *ret = -ENXIO;
-- 
2.20.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to