What changed, and why it matters
This commit fixes a race condition in how a background worker (called an 'isolate') for the Spark privacy feature is initialized. Previously, if two parts of the app tried to start the isolate at nearly the same time, both could try to create it, which could waste resources, cause errors, or leave the wallet in an inconsistent state. The fix uses a one-time 'Completer' so that any concurrent callers wait for the first initialization to finish instead of spawning duplicate isolates.
Treat as a stability/concurrency hardening fix. Review whether duplicate isolate creation could have caused observable failures (crashes, hangs, or inconsistent Spark state) and consider regression tests for concurrent initialize() calls. No immediate security patch urgency is indicated by the diff alone.
Security signals we found
Concurrency / race condition in isolate initialization
Potential duplicate resource creation (Isolate.spawn)
Defensive fix with no direct exploit evidence in diff
Evidence from the diff
The patch adds a static Completer to _SparkIsolate.initialize() in spark_interface.dart. It guards isolate creation so that subsequent callers await the same initialization future rather than spawning a second Isolate. This prevents duplicate isolate creation and ensures _sendPort is set exactly once. The change is defensive and corrects a concurrency bug, but the diff is small and does not show any exploit path by itself.
Changed components
lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dartSpark isolate initializationInspect captured patch +13 / −0
diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
index ecf7c94..f3729b2 100644
--- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
+++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'dart:math';
@@ -66,7 +67,18 @@ abstract class _SparkIsolate {
static SendPort? _sendPort;
static final ReceivePort _receivePort = ReceivePort();
+ static Completer<void>? completer;
+
static Future<void> initialize() async {
+ if (completer != null) {
+ if (!completer!.isCompleted) {
+ await completer!.future;
+ }
+
+ return;
+ }
+ completer = Completer();
+
final level = Prefs.instance.logLevel;
_isolate = await Isolate.spawn((SendPort sendPort) {
@@ -88,6 +100,7 @@ abstract class _SparkIsolate {
});
}, _receivePort.sendPort);
_sendPort = await _receivePort.first as SendPort;
+ completer!.complete();
}
static Future<R> run<M, R>(ComputeCallback<M, R> task, M argument) async {
Why this scored 29/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.