Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

fix(db/web): the identity wipe runs outside the Web Locks section a concurrent patch can resurrect wiped rows

Open
#571 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust
Domain
databases

Research direction

Start in rust/src/db/indexeddb.rs with exclusive(), clear_identity_data, and the three patch/update cycles named in the issue; then read web_lock.rs for the origin-wide lock behavior. Implement the specified lock ordering and cover the wipe-versus-write races, including mark_messages_read. Done when these storage-layer cycles cannot resurrect wiped rows, without changing the stated out-of-scope behavior.

Written by the indexing model from the issue text.

Description

On web, clear_identity_data (rust/src/db/indexeddb.rs:500) erases the identity's stores — trades, messages, bond claims, outbox, orders, the identity-scoped settings keys — in one read-write transaction. But unlike the whole-document writes in that file, it never enters the exclusive section: exclusive() (indexeddb.rs:230) takes the in-process patch_serial mutex plus the origin-wide Web Lock (web_lock::acquire), and the wipe takes neither.

The read-modify-write cycles it races against read a document, change a field and write the document back whole. Interleaved with the wipe:

  1. Tab B — or a concurrent task in the same tab, since patch_serial is not held either — reads trade document X.
  2. Tab A's wipe commits: every store cleared.
  3. Tab B writes document X back whole.

The wiped row is resurrected into the next identity's session — exactly the leak delete_identity promises does not happen (issue #533). The wipe's own atomicity ("all of it commits or none does", indexeddb.rs:528) protects against a partial wipe, not against this: the resurrection happens after the transaction committed. Three cycles have the shape:

  • Trade patches, under TRADES_LOCK (patch_trade_by_order_id, indexeddb.rs:257).
  • Outbox status updates, under OUTBOX_LOCK (update_queued_message_status, indexeddb.rs:431).
  • mark_messages_read (indexeddb.rs:367), under no lock at all — not even patch_serial: it lists the trade's messages, flips is_read and saves each back whole. This is also the easiest window to hit, since opening a chat is what triggers it.

An IndexedDB database is shared by every tab and worker of the origin (web_lock.rs:4), so two open tabs are enough.

Native is unaffected: SQLite does these as single atomic UPDATEs in one process (mark_messages_read at sqlite.rs:333, trade patches via json_set), which is why only the web store needs the locks.

Proposed fix

  • Add a MESSAGES_LOCK beside TRADES_LOCK and OUTBOX_LOCK, and make mark_messages_read enter the exclusive section — that also closes its same-tab interleaving, which exists today independently of the wipe.
  • Make clear_identity_data enter the exclusive section before opening its transaction: patch_serial plus all three origin locks, acquired in a fixed, documented order so a future caller taking more than one lock cannot deadlock against it.
  • Orders, bond claims and settings need no lock of their own: at the storage layer they are plain puts/deletes (save_order, save_bond_claim, set_setting).

Out of scope

  • A tab still running the old identity can keep plain-writing (new chat messages, new rows) after another tab's wipe — a wider multi-tab identity-coherence question, not a locking gap.
  • Read-modify-write cycles one layer above the storage API — e.g. a bond claim is read with get_bond_claim, changed and written back with save_bond_claim (orders.rs:4303), and the push_registrations settings family follows the same pattern — are cross-tab races of that same coherence question, not of the store's lock discipline. Noted here so they don't read as missed; this issue only covers the storage-layer cycles the existing locks were built for.

Related

Found while verifying #555 (itself from the review of #543 / issue #533).

Dominant language
Dart
Stars
11
Forks
9
Avg merge
13h 4m
Merged PRs (30d)
259

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from MostroP2P/app

All issues in MostroP2P/app

Similar issues

More Dart issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.