* [PATCH] arm64: mm: validate /dev/mem mmap ranges without overflow
@ 2026-11-01 17:10 Zhao Gongyi
2026-11-03 15:08 ` Catalin Marinas
0 siblings, 1 reply; 2+ messages in thread
From: Zhao Gongyi @ 2026-11-01 17:10 UTC (permalink / raw)
To: Catalin Marinas
Cc: Will Deacon, Mark Rutland, linux-arm-kernel, linux-kernel, Zhao Gongyi
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@vger.kernel.org
Signed-off-by: Zhao Gongyi <zhaogongyi@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)
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] arm64: mm: validate /dev/mem mmap ranges without overflow
2026-11-01 17:10 [PATCH] arm64: mm: validate /dev/mem mmap ranges without overflow Zhao Gongyi
@ 2026-11-03 15:08 ` Catalin Marinas
0 siblings, 0 replies; 2+ messages in thread
From: Catalin Marinas @ 2026-11-03 15:08 UTC (permalink / raw)
To: Zhao Gongyi; +Cc: Will Deacon, Mark Rutland, linux-arm-kernel, linux-kernel
On Mon, Nov 02, 2026 at 01:10:30AM +0800, Zhao Gongyi wrote:
> 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@vger.kernel.org
> Signed-off-by: Zhao Gongyi <zhaogongyi@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);
> }
Can we write this as:
int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
{
phys_addr_t limit = PHYS_MASK + 1;
return size <= limit && pfn <= (limit - size) >> PAGE_SHIFT;
}
It would resemble the generic __access_ok() better where we need to
check for similar overflow. I find it easier to read.
(happy to make the change when applying)
--
Catalin
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-03 15:08 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-01 17:10 [PATCH] arm64: mm: validate /dev/mem mmap ranges without overflow Zhao Gongyi
2026-11-03 15:08 ` Catalin Marinas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®