On Mon, Jul 19, 2021 at 8:31 PM Will Deacon <w...@kernel.org> wrote:
>
> Since commit 69031f500865 ("swiotlb: Set dev->dma_io_tlb_mem to the
> swiotlb pool used"), 'struct device' may hold a copy of the global
> 'io_default_tlb_mem' pointer if the device is using swiotlb for DMA. A
> subsequent call to swiotlb_exit() will therefore leave dangling pointers
> behind in these device structures, resulting in KASAN splats such as:
>
>   |  BUG: KASAN: use-after-free in __iommu_dma_unmap_swiotlb+0x64/0xb0
>   |  Read of size 8 at addr ffff8881d7830000 by task swapper/0/0
>   |
>   |  CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.12.0-rc3-debug #1
>   |  Hardware name: HP HP Desktop M01-F1xxx/87D6, BIOS F.12 12/17/2020
>   |  Call Trace:
>   |   <IRQ>
>   |   dump_stack+0x9c/0xcf
>   |   print_address_description.constprop.0+0x18/0x130
>   |   kasan_report.cold+0x7f/0x111
>   |   __iommu_dma_unmap_swiotlb+0x64/0xb0
>   |   nvme_pci_complete_rq+0x73/0x130
>   |   blk_complete_reqs+0x6f/0x80
>   |   __do_softirq+0xfc/0x3be
>
> Point 'io_default_tlb_mem' at a static structure, so that the per-device
> pointers remain valid after swiotlb_exit() has been invoked. The 'slots'
> array is still allocated dynamically and referenced via a pointer rather
> than a flexible array member.
>
> Cc: Claire Chang <tien...@chromium.org>
> Cc: Christoph Hellwig <h...@lst.de>
> Cc: Robin Murphy <robin.mur...@arm.com>
> Cc: Konrad Rzeszutek Wilk <konrad.w...@oracle.com>
> Fixes: 69031f500865 ("swiotlb: Set dev->dma_io_tlb_mem to the swiotlb pool 
> used")
> Reported-by: Nathan Chancellor <nat...@kernel.org>
> Tested-by: Nathan Chancellor <nat...@kernel.org>

Tested-by: Claire Chang <tien...@chromium.org>

> Signed-off-by: Will Deacon <w...@kernel.org>
> ---
>  include/linux/swiotlb.h |  2 +-
>  kernel/dma/swiotlb.c    | 34 +++++++++++++++++++++-------------
>  2 files changed, 22 insertions(+), 14 deletions(-)
>
> diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
> index 39284ff2a6cd..d3b617c19045 100644
> --- a/include/linux/swiotlb.h
> +++ b/include/linux/swiotlb.h
> @@ -103,7 +103,7 @@ struct io_tlb_mem {
>                 phys_addr_t orig_addr;
>                 size_t alloc_size;
>                 unsigned int list;
> -       } slots[];
> +       } *slots;
>  };
>  extern struct io_tlb_mem *io_tlb_default_mem;
>
> diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
> index f1a9ae7fad8f..992d73cdc944 100644
> --- a/kernel/dma/swiotlb.c
> +++ b/kernel/dma/swiotlb.c
> @@ -71,6 +71,7 @@
>  enum swiotlb_force swiotlb_force;
>
>  struct io_tlb_mem *io_tlb_default_mem;
> +static struct io_tlb_mem _io_tlb_default_mem;
>
>  /*
>   * Max segment that we can provide which (if pages are contingous) will
> @@ -201,7 +202,7 @@ static void swiotlb_init_io_tlb_mem(struct io_tlb_mem 
> *mem, phys_addr_t start,
>
>  int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int 
> verbose)
>  {
> -       struct io_tlb_mem *mem;
> +       struct io_tlb_mem *mem = &_io_tlb_default_mem;
>         size_t alloc_size;
>
>         if (swiotlb_force == SWIOTLB_NO_FORCE)
> @@ -211,9 +212,9 @@ int __init swiotlb_init_with_tbl(char *tlb, unsigned long 
> nslabs, int verbose)
>         if (WARN_ON_ONCE(io_tlb_default_mem))
>                 return -ENOMEM;
>
> -       alloc_size = PAGE_ALIGN(struct_size(mem, slots, nslabs));
> -       mem = memblock_alloc(alloc_size, PAGE_SIZE);
> -       if (!mem)
> +       alloc_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), nslabs));
> +       mem->slots = memblock_alloc(alloc_size, PAGE_SIZE);
> +       if (!mem->slots)
>                 panic("%s: Failed to allocate %zu bytes align=0x%lx\n",
>                       __func__, alloc_size, PAGE_SIZE);
>
> @@ -304,7 +305,7 @@ swiotlb_late_init_with_default_size(size_t default_size)
>  int
>  swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs)
>  {
> -       struct io_tlb_mem *mem;
> +       struct io_tlb_mem *mem = &_io_tlb_default_mem;
>         unsigned long bytes = nslabs << IO_TLB_SHIFT;
>
>         if (swiotlb_force == SWIOTLB_NO_FORCE)
> @@ -314,12 +315,11 @@ swiotlb_late_init_with_tbl(char *tlb, unsigned long 
> nslabs)
>         if (WARN_ON_ONCE(io_tlb_default_mem))
>                 return -ENOMEM;
>
> -       mem = (void *)__get_free_pages(GFP_KERNEL,
> -               get_order(struct_size(mem, slots, nslabs)));
> -       if (!mem)
> +       mem->slots = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
> +               get_order(array_size(sizeof(*mem->slots), nslabs)));
> +       if (!mem->slots)
>                 return -ENOMEM;
>
> -       memset(mem, 0, sizeof(*mem));
>         set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT);
>         swiotlb_init_io_tlb_mem(mem, virt_to_phys(tlb), nslabs, true);
>
> @@ -337,12 +337,13 @@ void __init swiotlb_exit(void)
>         if (!mem)
>                 return;
>
> -       size = struct_size(mem, slots, mem->nslabs);
> +       size = array_size(sizeof(*mem->slots), mem->nslabs);
>         if (mem->late_alloc)
> -               free_pages((unsigned long)mem, get_order(size));
> +               free_pages((unsigned long)mem->slots, get_order(size));
>         else
> -               memblock_free_late(__pa(mem), PAGE_ALIGN(size));
> +               memblock_free_late(__pa(mem->slots), PAGE_ALIGN(size));
>         io_tlb_default_mem = NULL;
> +       memset(mem, 0, sizeof(*mem));
>  }
>
>  /*
> @@ -783,10 +784,17 @@ static int rmem_swiotlb_device_init(struct reserved_mem 
> *rmem,
>          * to it.
>          */
>         if (!mem) {
> -               mem = kzalloc(struct_size(mem, slots, nslabs), GFP_KERNEL);
> +               mem = kzalloc(sizeof(*mem), GFP_KERNEL);
>                 if (!mem)
>                         return -ENOMEM;
>
> +               mem->slots = kzalloc(array_size(sizeof(*mem->slots), nslabs),
> +                                    GFP_KERNEL);
> +               if (!mem->slots) {
> +                       kfree(mem);
> +                       return -ENOMEM;
> +               }
> +
>                 set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
>                                      rmem->size >> PAGE_SHIFT);
>                 swiotlb_init_io_tlb_mem(mem, rmem->base, nslabs, false);
> --
> 2.32.0.402.g57bb445576-goog
>
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to