| From f95dca6c6b4292065a81946071f5ca02f4aba058 Mon Sep 17 00:00:00 2001 |
| From: Kevin Grandemange <kevin.grandemange@allegrodvt.com> |
| Date: Thu, 12 Mar 2020 15:41:45 +0000 |
| Subject: [PATCH] dma-coherent: fix integer overflow in the reserved-memory dma |
| allocation |
| |
| commit 286c21de32b904131f8cf6a36ce40b8b0c9c5da3 upstream. |
| |
| 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.grandemange@allegrodvt.com> |
| Signed-off-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c |
| index 99b21efe2649..3fe6e0119ee6 100644 |
| --- a/kernel/dma/coherent.c |
| +++ b/kernel/dma/coherent.c |
| @@ -146,7 +146,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); |
| @@ -156,8 +156,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; |
| @@ -206,7 +207,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; |
| |
| @@ -250,10 +251,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.7.4 |
| |