casaroli opened a new pull request, #19544:
URL: https://github.com/apache/nuttx/pull/19544

   ## Summary
   
   `addrenv_take()` and `addrenv_give()` dereference their argument
   unconditionally, but a task does not necessarily own an address environment.
   `tcb->addrenv_own` is set only by `addrenv_attach()`, which is reached only
   from `addrenv_allocate()`. A kernel thread never allocates one, and in a
   protected build nothing does — there is a single address space for the whole
   system and the architecture's `up_addrenv_*()` are stubs, so `addrenv_own` is
   NULL for every task, always.
   
   `addrenv_join()` calls `addrenv_take(ptcb->addrenv_own)` without a check, so
   `pthread_create()` faults on `&((struct addrenv_s *)NULL)->refs` whenever the
   calling task has no address environment. With `CONFIG_DEBUG_ASSERTIONS` off
   the same access silently corrupts low memory instead.
   
   That a task may have no address environment is already an expected state
   everywhere else. In `sched/addrenv/addrenv.c` itself, `addrenv_switch()`
   returns `OK` when `tcb->addrenv_curr` is NULL and `addrenv_drop()` returns
   early. `addrenv_select()` also calls `addrenv_take()` unconditionally, which 
is
   exactly why every one of its callers checks first:
   
   | Site | Guard |
   |---|---|
   | `sched/sched/sched_get_stateinfo.c:91` | `if (tcb->addrenv_own)` |
   | `sched/task/task_argvstr.c:75` | `if (tcb->addrenv_own != NULL)` |
   | `fs/procfs/fs_procfsproc.c:1494` | `if (info->tcb->addrenv_own != NULL)` |
   | `fs/procfs/fs_procfsproc.c:1563` | `if (tcb->addrenv_own != NULL)` |
   | `arch/arm/src/common/arm_checkstack.c:235` | `if (tcb->addrenv_own != 
NULL)` |
   | `arch/arm64/src/common/arm64_checkstack.c:220` | `if (tcb->addrenv_own != 
NULL)` |
   | `arch/risc-v/src/common/riscv_checkstack.c:190` | `if (tcb->addrenv_own != 
NULL)` |
   | `arch/tricore/src/common/tricore_checkstack.c:127` | `if (tcb->addrenv_own 
!= NULL)` |
   
   `addrenv_join()` is the one path into `addrenv_take()` that cannot push the
   check to its caller, and it is the one that was missed. This patch handles
   NULL in `addrenv_take()` and `addrenv_give()`, the way the rest of the file
   already does.
   
   `addrenv_give()` returns a non-zero count for the NULL case on purpose. Its
   only meaningful caller is `addrenv_drop()`:
   
   ```c
     if (addrenv_give(addrenv) == 0)
       {
         ... addrenv_destroy(addrenv) ...
       }
   ```
   
   Returning 0 would ask it to destroy an address environment that does not
   exist. `addrenv_drop()` happens to check for NULL first, so 0 would be
   harmless today, but "there are still users" is the honest answer for a
   reference that was never taken.
   
   ## Impact
   
   Affected whenever `CONFIG_ARCH_ADDRENV=y` and the calling task has no address
   environment:
   
   1. **Every `CONFIG_BUILD_PROTECTED` build with `CONFIG_ARCH_ADDRENV=y`** — no
      task ever owns an address environment, so *every* `pthread_create()`
      faults. In tree that is `fvp-armv8r:pnsh` and `fvp-armv8r:pnsh_smp`. Both
      enable `CONFIG_TESTING_OSTEST`, and ostest reaches `pthread_create()` long
      before it finishes, so neither can ever have been run. They require an Arm
      Cortex-R82 FVP, which is presumably why this went unnoticed.
   
      Note `CONFIG_ARCH_ADDRENV` is not optional for these configurations: it is
      what provides `up_addrenv_kstackalloc()`, required by
      `CONFIG_ARCH_KERNEL_STACK`.
   
   2. **`CONFIG_BUILD_KERNEL` builds where a kernel thread calls
      `pthread_create()`** — kernel threads never own an address environment
      either. In-tree call chain:
   
      ```
      drivers/video/vnc/vnc_fbdev.c:522   kthread_create("vnc_server", ..., 
vnc_server, ...)
      drivers/video/vnc/vnc_server.c:334    -> vnc_start_updater(session)
      drivers/video/vnc/vnc_updater.c:493      -> pthread_create(...)
      ```
   
   Not affected: `CONFIG_BUILD_FLAT` (`addrenv_join()` is compiled out), and
   `CONFIG_BUILD_KERNEL` user processes (they own a real address environment).
   
   No API, ABI, Kconfig or documentation change. Behaviour is unchanged whenever
   the address environment is non-NULL — the patch only adds the NULL case.
   
   ## Testing
   
   **Host:** macOS (Darwin 25.5.0), arm64
   **Toolchain:** `riscv-none-elf-gcc` 14.2.0 (xPack)
   **Emulator:** `qemu-system-riscv64` 10.x
   **Configuration:** `rv-virt:knsh64` (`CONFIG_BUILD_KERNEL=y`,
   `CONFIG_ARCH_ADDRENV=y`), unmodified in-tree defconfig
   
   ```sh
   ./tools/configure.sh rv-virt:knsh64 && make olddefconfig && make -j10
   make export
   cd ../apps && ./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz && 
make import
   cd ../nuttx
   qemu-system-riscv64 -semihosting -M virt -cpu rv64 -smp 1 -nographic -kernel 
./nuttx
   ```
   
   ### 1. Reproducing the fault
   
   A kernel thread has no address environment, so calling `pthread_create()` 
from
   one reaches the same `addrenv_join()` path as a protected build. Added
   temporarily to `nx_bringup()`, purely as a test harness — it is *not* part of
   this patch:
   
   ```c
   static pthread_addr_t addrenv_probe_child(pthread_addr_t arg)
   {
     return NULL;
   }
   
   static int addrenv_probe_kthread(int argc, FAR char *argv[])
   {
     pthread_t tid;
     pthread_create(&tid, NULL, addrenv_probe_child, NULL);
     return 0;
   }
   
   /* in nx_bringup(), after nx_workqueues() */
   kthread_create("addrenv_probe", 250, 2048, addrenv_probe_kthread, NULL);
   ```
   
   **Before this patch:**
   
   ```
   riscv_exception: EXCEPTION: Store/AMO access fault.
       MCAUSE: 0000000000000007, EPC: 0000000080200e90, MTVAL: 0000000000000060
   riscv_fault_handler: PANIC!!! Exception = 0000000000000007
   dump_assert_info: Current Version: NuttX  13.0.0 a493a9b52f-dirty Jul 26 
2026 18:39:10 risc-v
   dump_assert_info: Assertion failed panic: at file: 
common/riscv_exception.c:142
       task: addrenv_probe process: Kernel
   ```
   
   `EPC` is inside `addrenv_take()`:
   
   ```
   0000000080200e8a <addrenv_take>:
       80200e8a:        li        a5,1
       80200e8c:        addi    a4,a0,96
       80200e90:        amoadd.w.aqrl   zero,a5,(a4)     <-- faults
   ```
   
   `a0` is the NULL `addrenv`, `+96` is `offsetof(struct addrenv_s, refs)`
   (confirmed in DWARF: `refs` has `DW_AT_data_member_location: 96`), and 96 is
   `0x60` — exactly the reported `MTVAL`.
   
   **After this patch:** `addrenv_take()` returns without faulting.
   
   That probe then hits a second, unrelated problem — a kernel thread has no 
user
   heap mapped, so `pthread_create()` goes on to fault in `umm_try_initialize()`
   at `0xC0100008`. It is a reproducer for the NULL dereference, not a claim 
that
   `pthread_create()` from a kernel thread is otherwise supported. In a 
protected
   build, where the single address space *is* mapped, this patch is all that is
   needed.
   
   ### 2. No regression — `ostest` on `rv-virt:knsh64`
   
   Full `ostest` run, without the probe, with this patch applied:
   
   ```
   nsh> ostest
   ...
   user_main: Started with argc=5
   ...
   ostest_main: Exiting with status 0
   ```
   
   794 lines of output, no assertions or exceptions. `ostest` also passes 
without
   this patch on the same configuration, as expected — a `CONFIG_BUILD_KERNEL`
   user process owns a real address environment and never takes the NULL path.
   
   (Note for anyone reproducing: `apps/testing/ostest/Makefile` sets
   `PRIORITY = SCHED_PRIORITY_DEFAULT`, which `Application.mk` encodes as
   `nx_priority = 0`; the task is then created at priority 0 and trips
   `DEBUGASSERT(sched_priority >= SCHED_PRIORITY_MIN)` in
   `nxsched_add_prioritized()`. Setting an explicit `PRIORITY = 100` avoids it.
   That is a separate issue, unrelated to this patch.)
   
   ### 3. Style
   
   `tools/checkpatch.sh -g <commit>` — `All checks pass.`
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to