This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch releases/13.0 in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 6bfb85c96f65ad46a0401aa8b306f5e54ecac36c Author: liang.huang <[email protected]> AuthorDate: Wed Jul 8 09:09:41 2026 +0800 arch/riscv: reject overlapping leaf PTE in riscv_fillpage riscv_fillpage() is the LOADPF/STOREPF handler used under CONFIG_PAGING. It checked whether intermediate page table levels were already allocated, but never checked the final leaf PTE before installing a new mapping. RISC-V raises the same LOADPF/STOREPF cause both when a leaf PTE is absent (a real fault) and when it is present but its permission bits don't satisfy the access, e.g. a store to a .text page whose write access was revoked after ELF loading. The two cases are indistinguishable from mcause alone. Treating both cases as "page missing" let riscv_fillpage silently allocate a fresh, zeroed physical page over an existing mapping, discarding the old page (a leak) and defeating whatever permission that mapping was enforcing. Reproduced on real hardware: a user-space store to an already-loaded .text page got a fresh writable page instead of being rejected. Check the leaf PTE's valid bit before allocating; if a mapping already exists, panic instead of overwriting it. Signed-off-by: liang.huang <[email protected]> --- arch/risc-v/src/common/riscv_exception.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/risc-v/src/common/riscv_exception.c b/arch/risc-v/src/common/riscv_exception.c index bcfcd34f872..584db45c025 100644 --- a/arch/risc-v/src/common/riscv_exception.c +++ b/arch/risc-v/src/common/riscv_exception.c @@ -201,9 +201,7 @@ int riscv_fillpage(int mcause, void *regs, void *args) else { _alert("PANIC!!! virtual address not mappable: %" PRIxPTR "\n", vaddr); - up_irq_save(); - up_set_interrupt_context(true); - PANIC_WITH_REGS("panic", regs); + goto access_fault; } satp = READ_CSR(CSR_SATP); @@ -234,6 +232,21 @@ int riscv_fillpage(int mcause, void *regs, void *args) } ptlast = riscv_pgvaddr(paddr); + + /* LOADPF/STOREPF is also raised when the leaf PTE already exists but its + * permission bits don't satisfy the access (e.g. a store to a .text page + * whose write access was revoked). That is not a fault this function + * should handle: allocating a fresh page here would silently discard the + * existing mapping's page. + */ + + if (mmu_ln_getentry(ARCH_PGT_MAX_LEVELS, ptlast, vaddr) & PTE_VALID) + { + _alert("PANIC!!! page already mapped, permission violation: %" + PRIxPTR "\n", vaddr); + goto access_fault; + } + paddr = mm_pgalloc(1); if (!paddr) { @@ -249,6 +262,12 @@ int riscv_fillpage(int mcause, void *regs, void *args) mmu_ln_setentry(ARCH_PGT_MAX_LEVELS, ptlast, paddr, vaddr, mmuflags); return 0; + +access_fault: + up_irq_save(); + up_set_interrupt_context(true); + PANIC_WITH_REGS("panic", regs); + return -EINVAL; } #endif /* CONFIG_PAGING */
