linuxkpi: Handle direct-mapped addresses in linux_free_kmem()

See the analysis in PR 271333.  It is possible for driver code to
allocate a page, store its address as returned by page_address(), then
call free_page() on that address.  On most systems that'll result in the
LinuxKPI calling kmem_free() with a direct-mapped address, which is not
legal.

Fix the problem by making linux_free_kmem() check the address to see
whether it's direct-mapped or not, and handling it appropriately.

PR:		271333, 274515
Reviewed by:	hselasky, bz
Tested by:	trasz
MFC after:	1 week
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D40028
This commit is contained in:
Mark Johnston 2023-10-17 10:26:18 -04:00
parent 56279238b0
commit 6223d0b67a

View file

@ -145,6 +145,14 @@ linux_alloc_pages(gfp_t flags, unsigned int order)
return (page);
}
static void
_linux_free_kmem(vm_offset_t addr, unsigned int order)
{
size_t size = ((size_t)PAGE_SIZE) << order;
kmem_free((void *)addr, size);
}
void
linux_free_pages(struct page *page, unsigned int order)
{
@ -163,7 +171,7 @@ linux_free_pages(struct page *page, unsigned int order)
vaddr = (vm_offset_t)page_address(page);
linux_free_kmem(vaddr, order);
_linux_free_kmem(vaddr, order);
}
}
@ -185,9 +193,17 @@ linux_alloc_kmem(gfp_t flags, unsigned int order)
void
linux_free_kmem(vm_offset_t addr, unsigned int order)
{
size_t size = ((size_t)PAGE_SIZE) << order;
KASSERT((addr & PAGE_MASK) == 0,
("%s: addr %p is not page aligned", __func__, (void *)addr));
kmem_free((void *)addr, size);
if (addr >= VM_MIN_KERNEL_ADDRESS && addr < VM_MAX_KERNEL_ADDRESS) {
_linux_free_kmem(addr, order);
} else {
vm_page_t page;
page = PHYS_TO_VM_PAGE(DMAP_TO_PHYS(addr));
linux_free_pages(page, order);
}
}
static int