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


The following commit(s) were added to refs/heads/releases/13.0 by this push:
     new 69d7833fbab sched/backtrace: fix cross-CPU buffer access under addrenv
69d7833fbab is described below

commit 69d7833fbabc78030f4477e87da66687e5db7873
Author: liang.huang <[email protected]>
AuthorDate: Sun Jul 19 17:45:39 2026 +0800

    sched/backtrace: fix cross-CPU buffer access under addrenv
    
    The remote CPU's IPI handler wrote into the caller's buffer directly,
    which may not be reachable from the target CPU's address environment
    under CONFIG_ARCH_ADDRENV.
    
    Signed-off-by: liang.huang <[email protected]>
---
 sched/sched/sched_backtrace.c | 97 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 93 insertions(+), 4 deletions(-)

diff --git a/sched/sched/sched_backtrace.c b/sched/sched/sched_backtrace.c
index 40c993c0a05..b16e29bf7ee 100644
--- a/sched/sched/sched_backtrace.c
+++ b/sched/sched/sched_backtrace.c
@@ -28,15 +28,36 @@
 #include <nuttx/sched.h>
 #include <nuttx/init.h>
 
+#include <sys/param.h>
+
+#include <string.h>
+
 #include "sched.h"
 
 #ifdef CONFIG_ARCH_HAVE_BACKTRACE
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if defined(CONFIG_SMP) && defined(CONFIG_ARCH_ADDRENV)
+
+/* Depth of the scratch buffer used to relay a remote backtrace back to
+ * the caller, since the caller's own buffer may not be mapped in the
+ * address environment active on the target CPU.  Requests larger than
+ * this are serviced over multiple round trips.
+ */
+
+#define BACKTRACE_SCRATCH_DEPTH 32
+
+#endif
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
 
 #ifdef CONFIG_SMP
+
 struct backtrace_arg_s
 {
   pid_t pid;
@@ -105,6 +126,11 @@ int sched_backtrace(pid_t tid, FAR void **buffer, int 
size, int skip)
   FAR struct tcb_s *tcb = this_task();
   int ret = 0;
 
+  if (size <= 0 || buffer == NULL)
+    {
+      return 0;
+    }
+
   if (tcb->pid == tid)
     {
       ret = up_backtrace(tcb, buffer, size, skip);
@@ -121,25 +147,88 @@ int sched_backtrace(pid_t tid, FAR void **buffer, int 
size, int skip)
               tcb->task_state == TSTATE_TASK_RUNNING)
             {
               struct backtrace_arg_s arg;
+              bool need_restore;
+#ifdef CONFIG_ARCH_ADDRENV
+              FAR void *scratch[BACKTRACE_SCRATCH_DEPTH];
+#endif
 
               if ((tcb->flags & TCB_FLAG_CPU_LOCKED) != 0)
                 {
-                  arg.pid = tcb->pid;
-                  arg.need_restore = false;
+                  need_restore = false;
                 }
               else
                 {
-                  arg.pid = tcb->pid;
-                  arg.need_restore = true;
+                  need_restore = true;
                   tcb->flags |= TCB_FLAG_CPU_LOCKED;
                 }
 
+              arg.pid = tcb->pid;
+
+#ifdef CONFIG_ARCH_ADDRENV
+              arg.buffer = scratch;
+
+              while (ret < size)
+                {
+                  arg.size = MIN(size - ret, BACKTRACE_SCRATCH_DEPTH);
+                  arg.skip = skip + ret;
+
+                  /* If this round's request covers all remaining
+                   * frames (i.e. it was not capped by the scratch
+                   * buffer), there is nothing left to loop for
+                   * afterwards, so it is safe to have it release the
+                   * pin as well and skip the data-less round below.
+                   */
+
+                  arg.need_restore = need_restore &&
+                                      arg.size == size - ret;
+
+                  if (nxsched_smp_call_single(tcb->cpu,
+                                              sched_backtrace_handler,
+                                              &arg) < 0)
+                    {
+                      break;
+                    }
+
+                  if (arg.need_restore)
+                    {
+                      need_restore = false;
+                    }
+
+                  memcpy(&buffer[ret], scratch,
+                         arg.stacksize * sizeof(FAR void *));
+                  ret += arg.stacksize;
+
+                  if (arg.stacksize < arg.size)
+                    {
+                      /* Reached the bottom of the target's stack. */
+
+                      break;
+                    }
+                }
+
+              /* The loop above may have exited without a round
+               * releasing the pin (e.g. it broke out on reaching the
+               * bottom of the stack before a "guaranteed last round"
+               * occurred).  Send one more, data-less round purely to
+               * release it.
+               */
+
+              if (need_restore)
+                {
+                  arg.size = 0;
+                  arg.need_restore = true;
+                  nxsched_smp_call_single(tcb->cpu, sched_backtrace_handler,
+                                          &arg);
+                }
+#else
+              arg.need_restore = need_restore;
               arg.buffer = buffer;
               arg.size = size;
               arg.skip = skip;
               ret = nxsched_smp_call_single(tcb->cpu,
                                             sched_backtrace_handler,
                                             &arg) < 0 ? 0 : arg.stacksize;
+#endif
             }
           else
 #endif

Reply via email to