See how protocol compares to other vendors in security performance
go-ipld-prime is a series of Go interfaces for manipulating IPLD data and a Go module that contains the go-ipld-prime/codec/json codec.
Impact
Encoding data which contains a Bytes kind Node will pass a Bytes token to the JSON encoder which will panic as it doesn't expect to receive Bytes tokens. Such an encoding should be treated as an error, as plain JSON should not be able to encode Bytes.
This only impacts uses of the "json" codec, "dag-json" is not impacted. Use of "json" as a decoder is not impacted.
Patches
Fixed in v0.19.0.
Workarounds
Prefer the "dag-json" codec which has the ability to encode bytes.
References
See fix in #472
Description Summary The Rust libp2p Gossipsub implementation contains a remotely reachable panic in backoff expiry handling. After a peer sends a crafted PRUNE control message with an attacker-controlled, near-maximum backoff value, the value is accepted and stored as an Instant near the representable upper bound. On a later heartbeat, the implementation performs unchecked Instant + Duration arithmetic (backofftime + slack), which can overflow and panic with: overflow when adding duration to instant This issue is reachable from any Gossipsub peer over normal TCP + Noise + mplex/yamux connectivity and requires no further authentication beyond becoming a protocol peer. Attack Scenario An attacker that can establish a libp2p Gossipsub session with a target node can crash the target by sending crafted PRUNE control data: 1. Establish a standard libp2p session (TCP + Noise) and negotiate a stream multiplexer (mplex/yamux). 2. Open a Gossipsub stream and send an RPC containing ControlPrune with a very large backoff (chosen near boundary conditions, e.g. ~ i64::MAX - victimuptimeseconds; example observed: 9223372036854674580 for ~28h uptime). 3. The value is parsed from protobuf and passed through Behaviour::handleprune() into mesh/backoff update logic. 4. Initial storage path uses checked addition (Instant::now().checkedadd(...)), so the malicious near-max value is retained. 5. On the next heartbeat (typically within ~43–74s), expiry logic computes backofftime + slack using unchecked addition, which overflows and panics. Impact Remote unauthenticated denial of service (critical). Any application exposing an affected libp2p-gossipsub listener can be crashed by a network-reachable peer that sends crafted PRUNE backoff values. The crash is triggered during heartbeat processing (not immediately at PRUNE parse time), and can be repeated by reconnecting and replaying the message.
Differences from CVE-2026-33040 This advisory is related to CVE-2026-33040 but it is not the same defect. CVE-2026-33040 addressed overflow during backoff insertion by adding checked arithmetic when converting PRUNE backoff into an Instant. The issue in this advisory occurs at a different location and at a different time: a near-maximum backoff can still be stored successfully, and the crash happens later in the heartbeat path when slack is added to that stored Instant using unchecked arithmetic. This report covers a distinct secondary overflow path in heartbeat expiry handling that remained reachable after the original insertion-side hardening.
This vulnerability was originally reported by the Security team of the Ethereum Foundation.
Summary The Rust libp2p Gossipsub implementation accepts attacker-controlled PRUNE backoff values and may perform unchecked time arithmetic when storing backoff state. A specially crafted PRUNE control message with an extremely large backoff (e.g. u64::MAX) can lead to Duration/Instant overflow during backoff update logic, triggering a panic in the networking state machine. This is remotely reachable over a normal libp2p connection and does not require authentication.
Attack Scenario An attacker that can establish a libp2p Gossipsub session with a target node can crash the target by sending a single crafted PRUNE control message: 1. Establish a standard libp2p transport session and negotiate a stream multiplexer. 2. Open a Gossipsub stream and negotiate the meshsub protocol. 3. Send one protobuf RPC containing ControlPrune with a very large backoff value (e.g. 18446744073709551615 / u64::MAX). When processed, the oversized backoff can reach time-update logic that adds Duration::fromsecs(backoff) to Instant::now(), causing overflow and panic.
Impact Remote unauthenticated denial of service. Any application exposing a libp2p Gossipsub listener and using the affected backoff-handling path can be crashed by a network attacker that can reach the service port. The attack can be repeated by reconnecting and replaying the crafted control message. Patches Users should upgrade to a release that hardens Gossipsub backoff handling.
This vulnerability was originally submitted by @revofusion to the Ethereum Foundation bug bounty program
Summary
Thelibp2p-rendezvous server has no limit on how many namespaces a single peer can register. A malicious peer can repeatedly register unique namespaces in a loop, and the server accepts the requests, allocating memory for each registration without pushback. If an attacker continues submitting malicous requests for long enough, (or with multiple sybil peers) the server process crashes due to OOM.
No auth is required; therefore, any peer on the network can do this.
Details
the bug is in Registrations::add() inside protocols/rendezvous/src/server.rs.
the store uses a BiMap keyed on (PeerId, Namespace) so yes, a peer can't register the same namespace twice. but there's nothing stopping it from registering 10,000 different namespaces. each unique one gets its own entry in:
- registrationsforpeer (BiMap) - registrations (HashMap) - nextexpiry (FuturesUnordered a new heap-allocated BoxFuture per registration)
namespace strings are only validated for length (MAXNAMESPACE = 255), not count. there's no maxregistrationsperpeer anywhere in Config or the rest of the codebase.
making it worse MAXTTL = 72 hours. so every registration just sits there for up to 3 days. disconnecting doesn't clean anything up either, entries only go away when the TTL fires.
protocols/rendezvous/src/server.rs └── Registrations::add() ← no per-peer count check anywhere
protocols/rendezvous/src/lib.rs ├── MAXNAMESPACE = 255 ← length capped, count is not └── MAXTTL = 72h ← entries persist a long time
fix would be adding something like maxregistrationsperpeer to Config and checking it at the top of add() before inserting anything.
PoC
tested on libp2p v0.56.1, built from source.
step 1 - start the rendezvous server (uses the example from the repo): bash cargo run --manifest-path examples/rendezvous/Cargo.toml --bin rendezvous-example
step 2 - run the flood client (attached as rzv-flood.rs): bash cargo run --manifest-path examples/rendezvous/Cargo.toml --bin rzv-flood
it connects as a single peer and registers 10,000 unique namespaces (flood-00000000 through flood-00009999), chaining each registration on the confirmed Registered event from the previous one.
server accepted every single one. not one rejection.
memory on the server side (via ps aux RSS column):
baseline: ~18 MB mid flood: ~26 MB after 10k regs: ~28 MB
that's from one peer. scale to 100 sybil peers doing the same thing and you're looking at ~1GB. 1000 peers and the server is dead.
<img width="1032" height="124" alt="image" src="https://github.com/user-attachments/assets/f778f179-2aa1-4485-940c-25e218733fa8" />
server RSS climbing during the flood
<img width="553" height="760" alt="image" src="https://github.com/user-attachments/assets/691b0f52-dda0-443f-a3c2-98c8c6336f2f" />
10,000 registrations confirmed, zero rejected
Impact
any node running libp2p-rendezvous server-side is affected. rendezvous servers are typically well-known, publicly reachable nodes taking one down disrupts peer discovery for all clients depending on it. any rust-libp2p based project that deploys a rendezvous point is at risk.
no special position on the network needed. no crypto work. just open a connection and send REGISTER in a loop.
Summary The rendezvous server stores pagination cookies without bounds. An unauthenticated peer can repeatedly issue DISCOVER requests and force unbounded memory growth.
Details
Pagination state is stored in:
rs HashMap<Cookie, HashSet<RegistrationId>>
On Message::Discover:
remote peer → DISCOVER → handlerequest → registrations.get(...) → new cookie generated → cookie inserted into Registrations::cookies
There is no upper bound or eviction policy, so repeated DISCOVER requests grow this map indefinitely.
PoC A reproduction test and minimal harness will be provided in a private fork in a follow-up comment.
Impact
Remote state amplification leading to memory exhaustion.
Properties:
- etwork reachable - no authentication required - low attack complexity - protocol-compliant traffic
Impacts rendezvous nodes exposed to untrusted peers. ---
Possible Fixes
1. Global cap + eviction
Bound cookie storage (MAXCOOKIESTRACKED) with FIFO/expiry aware eviction. Tradeoff: attacker can churn cookies and evict legitimate pagination state.
2. Stateless cookies
Encode pagination state in authenticated cookies instead of storing server-side state. Tradeoff: more complex implementation.
3. Rate limiting / per-peer quotas
Limit cookie creation per peer. Tradeoff: requires peer tracking.
go-ipld-prime is an implementation of the InterPlanetary Linked Data (IPLD) spec interfaces, a batteries-included codec implementations of IPLD for CBOR and JSON, and tooling for basic operations on IPLD objects. Prior to 0.22.0, the DAG-CBOR decoder uses collection sizes declared in CBOR headers as Go preallocation hints for maps and lists. The decoder does not cap these size hints or account for their cost in its allocation budget, allowing small payloads to cause excessive memory allocation. This vulnerability is fixed in 0.22.0.
Summary The Rust implementation of Yamux can panic when processing a crafted inbound Data frame that sets SYN and uses a body length greater than DEFAULTCREDIT (e.g. 262145). On the first packet of a new inbound stream, stream state is created and a receiver is queued before oversized-body validation completes. When validation fails, the temporary stream is dropped and cleanup may call remove(...).expect("stream not found"), triggering a panic in the connection state machine. This is remotely reachable over a normal Yamux session and does not require authentication. kind of vulnerability is it? Who is Attack Scenario An attacker that can establish a Yamux session with a target node can crash the target by sending a single validly encoded Yamux Data|SYN frame with an oversized body: 1. Establish a standard authenticated transport session that negotiates Yamux. 2. Send one Yamux frame with: - Tag = Data - Flags = SYN - StreamId = 1 (or any new inbound stream id) - Length = DEFAULTCREDIT + 1 (e.g. 262145) - Body of matching size This can trigger a panic (stream not found) and terminate the process, depending on host application panic policy. Patches Users should upgrade to yamux v0.13.10
This vulnerability was originally submitted by @revofusion to the Ethereum Foundation bug bounty program
Sumary The Rust implementation of Yamux accepts WindowUpdate credit values from the remote peer and applies them to per-stream send-window state. A specially crafted WindowUpdate can cause arithmetic overflow in send-window accounting, which triggers a panic in the connection state machine. This is remotely reachable over a normal network connection and does not require authentication. Attack Scenario An attacker that can establish a Yamux session with a target node can crash the target by sending two validly encoded Yamux frames: 1. Open a stream (e.g. DATA + SYN) so the stream exists with initial send-window state (DEFAULTCREDIT). 2. Send a WindowUpdate on that stream with a very large credit value (e.g. 0xFFFF0000) such that adding credit to the current send-window overflows u32. Impact Remote unauthenticated denial of service. An attacker can repeatedly trigger panics by reconnecting and replaying the crafted frame sequence. Patches Users should upgrade to yamux v0.13.9
This vulnerability was originally submitted by @revofusion to the Ethereum Foundation bug bounty program
libp2p-rust is the official rust language Implementation of the libp2p networking stack. In versions prior to 0.45.1 an attacker node can cause a victim node to allocate a large number of small memory chunks, which can ultimately lead to the victim’s process running out of memory and thus getting killed by its operating system. When executed continuously, this can lead to a denial of service attack, especially relevant on a larger scale when run against more than one node of a libp2p based network. Users are advised to upgrade to libp2p v0.45.1 or above. Users unable to upgrade should reference the DoS Mitigation page for more information on how to incorporate mitigation strategies, monitor their application, and respond to attacks: https://docs.libp2p.io/reference/dos-mitigation/.
js-libp2p is the official javascript Implementation of libp2p networking stack. Versions older than v0.38.0 of js-libp2p are vulnerable to targeted resource exhaustion attacks. These attacks target libp2p’s connection, stream, peer, and memory management. An attacker can cause the allocation of large amounts of memory, ultimately leading to the process getting killed by the host’s operating system. While a connection manager tasked with keeping the number of connections within manageable limits has been part of js-libp2p, this component was designed to handle the regular churn of peers, not a targeted resource exhaustion attack. Users are advised to update their js-libp2p dependency to v0.38.0 or greater. There are no known workarounds for this vulnerability.
go-libp2p is the offical libp2p implementation in the Go programming language. Version 0.18.0 and older of go-libp2p are vulnerable to targeted resource exhaustion attacks. These attacks target libp2p’s connection, stream, peer, and memory management. An attacker can cause the allocation of large amounts of memory, ultimately leading to the process getting killed by the host’s operating system. While a connection manager tasked with keeping the number of connections within manageable limits has been part of go-libp2p, this component was designed to handle the regular churn of peers, not a targeted resource exhaustion attack. Users are advised to upgrade their version of go-libp2p to version 0.18.1 or newer. Users unable to upgrade may consult the denial of service (dos) mitigation page for more information on how to incorporate mitigation strategies, monitor your application, and respond to attacks.
go-merkledag implements the 'DAGService' interface and adds two ipld node types, Protobuf and Raw for the ipfs project. A ProtoNode may be modified in such a way as to cause various encode errors which will trigger a panic on common method calls that don't allow for error returns. A ProtoNode should only be able to encode to valid DAG-PB, attempting to encode invalid DAG-PB forms will result in an error from the codec. Manipulation of an existing (newly created or decoded) ProtoNode using the modifier methods did not account for certain states that would place the ProtoNode into an unencodeable form. Due to conformance with the github.com/ipfs/go-block-format#Block and github.com/ipfs/go-ipld-format#Node interfaces, certain methods, which internally require a re-encode if state has changed, will panic due to the inability to return an error. This issue has been addressed across a number of pull requests. Users are advised to upgrade to version 0.8.1 for a complete set of fixes. Users unable to upgrade may attempt to mitigate this issue by sanitising inputs when allowing user-input to set a new CidBuilder on a ProtoNode and by sanitising Tsize (Link#Size) values such that they are a reasonable byte-size for sub-DAGs where derived from user-input.
GossipSub 1.1, as used for Ethereum 2.0, allows a peer to maintain a positive score (and thus not be pruned from the network) even though it continuously misbehaves by never forwarding topic messages.
The dag-pb codec can panic when decoding invalid blocks.
github.com/ipfs/go-unixfsnode is an ADL IPLD prime node that wraps go-codec-dagpb's implementation of protobuf to enable pathing. In versions priot to 1.5.2 trying to read malformed HAMT sharded directories can cause panics and virtual memory leaks. If you are reading untrusted user input, an attacker can then trigger a panic. This is caused by bogus fanout parameter in the HAMT directory nodes. Users are advised to upgrade. There are no known workarounds for this vulnerability.
go-bitfield is a simple bitfield package for the go language aiming to be more performant that the standard library. When feeding untrusted user input into the size parameter of NewBitfield and FromBytes functions, an attacker can trigger panics. This happen when the size is a not a multiple of 8 or is negative. There were already a note in the NewBitfield documentation, however known users of this package are subject to this issue. Users are advised to upgrade. Users unable to upgrade should ensure that size is a multiple of 8 before calling NewBitfield or FromBytes.
Impact Trying to read malformed HAMT sharded directories can cause panics and virtual memory leaks. If you are reading untrusted user input, an attacker can then trigger a panic.
This is caused by bogus fanout parameter in the HAMT directory nodes. This include checks returned in ipfs/go-bitfield GHSA-2h6c-j3gf-xp9r, as well as limiting the fanout to <= 1024 (to avoid attempts of arbitrary sized allocations).
Patches - https://github.com/ipfs/go-unixfs/commit/dbcc43ec3e2db0d01e8d80c55040bba3cf22cb4b
Workarounds Do not feed untrusted user data to the decoding functions.
References - https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r
Impact An attacker is able allocate arbitrarily many bytes in the Bitswap server by sending many WANTBLOCK and or WANTHAVE requests which are queued in an unbounded queue, with allocations that persist even if the connection is closed. This affects users accepting untrusted connections with the Bitswap server, this also affects users using the old API stubs at github.com/ipfs/boxo/bitswap because it transitively uses github.com/ipfs/boxo/bitswap/server.
We have renamed go-libipfs to boxo; this document uses both terms interchangeably. The version numbers for both are applicable, as they share the same historical timeline.
Remediation Apply one of: - Update boxo to v0.6.0 or later - Update boxo to v0.4.1 Note that v0.5.0 is NOT safe, v0.4.1 is a backport of the v0.6.0 security fixes on top of v0.4.0.
Mitigations 1. The server now limits how many wantlist entries per peer it knows. The MaxQueuedWantlistEntriesPerPeer option allows configuring how many wantlist entries the server remembers; if a peer sends a wantlist bigger than this (including a sum of multiple delta updates) the server will truncate the wantlist to the match the limit. This defaults to 1024 entries per peer. 2. The server now properly clears state about peers when they disconnect. Peer state is more lazily allocated (only when a wantlist is received in the first place) and is properly cleared when the PeerDisconnected callback is received. 3. The server now ignores CIDs above some size. Clients were able to send any CID as long as the total protobuf message were bellow the 4MiB limit. This is allowed to allocate lots of memory with very little entries. This can be configured using the MaxCidSize option and defaults to 168 bytes. 4. The server now closes the connection if an inline CID is requested (either as WANT or CANCEL). The attack were more effective if done with CIDs that are present in target's blockstore, this is because this will push longer-lasting jobs on some priority queue. Since inline CID are literal data (instead of hashes of data), everyone always "has" any inline CID (since instead of loading the data from disk, it can be extracted from the CID). It makes no sense for anyone to ever ask you about an inline CID since they could also just parse it themselves. Thus, as a defensive measure, we kill the connection with peers that ask about an inline CID.
Vulnerable symbols - github.com/ipfs/go-libipfs/bitswap/server/internal/decision.(Engine).MessageReceived - github.com/ipfs/go-libipfs/bitswap/server/internal/decision.(Engine).NotifyNewBlocks - github.com/ipfs/go-libipfs/bitswap/server/internal/decision.(Engine).findOrCreate - github.com/ipfs/go-libipfs/bitswap/server/internal/decision.(Engine).PeerConnected
Patches - https://github.com/ipfs/boxo/commit/9cb5cb54d40b57084d1221ba83b9e6bb3fcc3197 (mitigations 1 and 2) - https://github.com/ipfs/boxo/commit/62cbac40b96f49e39cd7fedc77ee6b56adce4916 (mitigations 3 and 4) - https://github.com/ipfs/boxo/commit/baa748b682fabb21a4c1f7628a8af348d4645974 (tests)
Workarounds If you are using the stubs at github.com/ipfs/go-libipfs/bitswap and not taking advantage of the features provided by the server, refactoring your code to use the new split API will allow you to run in a client-only mode using: github.com/ipfs/boxo/bitswap/client.
Summary In go-libp2p, by using signed peer records a malicious actor can store an arbitrary amount of data in a remote node’s memory. This memory does not get garbage collected and so the victim can run out of memory and crash.
It is feasible to do this at scale. An attacker would have to transfer ~1/2 as much memory it wants to occupy (2x amplification factor).
The attacker can perform this attack over time as the target node’s memory will not be garbage collected.
This can occur because when a signed peer record is received, only the signature validity check is performed but the sender signature is not checked. Signed peer records from randomly generated peers can be sent by a malicious actor. A target node will accept the peer record as long as the signature is valid, and then stored in the peer store.
There is cleanup logic in the peer store that cleans up data when a peer disconnects, but this cleanup is never triggered for the fake peer (from which signed peer records were accepted) because it was never “connected”.
Impact If users of go-libp2p in production are not monitoring memory consumption over time, it could be a silent attack i.e. the attacker could bring down nodes over a period of time (how long depends on the node resources i.e. a go-libp2p node on a virtual server with 4 gb of memory takes about 90 sec to bring down; on a larger server, it might take a bit longer.)
Patches Update your go-libp2p dependency to the latest release, v0.30.0 at the time of writing.
If you'd like to stay on the 0.27.x release, we strongly recommend users to update to go-libp2p 0.27.7. Though this OOM issue was fixed in 0.27.4, there were subsequent patch releases afterwards (important fixes for other issues unrelated to the OOM).
Workarounds None
An issue was discovered in the multihash crate before 0.11.3 for Rust. The fromslice parsing code can panic via unsanitized data from a network server.
go-ipfs is an open-source golang implementation of IPFS which is a global, versioned, peer-to-peer filesystem. In go-ipfs before version 0.8.0, control characters are not escaped from console output. This can result in hiding input from the user which could result in the user taking an unknown, malicious action. This is fixed in version 0.8.0.
go-ipfs is an open-source golang implementation of IPFS which is a global, versioned, peer-to-peer filesystem. In go-ipfs before version 0.8.0-rc1, it is possible for path traversal to occur with DAGs containing relative paths during retrieval. This can cause files to be overwritten, or written to incorrect output directories. The issue can only occur when a get is done on an affected DAG. This is fixed in version 0.8.0-rc1.
Gossipsub 1.0 does not properly resist invalid message spam, such as an eclipse attack or a sybil attack.
An issue was discovered in IPFS (aka go-ipfs) 0.4.23. An attacker can generate ephemeral identities (Sybils) and leverage the IPFS connection management reputation system to poison other nodes' routing tables, eclipsing the nodes that are the target of the attack from the rest of the network. Later versions, in particular go-ipfs 0.7, mitigate this.