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 f17c74c626005a39171ecd06dfb76de4d97a002a
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Jul 28 15:18:31 2026 +0200

    arch/arm64: Report a recovered user fault as a segmentation fault.
    
    Two problems in the same path make a contained user fault look like a
    kernel failure.
    
    arm64_el1_undef() dumps the words around ELR.  For an exception taken
    from EL0, ELR is a user address, and the words around it can be in a
    page that is not mapped.  Then the memcpy faults inside the fatal
    handler.  That nested exception trips the DEBUGASSERT in
    arm64_fatal_handler(), and the fault that the user took is not reported.
    The ESR that tells what happened is lost.  Skip the dump when the
    exception came from EL0.  At EL1 the address is kernel code that was
    just fetched, so keep the dump there.
    
    arm64_fatal_handler() then reports the fault that it recovers from as
    "PANIC: Unhandled user exception", followed by a full register dump.
    But there is no panic: it sets TCB_FLAG_FORCED_CANCEL, changes ELR to
    _exit(SIGSEGV), and the system continues without the offending task.
    Print "Segmentation fault in <process> (PID n: <thread>)" instead, the
    same message as risc-v, and keep the register dump for the
    PANIC_WITH_REGS() path, which is fatal.
    
    Tested on QEMU qemu-armv8a:knsh with examples/sandbox and ostest.  A
    user read or write of kernel memory (0x40000000) now prints:
    
      arm64_exception_handler: ESR_ELn: 0x9200000e
      arm64_fatal_handler: Segmentation fault in sandbox (PID 10: sandbox)
      arm64_fatal_handler: Reason: DABT (lower EL) - Data Abort from a ...
      sandbox: the offender exited with status 2816
    
    Before this change it printed "PANIC: Unhandled user exception" and a
    register dump for the same recovered fault.  An undefined instruction
    at EL0 now prints "Undefined instruction at <ELR>" without the dump,
    then the same segmentation fault message.  The shell survives in all
    cases, and ostest exits with status 0 before and after this change.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/arm64/src/common/arm64_fatal.c | 46 ++++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/arch/arm64/src/common/arm64_fatal.c 
b/arch/arm64/src/common/arm64_fatal.c
index cd310ba11cf..aabeafc1fed 100644
--- a/arch/arm64/src/common/arm64_fatal.c
+++ b/arch/arm64/src/common/arm64_fatal.c
@@ -357,20 +357,30 @@ static int arm64_el1_bti(uint64_t *regs, uint64_t esr)
 
 static int arm64_el1_undef(uint64_t *regs, uint64_t esr)
 {
-  uint32_t insn;
   uint64_t elr = regs[REG_ELR];
+  uint32_t insn;
+  int      i;
+
+  serr("Undefined instruction at 0x%" PRIx64 "\n", elr);
+
+  /* Only dump the surrounding words for an exception taken at EL1, where
+   * ELR is kernel code that was just fetched.  From EL0, ELR is a user
+   * address and the words around it can be in a page that is not mapped.
+   * A fault here is a nested exception, which trips the DEBUGASSERT in
+   * arm64_fatal_handler() before the user fault is reported.
+   */
+
+  if ((regs[REG_SPSR] & SPSR_MODE_MASK) == SPSR_MODE_EL0T)
+    {
+      return -1;
+    }
 
-  serr("Undefined instruction at 0x%" PRIx64 ", dump:\n", elr);
-  memcpy(&insn, (void *)(elr - 8), 4);
-  serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr - 8, insn);
-  memcpy(&insn, (void *)(elr - 4), 4);
-  serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr - 4, insn);
-  memcpy(&insn, (void *)(elr), 4);
-  serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr, insn);
-  memcpy(&insn, (void *)(elr + 4), 4);
-  serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr + 4, insn);
-  memcpy(&insn, (void *)(elr + 8), 4);
-  serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr + 8, insn);
+  serr("dump:\n");
+  for (i = -8; i <= 8; i += 4)
+    {
+      memcpy(&insn, (FAR void *)(elr + i), sizeof(insn));
+      serr("0x%" PRIx64 " : 0x%" PRIx32 "\n", elr + i, insn);
+    }
 
   return -1;
 }
@@ -640,6 +650,7 @@ uint64_t *arm64_fatal_handler(uint64_t *regs)
           ((tcb->flags & TCB_FLAG_SYSCALL) == 0) &&
           ((regs[REG_SPSR] & SPSR_MODE_MASK) == SPSR_MODE_EL0T))
         {
+          struct tcb_s *ptcb;
           uint64_t esr;
           const char *reason;
           const char *desc;
@@ -659,10 +670,17 @@ uint64_t *arm64_fatal_handler(uint64_t *regs)
               desc = "";
             }
 
-          _alert("PANIC: Unhandled user exception in PID %d: %s\n",
+          /* This is not a panic:  the offending task is terminated and the
+           * system continues.  Say so in the same words as risc-v, and leave
+           * the register dump to the paths that are fatal.
+           */
+
+          ptcb = nxsched_get_tcb(tcb->group->tg_pid);
+
+          _alert("Segmentation fault in %s (PID %d: %s)\n",
+                 ptcb != NULL ? get_task_name(ptcb) : "<gone>",
                  tcb->pid, get_task_name(tcb));
           _alert("Reason: %s - %s\n", reason, desc);
-          up_dump_register(regs);
 
           tcb->flags |= TCB_FLAG_FORCED_CANCEL;
 

Reply via email to