git: 4862eb8604d5 - stable/14 - linuxkpi: Handle direct-mapped addresses in linux_free_kmem()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 24 Oct 2023 13:38:26 UTC
The branch stable/14 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=4862eb8604d503b52e7c3aa7ff32155b75a1ff93 commit 4862eb8604d503b52e7c3aa7ff32155b75a1ff93 Author: Mark Johnston <markj@FreeBSD.org> AuthorDate: 2023-10-17 14:26:18 +0000 Commit: Mark Johnston <markj@FreeBSD.org> CommitDate: 2023-10-24 13:20:01 +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 ce9ad34464bd..21e338acb089 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((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