Skip to content
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

fix(swarm): prevent overflow in keep-alive computation #4559

Merged
merged 11 commits into from
Sep 27, 2023
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 @@ -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" }
Expand Down
5 changes: 5 additions & 0 deletions swarm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.43.5 - unreleased
thomaseizinger marked this conversation as resolved.
Show resolved Hide resolved

- Fix overflow in `KeepAlive` computation that could occur if `SwarmBuilder::idle_connection_timeout` is configured with `u64::MAX`.
See [PR XXXX](XXXX.
thomaseizinger marked this conversation as resolved.
Show resolved Hide resolved

## 0.43.4

- Implement `Debug` for event structs.
Expand Down
2 changes: 1 addition & 1 deletion swarm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <admin@parity.io>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand Down
30 changes: 26 additions & 4 deletions swarm/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,11 @@ where
{
let effective_keep_alive = max(requested_keep_alive, *idle_timeout);

let (delay, _) = sleep_until_or_at_least_very_long(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 <https://github.com/libp2p/rust-libp2p/issues/3844>/
*shutdown =
Shutdown::Later(Delay::new(effective_keep_alive), earliest_shutdown)
*shutdown = Shutdown::Later(delay, earliest_shutdown)
}
}
(_, KeepAlive::No) if idle_timeout == &Duration::ZERO => {
Expand All @@ -379,8 +380,9 @@ 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 (delay, deadline) = sleep_until_or_at_least_very_long(*idle_timeout);

*shutdown = Shutdown::Later(delay, deadline);
}
(_, KeepAlive::Yes) => *shutdown = Shutdown::None,
};
Expand Down Expand Up @@ -479,6 +481,19 @@ fn gather_supported_protocols(handler: &impl ConnectionHandler) -> HashSet<Strea
.collect()
}

/// Constructs a [`Delay`] for the given [`Duration`] and returns the [`Instant`] at which it will fire.
///
/// If [`Duration`] + [`Instant::now`] overflows, we will return a [`Delay`] that at least sleeps _very_ long.
fn sleep_until_or_at_least_very_long(mut duration: Duration) -> (Delay, Instant) {
while Instant::now().checked_add(duration).is_none() {
log::debug!("Cannot represent time {duration:?} in the future, halving it ...");

duration /= 2;
}

(Delay::new(duration), Instant::now() + duration)
}
thomaseizinger marked this conversation as resolved.
Show resolved Hide resolved

/// Borrowed information about an incoming connection currently being negotiated.
#[derive(Debug, Copy, Clone)]
pub(crate) struct IncomingInfo<'a> {
Expand Down Expand Up @@ -957,6 +972,13 @@ mod tests {
));
}

#[test]
fn sleep_until_or_eternity_doesnt_overflow() {
env_logger::init();

sleep_until_or_at_least_very_long(Duration::from_secs(u64::MAX));
}

struct KeepAliveUntilConnectionHandler {
until: Instant,
}
Expand Down
Loading