Skip to content

Bug: WalletModel.continuations dictionary accessed without synchronization #3573

@alltheseas

Description

@alltheseas

Problem

The continuations dictionary in WalletModel is accessed from network callbacks (handle_nwc_response) and UI thread (waitForResponse) without synchronization, causing data races and potential crashes.

Location

damus/Features/Wallet/Models/WalletModel.swift lines 46, 212-240

class WalletModel: ObservableObject {
    private var continuations: [NoteId: CheckedContinuation<WalletConnect.Response.Result, any Error>] = [:]
    
    func handle_nwc_response(response: WalletConnect.FullWalletResponse) {
        // Called from background thread (network callback)
        self.resume(request: response.req_id, with: result)  // RACE
    }
    
    func waitForResponse(for requestId: NoteId, ...) async throws -> ... {
        return try await withCheckedThrowingContinuation({ continuation in
            self.continuations[requestId] = continuation  // RACE
        })
    }
}

Root Cause

Swift dictionaries are not thread-safe. Concurrent read/write from different threads causes undefined behavior.

Impact

  • Crash: "Invalid memory access" or EXC_BAD_ACCESS
  • Lost wallet responses
  • Continuation resumed twice

Suggested Fix

Either:

  1. Add NSLock protection around all continuations access
  2. Convert WalletModel to an actor (preferred per Swift concurrency guidelines)

Test Plan

  • Test multiple concurrent wallet requests
  • Test response arriving during request setup
  • Run with ThreadSanitizer

Changelog-Fixed: Fix race condition in WalletModel continuation handling

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not working, or not working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions