Skip to content

fix(core): displace a container a migration re-key evicts from its slot#295

Merged
vieiralucas merged 2 commits into
mainfrom
audit/put-slot-lww-displace
Jul 16, 2026
Merged

fix(core): displace a container a migration re-key evicts from its slot#295
vieiralucas merged 2 commits into
mainfrom
audit/put-slot-lww-displace

Conversation

@vieiralucas

@vieiralucas vieiralucas commented Jul 16, 2026

Copy link
Copy Markdown
Member

Audit fix (cs-crdt-audit MEDIUM, displacement-signal class).

put_slot_lww — the LWW slot installer used by snapshot-migration re-keying — overwrote an occupied slot without displacing the live composite it evicted, unlike Map::set.

Impact: a migration renaming a leaf field onto a key holding a live container (Map/List/Text) with a higher stamp dropped the container from the slot but left it is_displaced() == false. A later op targeting the detached container passed the displaced guard and silently mutated it — diverging from the op seam (whose Map::set displaces the loser) until a snapshot decode ran mark_displaced and healed it.

  • Displace the evicted live composite before the overwriting insert, mirroring Map::evict; skip a same-handle re-install (the resurrect loop re-landing a container on its own key). Fixed in put_slot_lww so all three callers are covered.
  • Regression tests in state_project.rs: a leaf renamed onto a live map/list/text asserts the evicted container reads DISPLACED; a converge test asserts snapshot-seam and op-seam project identical bytes and that an op targeting the detached container is dropped on both seams.

Fails before the fix (container reads installed), passes after.


Summary by cubic

Fixes migration re-key behavior so put_slot_lww displaces any container it evicts, matching Map::set and preventing silent mutations of detached containers. Snapshot and op seams now converge when a leaf is renamed onto a key holding a live Map/List/Text.

  • Bug Fixes
    • In Map::put_slot_lww, displace the evicted live composite before the overwrite; skip displacement on same-handle re-installs.
    • Added regression tests that a leaf renamed onto a live map/list/text marks the container as displaced and that snapshot-seam and op-seam project identical bytes; ops targeting the detached container are dropped on both seams.

Written for commit 545c5e0. Summary will update on new commits.

Review in cubic

put_slot_lww overwrote an occupied slot without displacing the live
composite it evicted, unlike Map::set. A migration renaming a leaf field
onto a key holding a live container with a higher stamp dropped the
container from the slot but left it is_displaced()==false — so a later op
targeting the detached container passed the displaced guard and silently
mutated it, diverging from the op seam until a snapshot decode healed it.

Displace the evicted live composite before the overwriting insert,
mirroring Map::evict, skipping a same-handle re-install (the resurrect
loop re-landing a container on its own key). Covers all three callers.
@vieiralucas
vieiralucas merged commit e0ff88f into main Jul 16, 2026
9 checks passed
@vieiralucas
vieiralucas deleted the audit/put-slot-lww-displace branch July 16, 2026 12:03
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.

1 participant