diff --git a/Cargo.lock b/Cargo.lock index a7f74567ea2..a284eb8cce3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3039,7 +3039,7 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.43.4" +version = "0.43.5" dependencies = [ "async-std", "either", diff --git a/Cargo.toml b/Cargo.toml index be26badcc30..4932efb58ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ libp2p-rendezvous = { version = "0.13.0", path = "protocols/rendezvous" } libp2p-upnp = { version = "0.1.1", path = "protocols/upnp" } libp2p-request-response = { version = "0.25.1", path = "protocols/request-response" } libp2p-server = { version = "0.12.3", path = "misc/server" } -libp2p-swarm = { version = "0.43.4", path = "swarm" } +libp2p-swarm = { version = "0.43.5", path = "swarm" } libp2p-swarm-derive = { version = "0.33.0", path = "swarm-derive" } libp2p-swarm-test = { version = "0.2.0", path = "swarm-test" } libp2p-tcp = { version = "0.40.0", path = "transports/tcp" } diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 2d5fcfe542d..230134862df 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.43.5 + +- Fix overflow in `KeepAlive` computation that could occur if `SwarmBuilder::idle_connection_timeout` is configured with `u64::MAX`. + See [PR 4559](https://github.com/libp2p/rust-libp2p/pull/4559). + ## 0.43.4 - Implement `Debug` for event structs. diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index c2de4cb624a..a991bacd53e 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-swarm" edition = "2021" rust-version = { workspace = true } description = "The libp2p swarm" -version = "0.43.4" +version = "0.43.5" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 310cf3a81e3..d99319cf2cb 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -361,15 +361,16 @@ where } } (_, KeepAlive::Until(earliest_shutdown)) => { - if let Some(requested_keep_alive) = - earliest_shutdown.checked_duration_since(Instant::now()) - { - let effective_keep_alive = max(requested_keep_alive, *idle_timeout); + let now = Instant::now(); + + if let Some(requested) = earliest_shutdown.checked_duration_since(now) { + let effective_keep_alive = max(requested, *idle_timeout); + + let safe_keep_alive = checked_add_fraction(now, effective_keep_alive); // Important: We store the _original_ `Instant` given by the `ConnectionHandler` in the `Later` instance to ensure we can compare it in the above branch. // This is quite subtle but will hopefully become simpler soon once `KeepAlive::Until` is fully deprecated. See / - *shutdown = - Shutdown::Later(Delay::new(effective_keep_alive), earliest_shutdown) + *shutdown = Shutdown::Later(Delay::new(safe_keep_alive), earliest_shutdown) } } (_, KeepAlive::No) if idle_timeout == &Duration::ZERO => { @@ -379,8 +380,10 @@ where // Do nothing, i.e. let the shutdown timer continue to tick. } (_, KeepAlive::No) => { - let deadline = Instant::now() + *idle_timeout; - *shutdown = Shutdown::Later(Delay::new(*idle_timeout), deadline); + let now = Instant::now(); + let safe_keep_alive = checked_add_fraction(now, *idle_timeout); + + *shutdown = Shutdown::Later(Delay::new(safe_keep_alive), now + safe_keep_alive); } (_, KeepAlive::Yes) => *shutdown = Shutdown::None, }; @@ -479,6 +482,20 @@ fn gather_supported_protocols(handler: &impl ConnectionHandler) -> HashSet Duration { + while start.checked_add(duration).is_none() { + log::debug!("{start:?} + {duration:?} cannot be presented, halving duration"); + + duration /= 2; + } + + duration +} + /// Borrowed information about an incoming connection currently being negotiated. #[derive(Debug, Copy, Clone)] pub(crate) struct IncomingInfo<'a> { @@ -957,6 +974,16 @@ mod tests { )); } + #[test] + fn checked_add_fraction_can_add_u64_max() { + let _ = env_logger::try_init(); + let start = Instant::now(); + + let duration = checked_add_fraction(start, Duration::from_secs(u64::MAX)); + + assert!(start.checked_add(duration).is_some()) + } + struct KeepAliveUntilConnectionHandler { until: Instant, }