Repository navigation
Fix double free when retain unwinds - #4
Merged
OverwrittenCode merged 3 commits intoSep 22, 2026
Merged
OverwrittenCode merged 3 commits into
OverwrittenCode merged 3 commits into
Conversation
tooson9010-spec
force-pushed
the
fix-panic-safety-double-free
branch
2 times, most recently
from
August 27, 2026 04:40
736589d to
d2e5aed
Compare
retain compacts kept elements with copy_nonoverlapping and commits the new length only after the loop. Both the predicate and a removed element's Drop are user code, so an unwind leaves either a duplicated slot or an already-dropped one inside 0..len, and StackVec::drop destroys it again. Use a backshift guard that restores a correct length on every exit path, matching what Vec::retain does.
tooson9010-spec
force-pushed
the
fix-panic-safety-double-free
branch
from
August 27, 2026 04:42
d2e5aed to
cbaf9e2
Compare
OverwrittenCode
self-requested a review
September 22, 2026 18:26
Signed-off-by: OverwrittenCode <83100104+OverwrittenCode@users.noreply.github.com>
Signed-off-by: OverwrittenCode <83100104+OverwrittenCode@users.noreply.github.com>
OverwrittenCode
approved these changes
Sep 22, 2026
OverwrittenCode
left a comment
Owner
There was a problem hiding this comment.
Verified the fix locally: cargo miri test --all-features and
cargo clippy --all-features -- -D warnings both pass, including
the two new panic-safety regression tests.
Pushed two small fixups on top:
- resolved a Stacked Borrows violation Miri caught in the
BackshiftOnDrop guard and the retain loop (interleaved
as_ptr()/as_mut_ptr() calls were invalidating each other's
reborrows) - moved the item definitions before the first statement and
split a 3-op unsafe block to satisfy clippy::pedantic
Thanks for the clear repro and the regression tests - good catch.
4 tasks done
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.
retaincompacts kept elements withptr::copy_nonoverlappingand commits thenew length only after the loop. Two ways a panic leaves the buffer unsound:
copy_nonoverlappingleaves thesource slot as a byte-for-byte duplicate, so both copies stay inside
0..lenand
StackVec::dropfrees the same value twice. No elementDrophas topanic — an
unwrap()orassert!in the predicate is enough.Dropunwinds. The slot was already destroyed but staysinside
0..len.Reported in #3.
Reproducer
The added tests count destructor calls on a 4-element vec. Both paths destroy
more than exist:
AddressSanitizer reports
attempting double-freewith a heap-owning elementtype.
The fix
Retire the buffer up front and let a backshift guard restore a correct length on
every exit path, the same shape
Vec::retainuses. The removed slot is countedout before
drop_in_place, so a panicking elementDropcan't be revisitedeither.
Regression test
Two cases in
src/stack_vec.rs, one per path.cargo testcatches both, nosanitizer needed;
retain_with_dropsand theretaindoc-test still pass.