AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Low 35 Bitcoin

util.EventListener: store WeakMethods in CallbackManager to avoid leaks

Public commit record

What the developer wrote

Authored by SomberNight

100/100 · Strong
util.EventListener: store WeakMethods in CallbackManager to avoid leaks

This patch changes the CallbackManager to use WeakMethods (weakrefs) to
break the ref cycle and allow the GC to clean up the wallet objects.
unregister_callbacks() will also get called automatically, from
EventListener.__del__, to clean up the CallbackManager.

I also added a few unit tests for this.

fixes https://github.com/spesmilo/electrum/issues/10427

-----

original problem:

In many subclasses of `EventListener`, such as `Abstract_Wallet`, `LNWatcher`,
`LNPeerManager`, we call `register_callbacks()` in `__init__`.
`unregister_callbacks()` is usually called in the `stop()` method.

Example - consider the wallet object:
- `Abstract_Wallet.__init__()` calls `register_callbacks()`
- there is a `start_network()` method
- there is a `stop()` method, which calls `unregister_callbacks()`
- typically the wallet API user only calls `stop()` if they also called
`start_network()`.

This means the callbacks are often left registered, leading to the wallet
objects not getting GC-ed. The GC won't clean them up as
`util.callback_mgr.callbacks` stores strong refs to instance methods
of `Abstract_Wallet`, hence strong refs to the `Abstract_Wallet` objects.

An annoying example is `daemon.check_password_for_directory`, which
potentially creates wallet objects for all wallet files in the datadir.
It simply constructs the wallets, does not call `start_network()` and
neither does it call `stop()`.
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification✓ Links an issue, advisory, or supporting reference
The short version

What changed, and why it matters

This commit fixes a memory leak in Electrum's event-callback system. Wallet and Lightning objects registered event callbacks during creation but often never unregistered them, so they stayed in memory forever. The fix stores callbacks as weak references and automatically unregisters them when an object is destroyed, allowing Python's garbage collector to reclaim the memory.

Recommended action

Treat as a routine bug-fix / hardening patch. No immediate exploit path is evident, but users running long-lived Electrum daemons should update to avoid memory growth. Reviewers should verify that weakref callbacks are safely handled during event triggering and that WeakMethodProper equality behaves correctly under concurrent registration/unregistration.

Security signals we found

01

Memory leak / resource exhaustion in long-running daemon

02

Reference cycle between CallbackManager and wallet/LN objects

03

Missing cleanup of registered event callbacks

04

Weak reference conversion to break GC retention

05

Fallback destructor cleanup added

Risk score

Why this scored 35/100

Our methodology →
Potential impact 8/30
Exploitability 3/25
Stealth signal 6/15
Affected reach 7/15
Confidence 7/10
Evidence quality 4/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.