* [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory
@ 2026-05-13 1:02 liulhong617
2026-07-16 14:01 ` Will Deacon
2026-08-06 17:11 ` Will Deacon
0 siblings, 2 replies; 5+ messages in thread
From: liulhong617 @ 2026-05-13 1:02 UTC (permalink / raw)
To: catalin.marinas, will; +Cc: linux-arm-kernel, linux-kernel, liulhong617
From: liulhong617 <liulhong617@gmail.com>
When reserved-memory regions with the "no-map" property are not
page-aligned, the kernel may accidentally map them into the linear
mapping, contradicting the no-map semantics.
The root cause is a mismatch between /proc/iomem's address boundaries
and the actual page table mapping boundaries:
1. /proc/iomem derives its ranges from memblock via
memblock_region_reserved_base_pfn/memblock_region_reserved_end_pfn,
which perform PFN rounding so the displayed boundaries are
page-aligned. This gives the impression that the no-map region
occupies whole pages.
2. However, memblock_mark_nomap() splits memblock.memory regions at
exact byte boundaries (memblock_isolate_range preserves raw DT
base/size with no alignment). When for_each_mem_range iterates the
non-NOMAP regions adjacent to a no-map region, it returns start/end
values that are NOT page-aligned — they are the precise byte
boundaries from the memblock split.
3. These sub-page-aligned values are passed to
__create_pgd_mapping_locked(), which does:
phys &= PAGE_MASK;
addr = virt & PAGE_MASK;
end = PAGE_ALIGN(virt + size);
The downward rounding of phys via PAGE_MASK extends the mapped
range backward into the adjacent no-map region, effectively
including no-map memory in the linear mapping.
For example, with 64K pages, reserved_region@A2000000 (base=0xA2000000,
size=0x8000, no-map) causes for_each_mem_range to return
start=0xA2008000 for the next mappable region. After phys &= PAGE_MASK,
the actual mapping starts at 0xA2000000 — the entire no-map region is
incorrectly mapped.
Fix this by rounding the mappable range inward to PAGE_SIZE boundaries
before passing it to __map_memblock: start is rounded UP and end is
rounded DOWN. This ensures the mapped area never overlaps with adjacent
no-map regions. The cost is at most one page of unmapped gap at each
boundary, which is preferable to violating no-map semantics.
Signed-off-by: liulhong617 <liulhong617@gmail.com>
---
arch/arm64/mm/mmu.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index dd85e093f..bc8ac7622 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1175,6 +1175,20 @@ static void __init map_mem(pgd_t *pgdp)
for_each_mem_range(i, &start, &end) {
if (start >= end)
break;
+ /*
+ * for_each_mem_range may return sub-page-aligned boundaries
+ * after memblock_mark_nomap() splits regions at byte precision.
+ * __create_pgd_mapping_locked aligns phys down to PAGE_MASK,
+ * which could accidentally map no-map memory on the boundary.
+ * Round the mappable range inward: start UP, end DOWN, so
+ * that the mapped area never overlaps with adjacent no-map
+ * regions. The cost is at most one page of unmapped gap at
+ * each boundary.
+ */
+ start = PAGE_ALIGN(start);
+ end = end & PAGE_MASK;
+ if (start >= end)
+ continue;
/*
* The linear map must allow allocation tags reading/writing
* if MTE is present. Otherwise, it has the same attributes as
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory 2026-05-13 1:02 [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory liulhong617 @ 2026-07-16 14:01 ` Will Deacon 2026-08-04 14:24 ` Ard Biesheuvel 2026-08-06 17:11 ` Will Deacon 1 sibling, 1 reply; 5+ messages in thread From: Will Deacon @ 2026-07-16 14:01 UTC (permalink / raw) To: liulhong617, ardb Cc: catalin.marinas, linux-arm-kernel, linux-kernel, liulhong617 [+Ard] On Wed, May 13, 2026 at 09:02:55AM +0800, liulhong617@163.com wrote: > From: liulhong617 <liulhong617@gmail.com> > > When reserved-memory regions with the "no-map" property are not > page-aligned, the kernel may accidentally map them into the linear > mapping, contradicting the no-map semantics. Crikey, I wonder what the semantics are for "no-map" if the region isn't page aligned? If the remaining part of the page is advertised as memory but doesn't have the "no-map" property, then we can't really satisfy what we're being asked to do. > The root cause is a mismatch between /proc/iomem's address boundaries > and the actual page table mapping boundaries: > > 1. /proc/iomem derives its ranges from memblock via > memblock_region_reserved_base_pfn/memblock_region_reserved_end_pfn, > which perform PFN rounding so the displayed boundaries are > page-aligned. This gives the impression that the no-map region > occupies whole pages. > > 2. However, memblock_mark_nomap() splits memblock.memory regions at > exact byte boundaries (memblock_isolate_range preserves raw DT > base/size with no alignment). When for_each_mem_range iterates the > non-NOMAP regions adjacent to a no-map region, it returns start/end > values that are NOT page-aligned — they are the precise byte > boundaries from the memblock split. > > 3. These sub-page-aligned values are passed to > __create_pgd_mapping_locked(), which does: > phys &= PAGE_MASK; > addr = virt & PAGE_MASK; > end = PAGE_ALIGN(virt + size); > The downward rounding of phys via PAGE_MASK extends the mapped > range backward into the adjacent no-map region, effectively > including no-map memory in the linear mapping. > > For example, with 64K pages, reserved_region@A2000000 (base=0xA2000000, > size=0x8000, no-map) causes for_each_mem_range to return > start=0xA2008000 for the next mappable region. After phys &= PAGE_MASK, > the actual mapping starts at 0xA2000000 — the entire no-map region is > incorrectly mapped. > > Fix this by rounding the mappable range inward to PAGE_SIZE boundaries > before passing it to __map_memblock: start is rounded UP and end is > rounded DOWN. This ensures the mapped area never overlaps with adjacent > no-map regions. The cost is at most one page of unmapped gap at each > boundary, which is preferable to violating no-map semantics. > > Signed-off-by: liulhong617 <liulhong617@gmail.com> > --- > arch/arm64/mm/mmu.c | 14 ++++++++++++++ > 1 file changed, 14 insertions(+) > > diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c > index dd85e093f..bc8ac7622 100644 > --- a/arch/arm64/mm/mmu.c > +++ b/arch/arm64/mm/mmu.c > @@ -1175,6 +1175,20 @@ static void __init map_mem(pgd_t *pgdp) > for_each_mem_range(i, &start, &end) { > if (start >= end) > break; > + /* > + * for_each_mem_range may return sub-page-aligned boundaries > + * after memblock_mark_nomap() splits regions at byte precision. > + * __create_pgd_mapping_locked aligns phys down to PAGE_MASK, > + * which could accidentally map no-map memory on the boundary. > + * Round the mappable range inward: start UP, end DOWN, so > + * that the mapped area never overlaps with adjacent no-map > + * regions. The cost is at most one page of unmapped gap at > + * each boundary. > + */ > + start = PAGE_ALIGN(start); > + end = end & PAGE_MASK; > + if (start >= end) > + continue; Maybe I'm over-worrying here, but I've seen firmware describe parts of memory as lots of small, adjacent regions in some cases and so I'm worried we'd fail to map the pages with this change. I'd be more in favour of detecting sub-page sized "no-map" regions that are adjacent to normal memory regions, emitting a warning/firmware taint and then doing... precisely nothing about it. What practical issues are you seeing on your system? Will ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory 2026-07-16 14:01 ` Will Deacon @ 2026-08-04 14:24 ` Ard Biesheuvel 2026-08-06 13:16 ` Will Deacon 0 siblings, 1 reply; 5+ messages in thread From: Ard Biesheuvel @ 2026-08-04 14:24 UTC (permalink / raw) To: Will Deacon, liulhong617 Cc: Catalin Marinas, linux-arm-kernel, linux-kernel, liulhong617 On Thu, 16 Jul 2026, at 17:01, Will Deacon wrote: > [+Ard] > > On Wed, May 13, 2026 at 09:02:55AM +0800, liulhong617@163.com wrote: >> From: liulhong617 <liulhong617@gmail.com> >> >> When reserved-memory regions with the "no-map" property are not >> page-aligned, the kernel may accidentally map them into the linear >> mapping, contradicting the no-map semantics. > > Crikey, I wonder what the semantics are for "no-map" if the region isn't > page aligned? If the remaining part of the page is advertised as memory > but doesn't have the "no-map" property, then we can't really satisfy > what we're being asked to do. > >> The root cause is a mismatch between /proc/iomem's address boundaries >> and the actual page table mapping boundaries: >> >> 1. /proc/iomem derives its ranges from memblock via >> memblock_region_reserved_base_pfn/memblock_region_reserved_end_pfn, >> which perform PFN rounding so the displayed boundaries are >> page-aligned. This gives the impression that the no-map region >> occupies whole pages. >> >> 2. However, memblock_mark_nomap() splits memblock.memory regions at >> exact byte boundaries (memblock_isolate_range preserves raw DT >> base/size with no alignment). When for_each_mem_range iterates the >> non-NOMAP regions adjacent to a no-map region, it returns start/end >> values that are NOT page-aligned — they are the precise byte >> boundaries from the memblock split. >> >> 3. These sub-page-aligned values are passed to >> __create_pgd_mapping_locked(), which does: >> phys &= PAGE_MASK; >> addr = virt & PAGE_MASK; >> end = PAGE_ALIGN(virt + size); >> The downward rounding of phys via PAGE_MASK extends the mapped >> range backward into the adjacent no-map region, effectively >> including no-map memory in the linear mapping. >> >> For example, with 64K pages, reserved_region@A2000000 (base=0xA2000000, >> size=0x8000, no-map) causes for_each_mem_range to return >> start=0xA2008000 for the next mappable region. After phys &= PAGE_MASK, >> the actual mapping starts at 0xA2000000 — the entire no-map region is >> incorrectly mapped. >> >> Fix this by rounding the mappable range inward to PAGE_SIZE boundaries >> before passing it to __map_memblock: start is rounded UP and end is >> rounded DOWN. This ensures the mapped area never overlaps with adjacent >> no-map regions. The cost is at most one page of unmapped gap at each >> boundary, which is preferable to violating no-map semantics. >> >> Signed-off-by: liulhong617 <liulhong617@gmail.com> >> --- >> arch/arm64/mm/mmu.c | 14 ++++++++++++++ >> 1 file changed, 14 insertions(+) >> >> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c >> index dd85e093f..bc8ac7622 100644 >> --- a/arch/arm64/mm/mmu.c >> +++ b/arch/arm64/mm/mmu.c >> @@ -1175,6 +1175,20 @@ static void __init map_mem(pgd_t *pgdp) >> for_each_mem_range(i, &start, &end) { >> if (start >= end) >> break; >> + /* >> + * for_each_mem_range may return sub-page-aligned boundaries >> + * after memblock_mark_nomap() splits regions at byte precision. >> + * __create_pgd_mapping_locked aligns phys down to PAGE_MASK, >> + * which could accidentally map no-map memory on the boundary. >> + * Round the mappable range inward: start UP, end DOWN, so >> + * that the mapped area never overlaps with adjacent no-map >> + * regions. The cost is at most one page of unmapped gap at >> + * each boundary. >> + */ >> + start = PAGE_ALIGN(start); >> + end = end & PAGE_MASK; >> + if (start >= end) >> + continue; > > Maybe I'm over-worrying here, but I've seen firmware describe parts of > memory as lots of small, adjacent regions in some cases and so I'm > worried we'd fail to map the pages with this change. > > I'd be more in favour of detecting sub-page sized "no-map" regions that > are adjacent to normal memory regions, emitting a warning/firmware taint > and then doing... precisely nothing about it. What practical issues are > you seeing on your system? > I think this change is conceptually sound: for_each_mem_range() will present a set of coalesced memory ranges, so any sub-[OS-]page regions will have been stitched together by this time. The only sane way to handle no-map regions is to round them outward, even if this might result in issues if the bootloader is being thick and e.g., puts part of the initramfs (which the kernel assumes is accessible via the linear map) in a region that remains unmapped as a result. I do wonder if it wouldn't be better if __create_pgd_mapping_locked() did inward rounding, or even better, did no rounding at all, and left it to the callers to perform the rounding. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory 2026-08-04 14:24 ` Ard Biesheuvel @ 2026-08-06 13:16 ` Will Deacon 0 siblings, 0 replies; 5+ messages in thread From: Will Deacon @ 2026-08-06 13:16 UTC (permalink / raw) To: Ard Biesheuvel Cc: liulhong617, Catalin Marinas, linux-arm-kernel, linux-kernel, liulhong617 On Tue, Aug 04, 2026 at 05:24:33PM +0300, Ard Biesheuvel wrote: > > On Thu, 16 Jul 2026, at 17:01, Will Deacon wrote: > > [+Ard] > > > > On Wed, May 13, 2026 at 09:02:55AM +0800, liulhong617@163.com wrote: > >> From: liulhong617 <liulhong617@gmail.com> > >> > >> When reserved-memory regions with the "no-map" property are not > >> page-aligned, the kernel may accidentally map them into the linear > >> mapping, contradicting the no-map semantics. > > > > Crikey, I wonder what the semantics are for "no-map" if the region isn't > > page aligned? If the remaining part of the page is advertised as memory > > but doesn't have the "no-map" property, then we can't really satisfy > > what we're being asked to do. > > > >> The root cause is a mismatch between /proc/iomem's address boundaries > >> and the actual page table mapping boundaries: > >> > >> 1. /proc/iomem derives its ranges from memblock via > >> memblock_region_reserved_base_pfn/memblock_region_reserved_end_pfn, > >> which perform PFN rounding so the displayed boundaries are > >> page-aligned. This gives the impression that the no-map region > >> occupies whole pages. > >> > >> 2. However, memblock_mark_nomap() splits memblock.memory regions at > >> exact byte boundaries (memblock_isolate_range preserves raw DT > >> base/size with no alignment). When for_each_mem_range iterates the > >> non-NOMAP regions adjacent to a no-map region, it returns start/end > >> values that are NOT page-aligned — they are the precise byte > >> boundaries from the memblock split. > >> > >> 3. These sub-page-aligned values are passed to > >> __create_pgd_mapping_locked(), which does: > >> phys &= PAGE_MASK; > >> addr = virt & PAGE_MASK; > >> end = PAGE_ALIGN(virt + size); > >> The downward rounding of phys via PAGE_MASK extends the mapped > >> range backward into the adjacent no-map region, effectively > >> including no-map memory in the linear mapping. > >> > >> For example, with 64K pages, reserved_region@A2000000 (base=0xA2000000, > >> size=0x8000, no-map) causes for_each_mem_range to return > >> start=0xA2008000 for the next mappable region. After phys &= PAGE_MASK, > >> the actual mapping starts at 0xA2000000 — the entire no-map region is > >> incorrectly mapped. > >> > >> Fix this by rounding the mappable range inward to PAGE_SIZE boundaries > >> before passing it to __map_memblock: start is rounded UP and end is > >> rounded DOWN. This ensures the mapped area never overlaps with adjacent > >> no-map regions. The cost is at most one page of unmapped gap at each > >> boundary, which is preferable to violating no-map semantics. > >> > >> Signed-off-by: liulhong617 <liulhong617@gmail.com> > >> --- > >> arch/arm64/mm/mmu.c | 14 ++++++++++++++ > >> 1 file changed, 14 insertions(+) > >> > >> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c > >> index dd85e093f..bc8ac7622 100644 > >> --- a/arch/arm64/mm/mmu.c > >> +++ b/arch/arm64/mm/mmu.c > >> @@ -1175,6 +1175,20 @@ static void __init map_mem(pgd_t *pgdp) > >> for_each_mem_range(i, &start, &end) { > >> if (start >= end) > >> break; > >> + /* > >> + * for_each_mem_range may return sub-page-aligned boundaries > >> + * after memblock_mark_nomap() splits regions at byte precision. > >> + * __create_pgd_mapping_locked aligns phys down to PAGE_MASK, > >> + * which could accidentally map no-map memory on the boundary. > >> + * Round the mappable range inward: start UP, end DOWN, so > >> + * that the mapped area never overlaps with adjacent no-map > >> + * regions. The cost is at most one page of unmapped gap at > >> + * each boundary. > >> + */ > >> + start = PAGE_ALIGN(start); > >> + end = end & PAGE_MASK; > >> + if (start >= end) > >> + continue; > > > > Maybe I'm over-worrying here, but I've seen firmware describe parts of > > memory as lots of small, adjacent regions in some cases and so I'm > > worried we'd fail to map the pages with this change. > > > > I'd be more in favour of detecting sub-page sized "no-map" regions that > > are adjacent to normal memory regions, emitting a warning/firmware taint > > and then doing... precisely nothing about it. What practical issues are > > you seeing on your system? > > > > I think this change is conceptually sound: for_each_mem_range() will present > a set of coalesced memory ranges, so any sub-[OS-]page regions will have > been stitched together by this time. Aha, thanks, that's the part I was missing. > I do wonder if it wouldn't be better if __create_pgd_mapping_locked() did > inward rounding, or even better, did no rounding at all, and left it to the > callers to perform the rounding. I can take this patch for now but maybe somebody can have a play with that in the future. Will ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory 2026-05-13 1:02 [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory liulhong617 2026-07-16 14:01 ` Will Deacon @ 2026-08-06 17:11 ` Will Deacon 1 sibling, 0 replies; 5+ messages in thread From: Will Deacon @ 2026-08-06 17:11 UTC (permalink / raw) To: catalin.marinas, liulhong617 Cc: kernel-team, Will Deacon, linux-arm-kernel, linux-kernel, liulhong617 On Wed, 13 May 2026 09:02:55 +0800, liulhong617@163.com wrote: > When reserved-memory regions with the "no-map" property are not > page-aligned, the kernel may accidentally map them into the linear > mapping, contradicting the no-map semantics. > > The root cause is a mismatch between /proc/iomem's address boundaries > and the actual page table mapping boundaries: > > [...] Applied to arm64 (for-next/mm), thanks! [1/1] arm64: mm: fix accidental linear mapping of no-map reserved memory https://git.kernel.org/arm64/c/7ace06a01efa Cheers, -- Will https://fixes.arm64.dev https://next.arm64.dev https://will.arm64.dev ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-06 17:12 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-05-13 1:02 [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory liulhong617 2026-07-16 14:01 ` Will Deacon 2026-08-04 14:24 ` Ard Biesheuvel 2026-08-06 13:16 ` Will Deacon 2026-08-06 17:11 ` Will Deacon
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®