mm: Enforce VM_IOREMAP flag and range in ioremap_page_range.
authorAlexei Starovoitov <ast@kernel.org>
Tue, 5 Mar 2024 03:05:15 +0000 (19:05 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Wed, 6 Mar 2024 18:19:04 +0000 (10:19 -0800)
There are various users of get_vm_area() + ioremap_page_range() APIs.
Enforce that get_vm_area() was requested as VM_IOREMAP type and range
passed to ioremap_page_range() matches created vm_area to avoid
accidentally ioremap-ing into wrong address range.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/bpf/20240305030516.41519-2-alexei.starovoitov@gmail.com
mm/vmalloc.c

index d12a17fc0c171cc40ad86bfe4b40d5badd1c0ee9..f42f98a127d56e269d833cf5f34f02f8ab24491b 100644 (file)
@@ -307,8 +307,21 @@ static int vmap_range_noflush(unsigned long addr, unsigned long end,
 int ioremap_page_range(unsigned long addr, unsigned long end,
                phys_addr_t phys_addr, pgprot_t prot)
 {
+       struct vm_struct *area;
        int err;
 
+       area = find_vm_area((void *)addr);
+       if (!area || !(area->flags & VM_IOREMAP)) {
+               WARN_ONCE(1, "vm_area at addr %lx is not marked as VM_IOREMAP\n", addr);
+               return -EINVAL;
+       }
+       if (addr != (unsigned long)area->addr ||
+           (void *)end != area->addr + get_vm_area_size(area)) {
+               WARN_ONCE(1, "ioremap request [%lx,%lx) doesn't match vm_area [%lx, %lx)\n",
+                         addr, end, (long)area->addr,
+                         (long)area->addr + get_vm_area_size(area));
+               return -ERANGE;
+       }
        err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
                                 ioremap_max_page_shift);
        flush_cache_vmap(addr, end);