Re: [PATCH] Fix incorrect flush address in direct page table reclaim
Qi Zheng <[email protected]> Tue, 4 Aug 2026 17:25:33 +0800
| Newsgroups | gmane.linux.kernel.stable,gmane.linux.kernel.mm |
|---|---|
| Message-ID | <[email protected]> |
On 8/4/26 5:05 PM, David Hildenbrand (Arm) wrote: > On 8/4/26 02:37, [email protected] wrote: >> From: Andy Lutomirski <[email protected]> >> >> When zap_pte_range reclaims a page table, it does: >> >> pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); >> >> and this is unconditionally wrong: if this code executes, addr *always* >> points one past the end of the range covered by the table. The addr >> parameter is used to flush the TLB (really the paging-structure-cache) >> to drop references to the to-be-freed table, and any architecture that >> cares about the parameter will flush the wrong address. (But they'll >> still free the correct page). >> >> I think it's worth contemplating why the kernel works at all. > > Note that this only triggers when someone does e.g., a MADV_DONTNEED over > a large enough range (covering at least a full PTE table). > > So this isn't the ordinary munmap()/exit() page table reclaim code. > > I'm still surprised that it took so long to show up; likely we need more > targeted tests for PT_RECLAIM that We backported the PT_RECLAIM to our internal tree a while ago (excluding the rework patch being fixed here), and it has been running stably ever since. > >> >> If we hit the offending line of code, we will first clear the PMD entry >> (line 1954, zap_empty_pte_table), then we will issue pending flushes if >> force_flush is set (tlb_flush_mmu_tlbonly(tlb)), then we will skip the >> retry on line 1979 (phew!), and then we will do the offending >> pte_free_tlb call. *Or* we will clear the PMD entry immediately before >> pte_free_tlb (line 1983, zap_pte_table_if_empty). >> >> If we have any pending flushes (i.e. we actually zapped any last-level >> entries) at the time we clear the PMD entry, then the flush really ought >> to flush all references to the table (Linus certainly seems to think it >> will on all architectures [0]). >> >> The condition under which we have no accumulated flushes at the time of >> the clear is very complex (the whole zap_pte_range function has absurdly >> complex control flow). If we do hit the bad case, then we will end up >> clearing the PMD entry after the last time the range is flushed, and any >> CPU is free to cache a reference to the (empty) page table. If this >> happens due to an ordinary read or write, it would segfault, so it would >> be rare. But the cache could be speculatively filled as well. Then >> we'll flush the wrong address and then free and possibly reuse the >> table. >> >> On x86, even flushing the wrong address works on non-KPTI Intel systems >> because INVLPG flushes *all* paging-structure-caches, not just the ones >> for the target address. But INVPCID does not, and flush_tlb_one_user >> will use INVPCID if it's available. And then we're toast. AMD systems >> are more susceptible: we set the EFER.TCE bit, which makes even INVLPG >> only flush the target address. >> >> P.S. IMO zap_pte_range is a mess. The control flow is excessively >> complex. The direct_reclaim variable itself has a confused meaning -- >> for the first part of the function it means, approximately, "we should >> free the table if can_reclaim_pt". But, later on, it means "we ALREADY >> reclaimed the table". And the goto retry on line 1979 is IMO just >> asking for trouble if the condition ever changes such that it might >> happen after clearing the PMD. > > There is certainly room for improvement :) > > This should, however, not be part of the patch description (best be had below the ---) > >> >> I think this might fix an issue in ripgrep reported here: >> https://github.com/BurntSushi/ripgrep/issues/3494 >> >> [0] https://lore.kernel.org/all/CA+55aFzBggoXtNXQeng5d_mRoDnaMBE5Y+URs+PHR67nUpMtaw@mail.gmail.com/T/#u >> >> Fixes: 4c640eb4181c ("mm: move pte table reclaim code to memory.c") >> Cc: David Hildenbrand (Red Hat) <[email protected]> >> Cc: Qi Zheng <[email protected]> >> Cc: Liam Howlett <[email protected]> >> Cc: "Liam R. Howlett" <[email protected]> >> Cc: Lorenzo Stoakes <[email protected]> >> Cc: Michal Hocko <[email protected]> >> Cc: Mike Rapoport <[email protected]> >> Cc: Suren Baghdasaryan <[email protected]> >> Cc: Vlastimil Babka <[email protected]> >> Cc: Andrew Morton <[email protected]>.org> >> Cc: [email protected] >> Signed-off-by: Andy Lutomirski <[email protected]> >> --- >> mm/memory.c | 2 +- >> 1 file changed, 1 insertion(+), 1 deletion(-) >> Acked-by: Qi Zheng <[email protected]> Thanks, Qi