Fishwaldo opened a new pull request, #19733:
URL: https://github.com/apache/nuttx/pull/19733
## Summary
* In a kernel build with assertions enabled, every exit of a process that
frees memory panics in `sem_post`. No program can be run twice, and
running one at all takes the shell down with it.
* A mutex records its holder as a task id in the low 31 bits of a word
whose top bit means "someone is blocked on this". The id was stored
without masking, so an id with its top bit set became a holder with the
blocking bit raised.
* Task ids are normally small and positive, but not always.
`nxsched_gettid()` reports `-ESRCH` for a context that no longer maps to
a running task, and there is a window where that is exactly what the
running context is: `nxtask_exit()` marks the next task ready to run
while the dying task is still executing on its own stack, and only then
releases the TCB. Freeing the group inside that release takes and drops
the group's mutexes, so the lock stores `0xfffffffd` and the unlock
compares `0x7ffffffd`, which are not equal.
* This is not specific to a build mode. `nxsched_gettid()` returns `-ESRCH`
whenever the task at the head of the ready-to-run list is not
`TSTATE_TASK_RUNNING`, which is generic scheduler behaviour. Kernel
builds
merely hit the window on essentially every process exit, since tearing
down a process frees memory inside it, and that is where it was found.
* With assertions off it is quieter and worse: the accidental blocking bit
sends the unlock looking for a waiter that never existed.
* The fix encodes the id the same way everywhere it is stored or compared,
so a lock and an unlock from one context agree whatever the id's sign.
* `mm_lock()` already sidesteps this window with a note that `gettid()` may
return `-ESRCH` during a context switch. This gives the generic mutex the
same footing rather than leaving a second special case.
* The masked forms of -1 and -2 would alias the `NXSEM_MRESET` and
`NXSEM_NO_MHOLDER` sentinels, but `nxsched_gettid()` yields only valid
ids and `-ESRCH`, which is -3.
* No related issue filed.
## Impact
* Is new feature added? Is existing feature changed? **NO.** Bug fix.
* Impact on user? **YES, positive.** Kernel builds with assertions become
usable: before this, a process that frees memory panics on exit, which in
a kernel build is every process. No API change.
* Impact on build? **NO.**
* Impact on hardware? **NO.** Generic scheduler code.
* Impact on documentation? **NO.**
* Impact on security? **Marginally, yes.** With assertions disabled the
stray blocking bit makes an unlock search for a waiter that does not
exist, so this removes a source of undefined behaviour on every process
exit rather than only a noisy assertion.
* Impact on compatibility? **NO.** Positive task ids encode exactly as
before; only the sign-extended case changes.
* Build-mode dependence? **NO.** The wrong encoding is present in flat,
protected and kernel builds alike. Flat builds reach the teardown window
less predictably, so the assertion fires less often, but nothing about
the defect or the fix is conditional on `CONFIG_BUILD_KERNEL`.
## Testing
I confirm that changes are verified on local setup and works as intended:
* Build Host: macOS 26.5.1, arm64 (Apple Silicon), xPack riscv-none-elf-gcc
15.2.0
* Target: RISC-V, ESWIN EIC7700X EVB (downstream board port, not yet
upstream), kernel build, assertions enabled
Any process exit that frees memory triggers it. The command in hand at the
time was `i2c bus`.
Testing logs before change:
```
nsh> i2c bus
BUS EXISTS?
[CPU1] dump_assert_info: Current Version: NuttX 13.0.0 beecb96e98 Aug 4
2026 17:58:09 risc-v
[CPU1] dump_assert_info: Assertion failed mholder ==
(((uint32_t)0x80000000) | ((uint32_t)0x7fffffff)) || (mholder &
(~((uint32_t)0x80000000))) == nxsched_gettid(): at file:
semaphore/sem_post.c:105 task(CPU1): /system/bin/init process: /system/bin/init
0xc000001a
[CPU1] up_dump_register: EPC: 0000000080204fa2
```
The shell does not survive it.
Testing logs after change:
```
exit: 0
asserts: 0
##### CMD 1: hello
Hello, World!!
##### END 1 (ok, 1.98s)
##### CMD 2: hello
Hello, World!!
##### END 2 (ok, 1.63s)
##### CMD 3: hello
Hello, World!!
##### END 3 (ok, 1.52s)
##### CMD 4: hello
Hello, World!!
##### END 4 (ok, 1.55s)
##### CMD 5: hello
Hello, World!!
##### END 5 (ok, 1.64s)
##### CMD 6: i2c bus
##### END 6 (ok, 1.64s)
##### CMD 7: i2c dev 0x08 0x77 0
##### END 7 (ok, 1.54s)
```
Five process exits in a row, the shell surviving all of them, and the
command that previously panicked now completing.
## PR verification Self-Check
* [x] This PR introduces only one functional change.
* [x] I have updated all required description fields above.
* [x] My PR adheres to Contributing Guidelines and Documentation.
* [ ] My PR is still work in progress (not ready for review).
* [x] My PR is ready for review and can be safely merged into a codebase.
---
*Claude (claude-opus-5) assisted with diagnosing this bug and with authoring
the
code comments and this PR description. The commit carries an `Assisted-by:`
tag
per
[CONTRIBUTING.md](https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md)
ยง1.5.*
--
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]