# Vulnerability Disclosure: Twin Memory Exhaustion DoS Vulnerabilities in Core Lightning

**URL:** <https://delvingbitcoin.org/t/vulnerability-disclosure-twin-memory-exhaustion-dos-vulnerabilities-in-core-lightning/2731>\
**Category:** Implementation\
**Tags:** lightning, fuzz\
**Created:** [July 19, 2026, 7:31am UTC](https://delvingbitcoin.org/t/vulnerability-disclosure-twin-memory-exhaustion-dos-vulnerabilities-in-core-lightning/2731 "2026-07-19T07:31:16Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Chand-ra](https://delvingbitcoin.org/user_avatar/delvingbitcoin.org/chand-ra/32/2064_2.png) [@Chand-ra](https://delvingbitcoin.org/u/Chand-ra)\
**Post date:** [July 19, 2026, 7:31am UTC](https://delvingbitcoin.org/t/vulnerability-disclosure-twin-memory-exhaustion-dos-vulnerabilities-in-core-lightning/2731/1 "2026-07-19T07:31:16Z")

</div>

## Summary

This post details two distinct [Denial-of-Service](https://en.wikipedia.org/wiki/Denial-of-service_attack) (DoS) vulnerabilities discovered in [Core Lightning](https://github.com/ElementsProject/lightning) (CLN). Both vulnerabilities allowed a remote peer to trigger unbounded memory growth, ultimately leading to Out-of-Memory (OOM) system crashes.

The first vulnerability affected the `connectd` daemon and was patched in [release v26.04](https://github.com/ElementsProject/lightning/releases/tag/v26.04). The second, related vulnerability affected the `gossipd` daemon and has been patched in [release v26.06rc2](https://github.com/ElementsProject/lightning/releases/tag/v26.06rc2). Both vulnerabilities stem from work initiated during my [Summer of Bitcoin](https://www.summerofbitcoin.org/) 2025 internship. All node operators are strongly advised to ensure they are running the latest patched versions.

## Background

Core Lightning utilizes a multi-daemon architecture designed to isolate faults. The Lightning Network relies on a [“gossip” protocol](https://github.com/lightning/bolts/blob/94eb038c42e664dd7862faeec6508ccd25f63ff8/07-routing-gossip.md) to propagate network state, including channel announcements, channel updates, and node announcements.

In CLN’s architecture, the [connect daemon](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/connectd/connectd.c) (`connectd`) receives external messages from peers and passes them to the [gossip daemon](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/gossipd/gossipd.c) (`gossipd`), which is responsible for managing the global view of the network. Because the network is inherently noisy, both daemons must operate with high efficiency. Furthermore, since they process untrusted external inputs, they must be robust against malicious message floods.

## Vulnerability 1: `connectd` Message Queue Exhaustion

### Discovery

This initial vulnerability was discovered using a new [fuzz target](https://github.com/google/fuzzing/blob/734e55f3cfed1adbb51bf6cb5c65b4c1197b7089/docs/why-fuzz.md) I developed during my SoB internship, `fuzz-gossipd-connectd`, which was aimed at testing the robustness of `gossipd`’s state machines.

Here is a brief overview of how the fuzz target works (see the corresponding [Pull Request](https://github.com/ElementsProject/lightning/pull/8423) for full details):

```c
/* 0 - In each fuzz run, perform the following steps: */
void run(const u8* data, size_t size) {

  /* 1 - Mock the required setup for connectd-gossipd communication. */
  initialize_setup(data, size);

  /* 2 - State loop. Repeat until there's no more fuzzer data left. */
  while (size) {

  /* 3 - Use a fuzzer byte as decision variable to perform one of the three possible operations: */
  switch (consume_int(data) % OP_COUNT) {

    case NEW PEER:
      /* 3.1 - Register a fake peer to attribute messages to. */
      connectd_new_peer(daemon, create_random_peer_id());
  
    case RECV GOSSIP:
      /* 3.2.1 - Generate a cryptographically valid gossip. Can be one of: */
      /* - Channel Announcements */
      /* - Channel Updates */
      /* - Node Announcements */
      /* - Reply channel_range */
      /* - Reply short_channel_ids_end */
      msg = create_gossip_msg(data, size, random_peer());

      /* 3.2.2 - Parse the gossip created. */
      handle_recv_gossip(daemon, msg);

    case PEER GONE:
      /* 3.3 - Delete a peer from the network map. */
      connectd_peer_gone(daemon, remove_random_peer());
  }
}

```

### The Bug

A remote attacker could trigger a DoS condition in the `connectd` sub-daemon by flooding it with a high volume of [`channel_update`](https://github.com/lightning/bolts/blob/94eb038c42e664dd7862faeec6508ccd25f63ff8/07-routing-gossip.md#the-channel_update-message) gossip messages. The root cause was a resource management issue in the inter-daemon [message queue](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/common/daemon_conn.c#L15) shared between `connectd` and `gossipd`.

[`do_enqueue()`](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/common/msg_queue.c#L48) allocated a new copy of every incoming message. Under a high-volume message flood, messages were enqueued far faster than they could be dequeued and processed, causing the internal queue to grow without bounds. While a [warning](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/common/msg_queue.c#L54) for excessive queue length (exceeding 250,000 items) existed, it was suppressed after the first instance due to a [`warned_once`](https://github.com/ElementsProject/lightning/blob/bc3b9b4f116b5c5386c730d2d2654348a71dba26/common/msg_queue.c#L8) flag. This left no further indication of the ongoing memory consumption as the queue swelled into the millions of messages, ultimately consuming all available RAM and triggering swap death.

## Vulnerability 2: `gossipd` Unknown SCID Map Exhaustion

### Discovery

Ironically, this second vulnerability was discovered while I was verifying the fix for the `connectd` bug described above.

### The Bug

Similar to the first bug, a remote attacker could trigger a DoS condition by flooding the node with a high volume of `channel_update` messages. However, in this case, the unbounded memory growth occurred in `gossipd`’s internal map for tracking unknown short channel IDs (SCIDs): [`unknown_scids`](https://github.com/ElementsProject/lightning/blob/73825bca7193c237693258e93572554e92bf1158/gossipd/seeker.c#L61).

When processing incoming updates via [`handle_recv_gossip()`](https://github.com/ElementsProject/lightning/blob/73825bca7193c237693258e93572554e92bf1158/gossipd/gossipd.c#L172), `gossipd` assumes that if an SCID is unrecognized, it missed the original announcement and passes the data to [`query_unknown_channel()`](https://github.com/ElementsProject/lightning/blob/73825bca7193c237693258e93572554e92bf1158/gossipd/seeker.c#L1153). To remember to query peers for these missing channels, the daemon calls [`add_unknown_scid()`](https://github.com/ElementsProject/lightning/blob/73825bca7193c237693258e93572554e92bf1158/gossipd/seeker.c#L688), which inserts every unique, fake SCID into an internal integer map (`uintmap`).

A continuous stream of unique, bogus SCIDs caused this `uintmap` to continuously allocate memory to split its internal nodes ([`split_node()`](https://github.com/ElementsProject/lightning/blob/73825bca7193c237693258e93572554e92bf1158/ccan/ccan/intmap/intmap.c#L53)). This caused `gossipd`’s memory consumption to balloon infinitely without logging any warnings.

## Verification & Execution

Both vulnerabilities are triggerable using the exact same attack program. The program acts as a malicious Lightning Network peer that connects to a CLN node and orchestrates a message flood to trigger the crash:

1. **Initialization and Connection:** The attacker establishes a connection to the victim CLN node.

2. **Initial Handshake:** The attacker completes the standard `init` message handshake with the victim.

3. **The Malicious Flood:** The attacker begins sending a continuous, high-volume stream of `channel_update` gossip messages to the victim node.

4. **Unbounded Growth and Crash:** Depending on the unpatched vulnerability present, either `connectd` queues the messages until memory is exhausted, or `gossipd` processes the bogus SCIDs and continuously expands its `uintmap` until all physical RAM is depleted, resulting in a system freeze.

## Criticality

| | |
| --- | --- |
| **Severity** | Medium/High (DoS) |
| **Attack Vector** | Remote (Peer-to-Peer) |
| **Consequence** | A remote attacker can reliably crash any publicly accessible CLN node by flooding it with peer-to-peer traffic. This requires no on-chain funds or channel relationship, making it a potent vector for disrupting network liquidity. |

## The Fixes

The architectural reasoning behind caching `channel_update` messages internally is to avoid losing gossip data for a channel that hasn’t been discovered yet. This allows the node to reach the latest state of a channel much faster when the corresponding [`channel_announcement`](https://github.com/lightning/bolts/blob/94eb038c42e664dd7862faeec6508ccd25f63ff8/07-routing-gossip.md#the-channel_announcement-message) eventually arrives, eliminating the need for the peer to rebroadcast the gossip. However, unbounded caching creates DoS vectors.

### Patch 1: `connectd` Message Queue

The optimal fix for the `connectd` queue was to simply drop messages after a specific cutoff point, which was decided to be 500,000 messages. More details can be found in the [Pull Request](https://github.com/ElementsProject/lightning/pull/8376/commits/f884813c241795ea1dd62e9e4d146496925000c2).

### Patch 2: `gossipd` Unknown SCIDs

Fortunately, Rusty Russell ([@rustyrussell](https://github.com/rustyrussell)) had already been working on a PR introducing an automatic compaction (garbage collection) mechanism for the gossip map. The patch introduces a new function that queries the store for the number of live and dead records, triggering a compaction if there are at least four times as many dead records as live ones (`num_dead` \< 4 \* `num_live`). This PR inadvertently fixed the second memory exhaustion issue. More details can be found in the [Pull Request](https://github.com/ElementsProject/lightning/pull/8903).

## Disclosure Timelines

### Vulnerability 1 (`connectd` exhaustion)

- **22/07/2025:** Fuzz target pushed upstream.

- **22/07/2025 - 31/07/2025:** Improvements made to the target, dealing with false positives.

- **01/08/2025:** Vulnerability confirmed using the attack program and disclosed to Matt (my SoB mentor at the time).

- **10/08/2025:** Investigation performed. Cause of the memory usage pinpointed.

- **11/08/2025:** Vulnerability independently confirmed by Matt.

- **12/08/2025:** Final vulnerability report sent to CLN’s security mailing list.

- **14/08/2025:** Reply by Rusty confirming the issue and disclosing the supposed fix.

- **18/08/2025:** Fix merged to master.

### Vulnerability 2 (`gossipd` exhaustion)

- **15/05/2026:** Vulnerability confirmed using the original attack program.

- **17/05/2026:** Investigation performed. Cause of the memory usage pinpointed to the SCID map.

- **18/05/2026:** Final vulnerability report sent to CLN’s security mailing list.

- **21/05/2026:** Reply by Rusty confirming the issue and identifying the compaction PR as the fix.

## Lessons Learned

These bugs emphasize that internal interfaces are attack surfaces too. Even if `connectd` handles external network throttling, it must also defend itself against internal floods. Fuzzing the IPC layer (daemon-to-daemon) is just as critical as fuzzing the external network layer.

Furthermore, this serves as a reminder of the interconnected nature of software vulnerabilities. The second bug would have been found sooner if someone had simply run the attack program I provided during my initial disclosure. I had already publicly posted the disclosure for the `connectd` DoS on my personal blog before independently finding the `gossipd` bug using the exact same script. It was entirely possible that a malicious actor could have read my first post, reproduced the attack program, and successfully exploited the network using the (then-unknown) second attack path. It is a fortunate coincidence that the compaction code was being revised at the same time.

_Special thanks to my SoB 2025 mentor, Matt Morehouse, for guiding the entire process, especially the triage and disclosure._

---

<div class="post-metadata">

**Author:** ![hevitisu](https://delvingbitcoin.org/user_avatar/delvingbitcoin.org/hevitisu/32/2463_2.png) [@hevitisu](https://delvingbitcoin.org/u/hevitisu)\
**Post date:** [October 27, 2026, 3:12pm UTC](https://delvingbitcoin.org/t/vulnerability-disclosure-twin-memory-exhaustion-dos-vulnerabilities-in-core-lightning/2731/2 "2026-10-27T15:12:16Z")

</div>

This post was flagged by the community and is temporarily hidden.

---

<div class="post-metadata">

**Author:** ![Chand-ra](https://delvingbitcoin.org/user_avatar/delvingbitcoin.org/chand-ra/32/2064_2.png) [@Chand-ra](https://delvingbitcoin.org/u/Chand-ra)\
**Post date:** [October 28, 2026, 2:34pm UTC](https://delvingbitcoin.org/t/vulnerability-disclosure-twin-memory-exhaustion-dos-vulnerabilities-in-core-lightning/2731/3 "2026-10-28T14:34:57Z")

</div>

You’re right, the post should read `num_dead >= 4 * num_live`, but I cannot figure out how to edit the post anymore 😔.
