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

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

commit a16dbf2100e6a1484a59cb41826034d01834b36d
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 12 01:04:30 2026 +0200

    arch/arm64: Keep the TLS info address of a thread in TPIDR_EL0.
    
    User code found the TLS info of the running thread by rounding the stack
    pointer down to the TLS stack alignment.  Code that runs on another
    stack, such as a context made by makecontext() or a coroutine, got
    another address, and libc read garbage as its stream list, errno and
    pthread keys: the first printf() could fault in the kernel.
    
    The kernel now sets TPIDR_EL0 of each thread to the base of its stack
    allocation, where the TLS info is, and saves and restores it with the
    other registers (in the context slot that was reserved).  A forked
    child gets the address of its own stack, and a signal handler keeps
    the thread's value.  arm64_jump_to_user() stores it too: the thread
    has used its initial context area as kernel stack before it enters
    user mode.  User code reads TPIDR_EL0 through up_tls_info().
    
    The kernel does not use up_tls_info(), so tls_get_info() is now always
    built: the kernel side of a flat build still calls it.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/arm64/include/arch.h                       |  8 ++++++++
 arch/arm64/include/irq.h                        |  2 +-
 arch/arm64/src/common/arm64_fork.c              |  2 ++
 arch/arm64/src/common/arm64_initialstate.c      |  5 +++++
 arch/arm64/src/common/arm64_schedulesigaction.c |  1 +
 arch/arm64/src/common/arm64_vector_table.S      |  3 +++
 arch/arm64/src/common/arm64_vectors.S           | 25 +++++++++++++++++++++++++
 libs/libc/tls/tls_getinfo.c                     |  8 +++-----
 8 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/arch.h b/arch/arm64/include/arch.h
index 4f0288e67db..2ffc9148f9d 100644
--- a/arch/arm64/include/arch.h
+++ b/arch/arm64/include/arch.h
@@ -57,6 +57,14 @@
 #define UP_WFE() __asm__ __volatile__ ("wfe" : : : "memory")
 #define UP_SEV() __asm__ __volatile__ ("sev" : : : "memory")
 
+/* The kernel keeps the address of the running thread's TLS info in
+ * TPIDR_EL0, so user code finds it whatever stack it runs on.
+ */
+
+#if !defined(__ASSEMBLY__) && !defined(__KERNEL__)
+#  define up_tls_info() ((struct tls_info_s *)read_sysreg(tpidr_el0))
+#endif
+
 /****************************************************************************
  * Inline functions
  ****************************************************************************/
diff --git a/arch/arm64/include/irq.h b/arch/arm64/include/irq.h
index e38d46c5bd8..fc2d25e895a 100644
--- a/arch/arm64/include/irq.h
+++ b/arch/arm64/include/irq.h
@@ -154,7 +154,7 @@
 #define REG_SP_EL0          (34)
 #define REG_EXE_DEPTH       (35)
 #define REG_SCTLR_EL1       (36)
-#define REG_RESERVED        (37)
+#define REG_TPIDR_EL0       (37)
 
 /* In Armv8-A Architecture, the stack must align with 16 byte */
 
diff --git a/arch/arm64/src/common/arm64_fork.c 
b/arch/arm64/src/common/arm64_fork.c
index f0e42d32d2e..ee19efdb816 100644
--- a/arch/arm64/src/common/arm64_fork.c
+++ b/arch/arm64/src/common/arm64_fork.c
@@ -262,6 +262,7 @@ static pid_t arm64_fork_direct(bool vfork, struct tcb_s 
*parent,
 #endif
 
   child->xcp.regs[REG_EXE_DEPTH] = 0;
+  child->xcp.regs[REG_TPIDR_EL0] = (uint64_t)child->stack_alloc_ptr;
   child->xcp.regs[REG_SP_ELX]    = newsp - XCPTCONTEXT_SIZE;
 #ifdef CONFIG_ARCH_KERNEL_STACK
   child->xcp.regs[REG_SP_EL0]    = (uint64_t)child->xcp.ustkptr;
@@ -377,6 +378,7 @@ static pid_t arm64_fork_syscall(bool vfork, struct tcb_s 
*parent)
   child->xcp.regs[REG_X0]        = 0;
   child->xcp.regs[REG_FP]        = newfp;
   child->xcp.regs[REG_EXE_DEPTH] = 0;
+  child->xcp.regs[REG_TPIDR_EL0] = (uint64_t)child->stack_alloc_ptr;
   child->xcp.regs[REG_SP_ELX]    = regtop - XCPTCONTEXT_SIZE;
 
   if ((sregs[REG_SPSR] & SPSR_MODE_MASK) == SPSR_MODE_EL0T)
diff --git a/arch/arm64/src/common/arm64_initialstate.c 
b/arch/arm64/src/common/arm64_initialstate.c
index c848ab8597e..3eb81075466 100644
--- a/arch/arm64/src/common/arm64_initialstate.c
+++ b/arch/arm64/src/common/arm64_initialstate.c
@@ -105,6 +105,10 @@ void arm64_new_task(struct tcb_s * tcb)
 #endif
   xcp->regs[REG_EXE_DEPTH] = 0;
 
+  /* TPIDR_EL0 points to the TLS info of the thread, see up_tls_info() */
+
+  xcp->regs[REG_TPIDR_EL0] = (uint64_t)tcb->stack_alloc_ptr;
+
 #ifndef CONFIG_BUILD_FLAT
   tcb->xcp.initregs        = tcb->xcp.regs;
 #endif
@@ -153,6 +157,7 @@ void up_initial_state(struct tcb_s *tcb)
        */
 
       write_sysreg(0, tpidrro_el0);
+      write_sysreg(tcb->stack_alloc_ptr, tpidr_el0);
 
 #ifdef CONFIG_STACK_COLORATION
 
diff --git a/arch/arm64/src/common/arm64_schedulesigaction.c 
b/arch/arm64/src/common/arm64_schedulesigaction.c
index 48a1f235d26..33c6350e2bb 100644
--- a/arch/arm64/src/common/arm64_schedulesigaction.c
+++ b/arch/arm64/src/common/arm64_schedulesigaction.c
@@ -78,6 +78,7 @@ static void arm64_init_signal_process(struct tcb_s *tcb, 
uint64_t *regs)
   tcb->xcp.regs[REG_SP_ELX] = regs[REG_SP_ELX] - XCPTCONTEXT_SIZE;
   tcb->xcp.regs[REG_EXE_DEPTH] = 1;
   tcb->xcp.regs[REG_SCTLR_EL1] = regs[REG_SCTLR_EL1];
+  tcb->xcp.regs[REG_TPIDR_EL0] = regs[REG_TPIDR_EL0];
 }
 
 /****************************************************************************
diff --git a/arch/arm64/src/common/arm64_vector_table.S 
b/arch/arm64/src/common/arm64_vector_table.S
index 6ca66d692e3..fd475f5ce06 100644
--- a/arch/arm64/src/common/arm64_vector_table.S
+++ b/arch/arm64/src/common/arm64_vector_table.S
@@ -274,6 +274,9 @@ SECTION_FUNC(text, arm64_exit_exception)
     msr    sctlr_el1, x0
 #endif
 
+    ldr    x0,   [sp, #8 * REG_TPIDR_EL0]
+    msr    tpidr_el0, x0
+
     ldp    x0, x1,   [sp, #8 * REG_SP_EL0]
     msr    sp_el0,   x0
     msr    tpidrro_el0,  x1
diff --git a/arch/arm64/src/common/arm64_vectors.S 
b/arch/arm64/src/common/arm64_vectors.S
index a1e0f5f4682..cc85a9f262a 100644
--- a/arch/arm64/src/common/arm64_vectors.S
+++ b/arch/arm64/src/common/arm64_vectors.S
@@ -89,6 +89,9 @@ SECTION_FUNC(text, up_saveusercontext)
     str    x4,  [x0, #8 * REG_SCTLR_EL1]
 #endif
 
+    mrs    x4,  tpidr_el0
+    str    x4,  [x0, #8 * REG_TPIDR_EL0]
+
     ret
 
 /****************************************************************************
@@ -134,6 +137,13 @@ SECTION_FUNC(text, arm64_jump_to_user)
     str x0,  [sp, #8 * REG_SCTLR_EL1]
 #endif
 
+    /* The thread has used this area as stack since its initial context
+     * was set: keep its TLS info address.
+     */
+
+    mrs x0,  tpidr_el0
+    str x0,  [sp, #8 * REG_TPIDR_EL0]
+
     b arm64_exit_exception
 #endif
 
@@ -147,6 +157,11 @@ SECTION_FUNC(text, arm64_jump_to_user)
 
 GTEXT(arm64_sync_exc)
 SECTION_FUNC(text, arm64_sync_exc)
+    /* Save TPIDR_EL0 here: the vector table entries have no room left */
+
+    mrs    x9,  tpidr_el0
+    str    x9,  [sp, #8 * REG_TPIDR_EL0]
+
     /* checking the EC value to see which exception need to be handle */
 
 #if CONFIG_ARCH_ARM64_EXCEPTION_LEVEL == 3
@@ -235,6 +250,11 @@ reserved_syscall:
 
 GTEXT(arm64_irq_handler)
 SECTION_FUNC(text, arm64_irq_handler)
+    /* Save TPIDR_EL0 here: the vector table entries have no room left */
+
+    mrs    x9,  tpidr_el0
+    str    x9,  [sp, #8 * REG_TPIDR_EL0]
+
     /* Switch to IRQ stack and save current sp on it. */
 #ifdef CONFIG_SMP
     get_cpu_id x0
@@ -314,6 +334,11 @@ SECTION_FUNC(text, arm64_fiq_handler)
 
     b      arm64_exit_exception
 #else
+    /* Save TPIDR_EL0 here: the vector table entries have no room left */
+
+    mrs    x9,  tpidr_el0
+    str    x9,  [sp, #8 * REG_TPIDR_EL0]
+
    /* Switch to FIQ stack and save current sp on it. */
 #ifdef CONFIG_SMP
     get_cpu_id x0
diff --git a/libs/libc/tls/tls_getinfo.c b/libs/libc/tls/tls_getinfo.c
index 206ea9b0f40..a1d1480c3b8 100644
--- a/libs/libc/tls/tls_getinfo.c
+++ b/libs/libc/tls/tls_getinfo.c
@@ -32,8 +32,6 @@
 #include <nuttx/tls.h>
 #include <nuttx/sched.h>
 
-#ifndef up_tls_info
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -43,8 +41,9 @@
  *
  * Description:
  *   Return a reference to the tls_info_s structure.  This is used as part
- *   of the internal implementation of tls_get/set_elem() and ONLY for the
- *   where CONFIG_TLS_ALIGNED is *not* defined or __KERNEL__ is defined.
+ *   of the internal implementation of tls_get/set_elem() when neither
+ *   up_tls_info() nor CONFIG_TLS_ALIGNED gives it, and in the kernel,
+ *   where up_tls_info() may not be used.
  *
  * Input Parameters:
  *   None.
@@ -75,4 +74,3 @@ FAR struct tls_info_s *tls_get_info(void)
   return info;
 }
 
-#endif /* !defined(up_tls_info) */

Reply via email to