Repository navigation
collections: LinearFifo drops its live items and iterates both ring halves #39545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡
ordered_remove_itemrunsdrop_in_placebefore the shift and beforeself.count -= 1, so a panickingT::dropunwinds through a fifo whose live range still contains the already-dropped slot —LinearFifo::drop→discard(self.count)then drops it again.discard()explicitly guards against this ('Moveheadfirst so a panickingT::dropleaves the fifo consistent'); this sibling path should get the same treatment — e.g.ptr::readthe item out, do the shift +self.count -= 1, then drop the local. (Bun shipspanic = "abort", so this only bites undercargo test's unwind — flagging for consistency, not blocking.)Extended reasoning...
What the bug is
In the
offset != 0branch ofordered_remove_item, this PR adds:The
drop_in_placeruns before the shift and beforeself.count -= 1. IfT::droppanics and the panic unwinds,self.countis unchanged and the slot atoffset— now containing already-dropped bits — is still inside the live range[head, head+count). Unwinding then runsLinearFifo::drop(also new in this PR), which callsself.discard(self.count), whichdrop_in_place's the entire live range including that slot again. That's a double drop; for the PR's ownCountedtest type (which owns aBox<u32>) it would be a double free.Why this is inconsistent with the same PR's
discard()The PR explicitly designed
discard()to be panic-safe on exactly this hazard, with a comment stating the intent:ordered_remove_itemwas given the same drop responsibility in this PR but not the same panic guard. Per REVIEW.md "Fix the whole class in the same PR" — the two drop paths added here should be consistent.Step-by-step proof
Take a
LinearFifo<Counted, DynamicBuffer<_>>withhead=0, count=3, and supposeCounted::dropfor the item at offset 1 panics:ordered_remove_item(1)—offset != 0, sodrop_in_place(self.peek_item_mut(1))runs.Counted::droppanics. At the panic point,self.count == 3and slot 1 holds a droppedCounted(itsBox<u32>freed).LinearFifo.Drop::dropseesself.count == 3and callsself.discard(3).discard(3)computes(a, b) = as_mut_slices()covering slots[0, 3), moves head, thenptr::drop_in_place(a)— which drops slot 1 a second time. UB / double free of theBox.Contrast with
discard(): it movesheadpast the range before dropping, so on a panic during the drop loop the fifo's ownDropseescount == 0and does nothing.Impact
Low in practice:
Cargo.tomlsetspanic = "abort"for both dev and release (lines 144, 147), so in the shipped binary a panickingT::dropaborts the process — there is no unwind and no double drop.ordered_remove_item(the dev-serverweak_refsfifo insource_map_store.rs) stores a PODWeakRef {u32, u32, i64}with no drop glue, sodrop_in_placeon it is a no-op that cannot panic.cargo testbuilds unwind (the test harness forces it), and none of the new tests use a panickingDrop.So this cannot cause a concrete failure today. It's flagged as a nit because (a) the author clearly intended panic-safety here — they wrote the comment in
discard()— and (b) the two sibling drop paths added in the same PR should carry the same guard.How to fix
Match the
discard()pattern — make the fifo consistent before runningT::drop:If
drop(removed)panics, the fifo has already shifted and decrementedcount, soLinearFifo::dropdrops only the remaining live items — the removed one leaks instead of being dropped twice, mirroringdiscard()'s guarantee.