sched/addrenv: do not dereference a NULL address environment

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.  addrenv_own is then NULL for every task, always.

That a task may have no address environment is already an expected state.
addrenv_switch() returns OK when tcb->addrenv_curr is NULL and addrenv_drop()
returns early, and every caller of addrenv_select() checks addrenv_own != NULL
before calling in:  nxsched_get_stateinfo(), nxtask_argvstr(), proc_groupenv()
and the arm, arm64, risc-v and tricore up_check_tcbstack().

addrenv_take() and addrenv_give() are the only two that dereference
unconditionally.  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.

Handle NULL in both, the way the rest of the file already does.
addrenv_give() returns a non-zero count for the NULL case so that callers
never conclude an absent address environment has become unreferenced and
should be destroyed.

Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marco Casaroli 2026-07-26 11:59:48 +02:00 committed by Xiang Xiao
parent 7fd17c9d7c
commit 6c1feb7839

View file

@ -395,7 +395,17 @@ int addrenv_restore(FAR struct addrenv_s *addrenv)
void addrenv_take(FAR struct addrenv_s *addrenv)
{
atomic_fetch_add(&addrenv->refs, 1);
/* A task can legitimately have no address environment -- addrenv_switch()
* and addrenv_drop() both treat that as "nothing to do". A kernel thread
* never owns one, and in a protected build no task does: there is a
* single address space for the whole system and the architecture's
* up_addrenv_*() are stubs. There is then nothing to reference count.
*/
if (addrenv != NULL)
{
atomic_fetch_add(&addrenv->refs, 1);
}
}
/****************************************************************************
@ -415,7 +425,12 @@ void addrenv_take(FAR struct addrenv_s *addrenv)
int addrenv_give(FAR struct addrenv_s *addrenv)
{
return atomic_fetch_sub(&addrenv->refs, 1) - 1;
/* See addrenv_take(): nothing was counted, so nothing is given back. A
* non-zero count is returned so that callers never conclude the (absent)
* address environment has become unreferenced and should be destroyed.
*/
return addrenv ? atomic_fetch_sub(&addrenv->refs, 1) - 1 : 1;
}
/****************************************************************************