Re: [PATCH] arm64: mm: fix accidental linear mapping of no-map reserved memory
Will Deacon <[email protected]>
| Newsgroups | gmane.linux.kernel,gmane.linux.ports.arm.kernel |
|---|---|
| Message-ID | <anSJO36In-3tNQNj@willie-the-truck> |
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, [email protected] wrote: > >> From: liulhong617 <[email protected]> > >> > >> 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 <[email protected]> > >> --- > >> 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