Skip to content

fix: arithmetic overflow in mDNS TTL implementation #5967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ libp2p-gossipsub = { version = "0.49.0", path = "protocols/gossipsub" }
libp2p-identify = { version = "0.47.0", path = "protocols/identify" }
libp2p-identity = { version = "0.2.10" }
libp2p-kad = { version = "0.47.0", path = "protocols/kad" }
libp2p-mdns = { version = "0.47.0", path = "protocols/mdns" }
libp2p-mdns = { version = "0.47.1", path = "protocols/mdns" }
libp2p-memory-connection-limits = { version = "0.4.0", path = "misc/memory-connection-limits" }
libp2p-metrics = { version = "0.16.1", path = "misc/metrics" }
libp2p-mplex = { version = "0.43.1", path = "muxers/mplex" }
Expand Down
5 changes: 5 additions & 0 deletions protocols/mdns/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.47.1

- Fixed a Arithmetic overflow bug in the mDNS implementation method processing response packets with extremely large TTL values.
See [PR 5967](https://github.com/libp2p/rust-libp2p/pull/5967)

## 0.47.0

- Emit `ToSwarm::NewExternalAddrOfPeer` on discovery.
Expand Down
2 changes: 1 addition & 1 deletion protocols/mdns/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "libp2p-mdns"
edition.workspace = true
rust-version = { workspace = true }
version = "0.47.0"
version = "0.47.1"
description = "Implementation of the libp2p mDNS discovery method"
authors = ["Parity Technologies <[email protected]>"]
license = "MIT"
Expand Down
84 changes: 84 additions & 0 deletions protocols/mdns/src/behaviour/iface/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ impl MdnsResponse {
) -> impl Iterator<Item = (PeerId, Multiaddr, Instant)> + '_ {
self.discovered_peers()
.filter(move |peer| peer.id() != &local_peer_id)
.filter(move |peer| now.checked_add(peer.ttl()).is_some())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(move |peer| peer.id() != &local_peer_id)
.filter(move |peer| now.checked_add(peer.ttl()).is_some())
.filter(move |peer| peer.id() != &local_peer_id && peer.ttl() > now)

No need for multiple Filter wrappers.

Copy link
Member

@elenaf9 elenaf9 Apr 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just saw that my above suggestion doesn't make any sense. I meant:

Suggested change
.filter(move |peer| peer.id() != &local_peer_id)
.filter(move |peer| now.checked_add(peer.ttl()).is_some())
.filter(move |peer| peer.id() != &local_peer_id && now.checked_add(peer.ttl()).is_some())

Still, I am not a big fan of calculation the now + peer.ttl() sum twice. Why not use ffilter_map and return the now.checked_add(peer.ttl()) Option so it can be used directly in the next step?

.flat_map(move |peer| {
let observed = self.observed_address();
let new_expiration = now + peer.ttl();
Expand Down Expand Up @@ -313,6 +314,8 @@ impl fmt::Debug for MdnsPeer {

#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr};

use super::{super::dns::build_query_response, *};

#[test]
Expand Down Expand Up @@ -353,4 +356,85 @@ mod tests {
assert_eq!(peer.peer_id, peer_id);
}
}

#[test]
fn test_extract_discovered_ttl_overflow() {
// Create a mock MdnsResponse with peers that have various TTLs
let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 5353);
let mut response = MdnsResponse {
peers: Vec::new(),
from: socket_addr,
};

// Create local peer ID (to be filtered out)
let local_peer_id = PeerId::random();

// Create remote peer IDs
let normal_peer_id = PeerId::random();
let huge_ttl_peer_id = PeerId::random();

// Setup addresses
let addr1 = "/ip4/192.168.1.2/tcp/1234".parse::<Multiaddr>().unwrap();
let addr2 = "/ip4/192.168.1.3/tcp/5678".parse::<Multiaddr>().unwrap();

// Add a peer with normal TTL
response.peers.push(MdnsPeer {
addrs: vec![addr1],
peer_id: normal_peer_id,
ttl: 120, // 2 minutes
});

// Add a peer with extremely large TTL
response.peers.push(MdnsPeer {
addrs: vec![addr2],
peer_id: huge_ttl_peer_id,
ttl: u32::MAX, // Maximum possible TTL
});

// Add the local peer (should be filtered out)
response.peers.push(MdnsPeer {
addrs: vec!["/ip4/127.0.0.1/tcp/9000".parse::<Multiaddr>().unwrap()],
peer_id: local_peer_id,
ttl: 120,
});

// Current time
let now = Instant::now();

// Call the method under test
let result: Vec<(PeerId, Multiaddr, Instant)> =
response.extract_discovered(now, local_peer_id).collect();

// Verify results

// Local peer should be filtered out
assert!(!result.iter().any(|(id, _, _)| id == &local_peer_id));

// The normal TTL peer should be included
assert!(result.iter().any(|(id, _, _)| id == &normal_peer_id));

// Verify the behavior for the huge TTL peer
// First, check if huge TTL causes overflow with checked_add
let huge_ttl_duration = Duration::from_secs(u64::from(u32::MAX));
let overflow_check = now.checked_add(huge_ttl_duration);

if overflow_check.is_some() {
// If checked_add doesn't detect overflow, huge TTL peer should be included
assert!(result.iter().any(|(id, _, _)| id == &huge_ttl_peer_id));
} else {
// If checked_add detects overflow, huge TTL peer should be excluded
assert!(!result.iter().any(|(id, _, _)| id == &huge_ttl_peer_id));
}

// For normal peer, verify expiration time calculation
let normal_peer_result = result.iter().find(|(id, _, _)| id == &normal_peer_id);
assert!(normal_peer_result.is_some());

let (_, _, expiration) = normal_peer_result.unwrap();
let expected_expiration = now + Duration::from_secs(120);

// Allow small tolerance for timing differences
assert!(*expiration >= expected_expiration - Duration::from_millis(10));
assert!(*expiration <= expected_expiration + Duration::from_millis(10));
}
}
Loading