iommu: Add page_ext for IOMMU_DEBUG_PAGEALLOC

Add a new config IOMMU_DEBUG_PAGEALLOC, which registers new data to
page_ext.

This config will be used by the IOMMU API to track pages mapped in
the IOMMU to catch drivers trying to free kernel memory that they
still map in their domains, causing all types of memory corruption.

This behaviour is disabled by default and can be enabled using
kernel cmdline iommu.debug_pagealloc.

Acked-by: David Hildenbrand (Red Hat) <david@kernel.org>
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
This commit is contained in:
Mostafa Saleh 2026-01-09 17:18:02 +00:00 committed by Joerg Roedel
parent f5b16b8021
commit 466ae6978a
6 changed files with 82 additions and 0 deletions

View file

@ -2675,6 +2675,15 @@ Kernel parameters
1 - Bypass the IOMMU for DMA.
unset - Use value of CONFIG_IOMMU_DEFAULT_PASSTHROUGH.
iommu.debug_pagealloc=
[KNL,EARLY] When CONFIG_IOMMU_DEBUG_PAGEALLOC is set, this
parameter enables the feature at boot time. By default, it
is disabled and the system behaves the same way as a kernel
built without CONFIG_IOMMU_DEBUG_PAGEALLOC.
Format: { "0" | "1" }
0 - Sanitizer disabled.
1 - Sanitizer enabled, expect runtime overhead.
io7= [HW] IO7 for Marvel-based Alpha systems
See comment before marvel_specify_io7 in
arch/alpha/kernel/core_marvel.c.

View file

@ -384,6 +384,25 @@ config SPRD_IOMMU
Say Y here if you want to use the multimedia devices listed above.
config IOMMU_DEBUG_PAGEALLOC
bool "Debug IOMMU mappings against page allocations"
depends on DEBUG_PAGEALLOC && IOMMU_API && PAGE_EXTENSION
help
This enables a consistency check between the kernel page allocator and
the IOMMU subsystem. It verifies that pages being allocated or freed
are not currently mapped in any IOMMU domain.
This helps detect DMA use-after-free bugs where a driver frees a page
but forgets to unmap it from the IOMMU, potentially allowing a device
to overwrite memory that the kernel has repurposed.
These checks are best-effort and may not detect all problems.
Due to performance overhead, this feature is disabled by default.
You must enable "iommu.debug_pagealloc" from the kernel command
line to activate the runtime checks.
If unsure, say N.
endif # IOMMU_SUPPORT
source "drivers/iommu/generic_pt/Kconfig"

View file

@ -36,3 +36,4 @@ obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
obj-$(CONFIG_APPLE_DART) += apple-dart.o
obj-$(CONFIG_IOMMU_DEBUG_PAGEALLOC) += iommu-debug-pagealloc.o

View file

@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 - Google Inc
* Author: Mostafa Saleh <smostafa@google.com>
* IOMMU API debug page alloc sanitizer
*/
#include <linux/atomic.h>
#include <linux/iommu-debug-pagealloc.h>
#include <linux/kernel.h>
#include <linux/page_ext.h>
static bool needed;
struct iommu_debug_metadata {
atomic_t ref;
};
static __init bool need_iommu_debug(void)
{
return needed;
}
struct page_ext_operations page_iommu_debug_ops = {
.size = sizeof(struct iommu_debug_metadata),
.need = need_iommu_debug,
};
static int __init iommu_debug_pagealloc(char *str)
{
return kstrtobool(str, &needed);
}
early_param("iommu.debug_pagealloc", iommu_debug_pagealloc);

View file

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 - Google Inc
* Author: Mostafa Saleh <smostafa@google.com>
* IOMMU API debug page alloc sanitizer
*/
#ifndef __LINUX_IOMMU_DEBUG_PAGEALLOC_H
#define __LINUX_IOMMU_DEBUG_PAGEALLOC_H
#ifdef CONFIG_IOMMU_DEBUG_PAGEALLOC
extern struct page_ext_operations page_iommu_debug_ops;
#endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */
#endif /* __LINUX_IOMMU_DEBUG_PAGEALLOC_H */

View file

@ -11,6 +11,7 @@
#include <linux/page_table_check.h>
#include <linux/rcupdate.h>
#include <linux/pgalloc_tag.h>
#include <linux/iommu-debug-pagealloc.h>
/*
* struct page extension
@ -89,6 +90,9 @@ static struct page_ext_operations *page_ext_ops[] __initdata = {
#ifdef CONFIG_PAGE_TABLE_CHECK
&page_table_check_ops,
#endif
#ifdef CONFIG_IOMMU_DEBUG_PAGEALLOC
&page_iommu_debug_ops,
#endif
};
unsigned long page_ext_size;