mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®