This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit ca56af65d022192f510e2b8dfecde452181481ed
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 19 21:34:40 2026 +0200

    arch/arm64: Check the range in up_addrenv_mprot() and support PROT_NONE.
    
    up_addrenv_mprot() walked the page tables for each page of the range
    and changed the last level entry.  It did not check that the address
    is a user address, it changed entries that are not valid, and it left
    the first pages changed when a later page had no page table.  It also
    always gave user read access, so PROT_NONE gave a readable page.
    
    Now the function first checks each page: a user address with a valid
    page entry.  If one page fails the check, it changes nothing and
    returns -ENOMEM.  Then it changes the entries.  PROT_NONE removes the
    user access (AP[1] clear, UXN set).
    
    The ELF loader is the only caller today, and its .text range passes
    the check.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/arm64/src/common/arm64_addrenv_perms.c | 58 ++++++++++++++++++++---------
 1 file changed, 40 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/src/common/arm64_addrenv_perms.c 
b/arch/arm64/src/common/arm64_addrenv_perms.c
index 91176daf604..93251d2083d 100644
--- a/arch/arm64/src/common/arm64_addrenv_perms.c
+++ b/arch/arm64/src/common/arm64_addrenv_perms.c
@@ -51,14 +51,28 @@
  * Private Functions
  ****************************************************************************/
 
+static uintptr_t get_pgtable(uintptr_t l1vaddr, uintptr_t vaddr)
+{
+  uintptr_t lnvaddr;
+  uint32_t  ptlevel;
+
+  for (ptlevel = mmu_get_base_pgt_level(), lnvaddr = l1vaddr;
+       ptlevel < MMU_PGT_LEVEL_MAX && lnvaddr != 0;
+       ptlevel++)
+    {
+      lnvaddr = arm64_pgvaddr(mmu_pte_to_paddr(
+                  mmu_ln_getentry(ptlevel, lnvaddr, vaddr)));
+    }
+
+  return lnvaddr;
+}
+
 static int modify_region(uintptr_t vstart, uintptr_t vend, uintptr_t setmask)
 {
   uintptr_t l1vaddr;
   uintptr_t lnvaddr;
   uintptr_t entry;
-  uintptr_t paddr;
   uintptr_t vaddr;
-  uint32_t  ptlevel;
 
   /* Must perform a reverse table walk */
 
@@ -71,29 +85,31 @@ static int modify_region(uintptr_t vstart, uintptr_t vend, 
uintptr_t setmask)
       return -EINVAL;
     }
 
+  /* Change nothing unless each page is mapped user memory */
+
   for (vaddr = vstart; vaddr < vend; vaddr += MM_PGSIZE)
     {
-      for (ptlevel = mmu_get_base_pgt_level(), lnvaddr = l1vaddr;
-           ptlevel < MMU_PGT_LEVEL_MAX;
-           ptlevel++)
+      lnvaddr = get_pgtable(l1vaddr, vaddr);
+      if (!arm64_uservaddr(vaddr) || lnvaddr == 0 ||
+          (mmu_ln_getentry(MMU_PGT_LEVEL_MAX, lnvaddr, vaddr) &
+           PTE_DESC_TYPE_MASK) != PTE_PAGE_DESC)
         {
-          paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, lnvaddr, vaddr));
-          lnvaddr = arm64_pgvaddr(paddr);
-          if (!lnvaddr)
-            {
-              return -EINVAL;
-            }
+          return -ENOMEM;
         }
+    }
 
+  for (vaddr = vstart; vaddr < vend; vaddr += MM_PGSIZE)
+    {
       /* Get entry and modify the flags */
 
-      entry  = mmu_ln_getentry(ptlevel, lnvaddr, vaddr);
-      entry &= ~CLR_MASK;
-      entry |= setmask | PTE_BLOCK_DESC_AP_USER;
+      lnvaddr = get_pgtable(l1vaddr, vaddr);
+      entry   = mmu_ln_getentry(MMU_PGT_LEVEL_MAX, lnvaddr, vaddr);
+      entry  &= ~CLR_MASK;
+      entry  |= setmask;
 
       /* Restore the entry */
 
-      mmu_ln_restore(ptlevel, lnvaddr, vaddr, entry);
+      mmu_ln_restore(MMU_PGT_LEVEL_MAX, lnvaddr, vaddr, entry);
     }
 
   return OK;
@@ -129,15 +145,21 @@ int up_addrenv_mprot(arch_addrenv_t *addrenv, uintptr_t 
addr, size_t len,
 
   UNUSED(addrenv);
 
-  /* The access is either RO or RW, read access cannot be revoked */
+  /* PROT_NONE removes all user access.  Else the access is RO or RW, and
+   * PROT_WRITE or PROT_EXEC also give read access.
+   */
 
-  if ((prot & PROT_WRITE) == 0)
+  if (prot == PROT_NONE)
     {
       setmask = PTE_BLOCK_DESC_AP_RO;
     }
+  else if ((prot & PROT_WRITE) == 0)
+    {
+      setmask = PTE_BLOCK_DESC_AP_RO | PTE_BLOCK_DESC_AP_USER;
+    }
   else
     {
-      setmask = PTE_BLOCK_DESC_AP_RW;
+      setmask = PTE_BLOCK_DESC_AP_RW | PTE_BLOCK_DESC_AP_USER;
     }
 
   /* Execute access is explicitly revoked, implicitly granted */

Reply via email to