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 df782bd1e587d4d85389235c267080768796bbc6 Author: Marco Casaroli <[email protected]> AuthorDate: Tue Jul 28 23:57:28 2026 +0200 xtensa/esp32s3: Stop an unreportable cache fault from livelocking. Reporting a fault can itself fault. syslog reaches memory the fault being reported may have made unreachable, so esp32s3_pagefault_dispatch() is re-entered from inside its own _alert() and never returns, and the console fills with the same half-printed line forever. Found under Espressif's QEMU, where PSRAM never initialises and the kernel build needs it; the board's PSRAM works, so hardware does not take this path. A fault repeating at the same address and PC is not helped by reporting it again, so the dispatcher tries three times and then halts with interrupts off. esp32s3_userfault_abort() clears the count through esp32s3_pagefault_clear_repeat(): reaching it means the fault was contained, so only unbroken recursion stops the machine, and three probes at one address do not halt a healthy system. Verified under QEMU: 12,958,521 bytes of output in 60 s before, four reports and a halt after. On an ESP32-S3 DevKitC, esp32s3-devkit:kernel_oct, three identical sandbox probes in one boot are all contained. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- arch/xtensa/src/esp32s3/esp32s3_pagefault.c | 59 +++++++++++++++++++++++++++++ arch/xtensa/src/esp32s3/esp32s3_pagefault.h | 11 ++++++ arch/xtensa/src/esp32s3/esp32s3_userfault.c | 14 +++++++ 3 files changed, 84 insertions(+) diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c index 4d029d7a585..af3b9176f5f 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c +++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c @@ -66,6 +66,18 @@ static volatile int g_pf_selftest_hits; #endif +/* How many times to let the same fault be reported before giving up on + * reporting it. Note the report may not survive even once -- see the + * comment in the dispatcher -- so this bounds the damage rather than + * guaranteeing a legible message. + */ + +#define PF_REPEAT_LIMIT 3 + +static uintptr_t g_pf_last_vaddr; +static uintptr_t g_pf_last_pc; +static int g_pf_repeats; + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -125,6 +137,36 @@ int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs) } #endif + /* Reporting a fault can itself fault. syslog reaches memory that the + * very fault being reported may have made unreachable -- PSRAM that never + * initialised, say -- and then this handler is re-entered from inside its + * own _alert(). The console fills with the same line severed part-way + * through EXCVADDR, forever, and nothing legible ever reaches it. + * + * A fault repeating at the same address and PC is not going to be helped + * by reporting it again. Try a few times, then stop and halt. The lines + * may still be truncated -- the print is what faults, so it cannot be made + * to complete from here -- but a handful of severed lines followed by + * silence is diagnosable, and an endless stream of them is not. + */ + + if (vaddr == g_pf_last_vaddr && pc == g_pf_last_pc) + { + if (++g_pf_repeats >= PF_REPEAT_LIMIT) + { + up_irq_save(); + for (; ; ) + { + } + } + } + else + { + g_pf_last_vaddr = vaddr; + g_pf_last_pc = pc; + g_pf_repeats = 0; + } + /* Report the precise fault (with its tracking EXCVADDR) and decline to * service it, so the caller falls through to the panic / abort path. */ @@ -135,3 +177,20 @@ int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs) return -EFAULT; } + +/**************************************************************************** + * Name: esp32s3_pagefault_clear_repeat + * + * Description: + * Forget the last serviced fault. Called once a fault has been contained + * some other way -- the task terminated -- so that later, unrelated faults + * at the same address are not counted as runaway recursion. + * + ****************************************************************************/ + +void esp32s3_pagefault_clear_repeat(void) +{ + g_pf_last_vaddr = 0; + g_pf_last_pc = 0; + g_pf_repeats = 0; +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.h b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h index 13ed1dbbcf3..3b268d9ef00 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_pagefault.h +++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h @@ -59,4 +59,15 @@ int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs); +/**************************************************************************** + * Name: esp32s3_pagefault_clear_repeat + * + * Description: + * Forget the last serviced fault, so that later faults at the same address + * are not mistaken for runaway recursion. + * + ****************************************************************************/ + +void esp32s3_pagefault_clear_repeat(void); + #endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_userfault.c b/arch/xtensa/src/esp32s3/esp32s3_userfault.c index becff2125b5..e0f6ca343a8 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_userfault.c +++ b/arch/xtensa/src/esp32s3/esp32s3_userfault.c @@ -41,6 +41,9 @@ #include "signal/signal.h" #include "esp32s3_userfault.h" +#ifdef CONFIG_ESP32S3_PAGEFAULT +#include "esp32s3_pagefault.h" +#endif /**************************************************************************** * Public Functions @@ -82,6 +85,17 @@ uint32_t *esp32s3_userfault_abort(int exccause, uint32_t *regs) struct tcb_s *tcb = this_task(); siginfo_t info; +#ifdef CONFIG_ESP32S3_PAGEFAULT + /* Reaching here means the fault was contained and the system carried on, + * so the dispatcher's repeat counter has served its purpose. Clear it, or + * a probe run three times at one address would trip that guard and halt a + * perfectly healthy system. Only *unbroken* recursion -- a report that + * faults before the abort can happen -- should stop the machine. + */ + + esp32s3_pagefault_clear_repeat(); +#endif + _alert("SIGSEGV task %s: EXCCAUSE=%d EXCVADDR=%08x PC=%08x\n", get_task_name(tcb), exccause, (unsigned)regs[REG_EXCVADDR], (unsigned)regs[REG_PC]);
