git: 6cba7aec21bc - stable/13 - linuxkpi: Handle direct-mapped addresses in linux_free_kmem()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 09 Jan 2024 18:00:00 UTC
The branch stable/13 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=6cba7aec21bcd957478a987f9391fd33a4babdac
commit 6cba7aec21bcd957478a987f9391fd33a4babdac
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-10-17 14:26:18 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-01-09 17:59:49 +0000
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
(cherry picked from commit 6223d0b67af923f53d962a9bf594dc37004dffe8)
---
sys/compat/linuxkpi/common/src/linux_page.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index d59435ba934f..e732d6ff5047 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -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(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(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