Re: [PATCH] LoongArch: fix silent hang on kernel exec into an NX page
Huacai Chen <[email protected]> Thu, 16 Jul 2026 14:24:39 +0800
| Newsgroups | dev.linux.lists.loongarch,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAAhV-H41ZG=yh7jOmsHGTrh2RBy9PeiqckQsW9bdhRfYkYkQzw@mail.gmail.com> |
Hi, Song, On Thu, Jul 16, 2026 at 11:02 AM Song Hu <[email protected]> wrote: > > spurious_fault() returns 1 for any non-write fault on a present page > without checking the access permission, so a kernel instruction fetch into > a present NX page (TLBNX) is treated as spurious and the faulting > instruction re-executes forever -- a silent hang that bypasses > fixup_exception() and the oops path. Can you give a method to reproduce this hang? > > Recover the access type from the exception code: an exec fault (TLBNX) is > spurious only if the page is executable, a read fault only if it is > readable. Fold the pmd/pte check together and add pud_leaf/p4d_leaf. > > Fixes: 09cfefb7fa70c ("LoongArch: Add memory management") > Signed-off-by: Song Hu <[email protected]> > --- > arch/loongarch/mm/fault.c | 24 ++++++++++++++++++++---- > 1 file changed, 20 insertions(+), 4 deletions(-) > > diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c > index 2c93d33356e5..591a9c7060f2 100644 > --- a/arch/loongarch/mm/fault.c > +++ b/arch/loongarch/mm/fault.c > @@ -38,6 +38,7 @@ static int __kprobes spurious_fault(unsigned long write, unsigned long address) > pud_t *pud; > pmd_t *pmd; > pte_t *pte; > + unsigned long prot; > > if (!(address & __UA_LIMIT)) > return 0; > @@ -58,15 +59,30 @@ static int __kprobes spurious_fault(unsigned long write, unsigned long address) > if (!pmd_present(pmdp_get(pmd))) > return 0; > > - if (pmd_leaf(*pmd)) { > - return write ? pmd_write(pmdp_get(pmd)) : 1; > + if (p4d_leaf(p4dp_get(p4d))) { > + prot = p4d_val(p4dp_get(p4d)); > + } else if (pud_leaf(pudp_get(pud))) { > + prot = pud_val(pudp_get(pud)); > + } else if (pmd_leaf(*pmd)) { > + prot = pmd_val(pmdp_get(pmd)); P4D doesn't exist on LoongArch, PUD huge page isn't supported on LoongArch, so what are you doing? Huacai > } else { > pte = pte_offset_kernel(pmd, address); > if (!pte_present(ptep_get(pte))) > return 0; > - > - return write ? pte_write(ptep_get(pte)) : 1; > + prot = pte_val(ptep_get(pte)); > } > + > + /* > + * The TLB-protect handler passes write=0 for read (TLBNR), exec (TLBNX) > + * and privilege (TLBPE) faults alike, so recover the real cause from the > + * exception code; otherwise an exec into a present NX page (or a read of > + * a NO_READ page) is wrongly treated as spurious and loops forever. > + */ > + if (write) > + return prot & _PAGE_WRITE; > + if (read_csr_excode() == EXCCODE_TLBNX) > + return !(prot & _PAGE_NO_EXEC); > + return !(prot & _PAGE_NO_READ); > } > > static void __kprobes no_context(struct pt_regs *regs, > -- > 2.43.0 >