fix use-after-free of a blackboard entry held by getAnyLocked (alternative to #1181) - #1202
Merged
Merged
Conversation
AnyPtrLocked holds raw pointers into a Blackboard::Entry and can not own it without changing its layout (ABI). Attach a custom deleter to every Entry at its two allocation sites: an entry whose last shared_ptr is dropped while its mutex is still locked is parked, and destroyed by a later retirement that finds it unlocked. Being attached at allocation time, the deleter covers unset(), clear(), cloneInto() and ~Blackboard with no change to them, including the unset() already inlined in binaries built against the 4.10 headers. No header change. Alternative to #1181; the AnyPtrLocked* regression tests come from that PR. Co-Authored-By: Aysha Afrah Ziya <aysha26@digiscrypt.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the storage lock Changes requested by two independent reviews: - RetireEntry is noexcept, and neither locks nor allocates when nothing is parked - unset(), clear() and cloneInto() destroy the removed values after releasing storage_mutex_ - parked entries are also swept when a new entry is created, to bound the delay of the stored value's destructor - document that LockedPtr is not valid after unlock() Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 20, 2026
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Alternative to #1181, same bug, smaller and ABI-transparent fix. The
AnyPtrLocked*regression tests are taken from #1181 (thanks @aysha-afrah26, credited as co-author).Bug
Blackboard::getAnyLocked()returns anAnyPtrLocked: a rawAny*plus the raw, lockedentry_mutexof aBlackboard::Entry. Nothing keeps theEntryalive, sounset(),clear(),cloneInto()or~Blackboardcan destroy it while the handle is in use: heap-use-after-free (seen on the Groot2 thread,ExportBlackboardToJSONvs anUnsetBlackboardnode).LockedPtrcan not become an owning handle: it is returned by value from exported functions and its ctor/dtor are inlined in user binaries, so its layout (two raw pointers) is ABI.Fix
Attach a custom deleter to every
Entryat its only two allocation sites (both insrc/blackboard.cpp). When the lastshared_ptris dropped:deleteimmediately, no lock, no allocation;Compared with #1181:
unset()and~Blackboardalready inlined into binaries built against the 4.10 headers: they get the fix by just upgrading the library. A future removal path can not forget the protocol.getAnyLocked()is untouched (no second lookup / retry loop on the read path).blackboard.cpp; no layout or symbol change.Also in this PR (from review):
unset(),clear()andcloneInto()now destroy the removed values after releasingstorage_mutex_; doc note inLockedPtrthat the pointer is not valid afterunlock().Known limits (shared with #1181, inherent to the 16-byte handle)
try_lock()on a mutex owned by the calling thread (holder and remover on the same thread). Formally undefined forstd::mutex, but it simply fails on the supported platforms; documented in the code. TSan does not object.unlock()-> removal ->lock()on the same handle is still unsafe. A real fix needs an owning handle, i.e. an ABI break (BT.CPP 5).Verification
🤖 Generated with Claude Code