[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