Skip to content

fix null _p access in the TestNode async_delay timer handler - #1243

Merged
facontidavide merged 1 commit into
BehaviorTree:masterfrom
aysha-afrah26:testnode-timer-null-pimpl
Oct 24, 2026
Merged

facontidavide merged 1 commit into
BehaviorTree:masterfrom
aysha-afrah26:testnode-timer-null-pimpl

Conversation

@aysha-afrah26

Copy link
Copy Markdown
Contributor

TestNode::onStart arms a TimerQueue handler for async_delay, and that handler reaches its state through _p->completed. The TimerQueue itself lives inside TestNode::PImpl, and ~TimerQueue() runs every pending handler with aborted=true on its worker thread while it joins, so the handler executes in the middle of deleting the PImpl. libc++'s unique_ptr clears its pointer before calling the deleter, so _p is null at that point and the store crashes the process (UBSan reports a member access within null pointer of type TestNode::PImpl, ASan a SEGV at test_node.cpp:144); with libstdc++ it instead writes into completed after it was destroyed, since it was declared after timer. I ran into it just by letting a tree go out of scope after one tick while a substituted TestNode with async_delay was still RUNNING: ~Tree halts first, but cancelAll() only schedules the aborted handler, so the destruction usually wins the race. The handler now captures the PImpl pointer instead of reading _p, and timer is declared last in PImpl so it is joined while completed is still alive, the same ordering #1180 applied to Sleep, Delay and Timeout. PImpl is private, so there is no API or ABI change, and the new Substitution.DestroyTestNodeWithPendingAsyncDelay aborts under the ASan/UBSan build on master and passes with the fix.

@facontidavide
facontidavide merged commit a7a0014 into BehaviorTree:master Oct 24, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants