[PATCH] arm64: mm: validate /dev/mem mmap ranges without overflow
Zhao Gongyi
zhaogongyi at bytedance.com
Sun Nov 1 09:10:30 PST 2026
An arm64 syzkaller reproducer creates a FUSE character device with
major:minor 1:1, so opening it uses the /dev/mem file operations. It
maps one page from offset 0xfffffffffffff000 at user address zero.
The next openat() with a NULL pathname triggers a synchronous external
abort (SEA, ESR 0x96000010) in strncpy_from_user() via do_getname(),
panicking the kernel.
valid_mmap_phys_addr_range() checks the exclusive end by adding the
mapping size to the starting address. For this offset, the sum wraps
to zero and passes the PHYS_MASK test, allowing an out-of-range PFN to
reach remap_pfn_range(). The inclusive-end overflow check in
mmap_mem_prepare() does not catch it because the end is ULONG_MAX.
Reject starting PFNs outside PHYS_MASK and compare the inclusive length
against the remaining physical range without overflowing. This also
allows mappings that end exactly at PHYS_MASK, which the old check
incorrectly rejects.
Fixes: 1d18c47c735e ("arm64: MMU fault handling and page table management")
Cc: stable at vger.kernel.org
Signed-off-by: Zhao Gongyi <zhaogongyi at bytedance.com>
---
Tested on the base commit with QEMU TCG and 52-bit LPA2:
the overflowing one-page mapping changes from success to -EINVAL,
while the last valid one- and two-page mappings change from -EINVAL to
success. A direct /dev/mem mmap followed by openat(NULL) panics before
this change; with the change, mmap fails with -EINVAL and openat()
returns -EFAULT.
arch/arm64/mm/mmap.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 32e0771..d6286bc 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -61,7 +61,12 @@ int valid_phys_addr_range(phys_addr_t addr, size_t size)
*/
int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
{
- return !(((pfn << PAGE_SHIFT) + size) & ~PHYS_MASK);
+ const phys_addr_t max_addr = PHYS_MASK;
+
+ if (!size || pfn > (max_addr >> PAGE_SHIFT))
+ return 0;
+
+ return size - 1 <= max_addr - (pfn << PAGE_SHIFT);
}
static int __init adjust_protection_map(void)
base-commit: cbbddb6b08cc9900a87cccaa9b22bbe06aa4f50a
--
2.39.5 (Apple Git-154)
More information about the linux-arm-kernel
mailing list