linux/mm/vmalloc.h
Dima Koziuk 101fea09a8 mm: kmsan: fix iounmap metadata teardown
kmsan_vmalloc_to_page_or_null() rejects addresses outside the regular
vmalloc and module ranges, including the shadow and origin addresses
passed by its callers.  As a result, iounmap does not free the metadata
backing pages.  Also, the first iteration of the teardown loop unmaps the
entire metadata range, preventing subsequent iterations from finding their
pages even if the address lookup is fixed.

Factor the page-table walk out of vmalloc_to_page() into
__vmalloc_to_page(), keeping the address check in the public wrapper.  Use
the unchecked helper in kmsan_vmalloc_to_page_or_null() and check for NULL
before converting the returned page to a PFN.  Mark __vmalloc_to_page() as
__always_inline to avoid an additional function call in vmalloc_to_page().

Move metadata teardown into kmsan_iounmap_pages().  Free the backing
blocks while their mappings are still available, then unmap each metadata
range once and flush its TLB entries.

Link: https://lore.kernel.org/20261002200508.546-1-dmytrokoziuk68@gmail.com
Fixes: b073d7f8ae ("mm: kmsan: maintain KMSAN metadata for page operations")
Signed-off-by: Dima Koziuk <dmytrokoziuk68@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Suggested-by: Alexander Potapenko <glider@google.com>
Link: https://lkml.iu.edu/2609.3/12748.html
Cc: Marco Elver <elver@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Uladzislau Rezki (Sony) <urezki@gmail.com>
2026-10-24 01:56:24 -07:00

50 lines
1.4 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* mm-internal APIs for vmalloc
*/
#ifndef __MM_VMALLOC_H
#define __MM_VMALLOC_H
#include <linux/vmalloc.h>
#ifdef CONFIG_MMU
struct page *__vmalloc_to_page(const void *vmalloc_addr);
void __init vmalloc_init(void);
int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,
pgprot_t prot, struct page **pages,
unsigned int page_shift, gfp_t gfp_mask);
unsigned int get_vm_area_page_order(const struct vm_struct *vm);
#else
static inline void vmalloc_init(void) {}
static inline
int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,
pgprot_t prot, struct page **pages,
unsigned int page_shift, gfp_t gfp_mask)
{
return -EINVAL;
}
static inline void vunmap_range_noflush(unsigned long start, unsigned long end)
{
}
#endif
struct vm_struct *__get_vm_area_node(unsigned long size,
unsigned long align, unsigned long shift,
unsigned long vm_flags, unsigned long start,
unsigned long end, int node, gfp_t gfp_mask,
const void *caller);
void clear_vm_uninitialized_flag(struct vm_struct *vm);
int __must_check __vmap_pages_range_noflush(unsigned long addr,
unsigned long end, pgprot_t prot,
struct page **pages, unsigned int page_shift);
void vunmap_range_noflush(unsigned long start, unsigned long end);
void __vunmap_range_noflush(unsigned long start, unsigned long end);
#endif /* __MM_VMALLOC_H */