Skip to content

Commit d7faa87

Browse files
authored
Audit uses of SmallRng (#757)
While testing another program, I was reminded that `SmallRng` can have surprising behavior when cloned: since a SmallRng's internal state is stored in a field, each clone ends up in the same state such that each clone produces the same sequence of values. Furthermore, the use of `SmallRng::from_entropy` uses `getrandom` internally, which can be quite slow, especially compared to the alternative `SmallRng::from_rng(&mut thread_rng())`. It doesn't appear that any of our uses of `SmallRng` are particularly problematic -- i.e. the services that hold a `SmallRng` are not cloned, so we shouldn't be in a place where we always use a single value. However, in the spirit of being defensive to this scenario, this change avoid storing a `SmallRng` in Layer and NewService implemetnations so that each produced service obtains a unique `SmallRng`. Furthermore, all uses of `SmallRng::from_entropy()` are converted to use the faster thread-local PRNG equivalent.
1 parent 876ae02 commit d7faa87

File tree

10 files changed

+14
-24
lines changed

10 files changed

+14
-24
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,6 @@ dependencies = [
838838
"pin-project",
839839
"procinfo",
840840
"prost-types",
841-
"rand 0.7.2",
842841
"regex 1.3.9",
843842
"tokio",
844843
"tokio-timer",

linkerd/app/core/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ linkerd2-stack = { path = "../../stack" }
5959
linkerd2-stack-metrics = { path = "../../stack/metrics" }
6060
linkerd2-stack-tracing = { path = "../../stack/tracing" }
6161
linkerd2-trace-context = { path = "../../trace-context" }
62-
rand = { version = "0.7", features = ["small_rng"] }
6362
regex = "1.0.0"
6463
tokio = { version = "0.2.22", features = ["macros", "sync", "parking_lot"]}
6564
tokio-timer = "0.2"

linkerd/exp-backoff/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![deny(warnings, rust_2018_idioms)]
22

33
use pin_project::pin_project;
4-
use rand::{rngs::SmallRng, SeedableRng};
4+
use rand::{rngs::SmallRng, thread_rng, SeedableRng};
55
use std::fmt;
66
use std::future::Future;
77
use std::ops::Mul;
@@ -44,7 +44,7 @@ impl ExponentialBackoff {
4444
pub fn stream(&self) -> ExponentialBackoffStream {
4545
ExponentialBackoffStream {
4646
backoff: *self,
47-
rng: SmallRng::from_entropy(),
47+
rng: SmallRng::from_rng(&mut thread_rng()).expect("RNG must be valid"),
4848
iterations: 0,
4949
delay: None,
5050
}

linkerd/proxy/http/src/balance.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::Error;
22
use http;
33
use hyper::body::HttpBody;
44
pub use hyper_balance::{PendingUntilFirstData, PendingUntilFirstDataBody};
5-
use rand::{rngs::SmallRng, SeedableRng};
5+
use rand::thread_rng;
66
use std::{hash::Hash, marker::PhantomData, time::Duration};
77
use tower::discover::Discover;
88
pub use tower::{
@@ -16,7 +16,6 @@ pub use tower::{
1616
pub struct Layer<A, B> {
1717
decay: Duration,
1818
default_rtt: Duration,
19-
rng: SmallRng,
2019
_marker: PhantomData<fn(A) -> B>,
2120
}
2221

@@ -26,7 +25,6 @@ pub fn layer<A, B>(default_rtt: Duration, decay: Duration) -> Layer<A, B> {
2625
Layer {
2726
decay,
2827
default_rtt,
29-
rng: SmallRng::from_entropy(),
3028
_marker: PhantomData,
3129
}
3230
}
@@ -36,7 +34,6 @@ impl<A, B> Clone for Layer<A, B> {
3634
Self {
3735
decay: self.decay,
3836
default_rtt: self.default_rtt,
39-
rng: self.rng.clone(),
4037
_marker: PhantomData,
4138
}
4239
}
@@ -58,6 +55,6 @@ where
5855
fn layer(&self, discover: D) -> Self::Service {
5956
let instrument = PendingUntilFirstData::default();
6057
let loaded = PeakEwmaDiscover::new(discover, self.default_rtt, self.decay, instrument);
61-
Balance::from_rng(loaded, self.rng.clone()).expect("RNG must be valid")
58+
Balance::from_rng(loaded, &mut thread_rng()).expect("RNG must be valid")
6259
}
6360
}

linkerd/proxy/tap/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ linkerd2-proxy-api = { git = "https://github.com/linkerd/linkerd2-proxy-api", ta
1919
linkerd2-proxy-http = { path = "../http" }
2020
linkerd2-proxy-transport = { path = "../transport" }
2121
linkerd2-stack = { path = "../../stack" }
22-
rand = { version = "0.7", features = ["small_rng"] }
22+
rand = { version = "0.7" }
2323
tokio = { version = "0.2", features = ["time"]}
2424
tower = {version = "0.3", default-features = false }
2525
tonic = { version = "0.3", default-features = false }

linkerd/proxy/tcp/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ futures = { version = "0.3", features = ["compat"] }
1111
linkerd2-duplex = { path = "../../duplex" }
1212
linkerd2-error = { path = "../../error" }
1313
linkerd2-stack = { path = "../../stack" }
14-
rand = { version = "0.7", features = ["small_rng"] }
14+
rand = { version = "0.7" }
1515
tokio = { version = "0.2" }
1616
tower = { version = "0.3", default-features = false, features = ["balance", "load", "discover"] }
1717
pin-project = "0.4"

linkerd/proxy/tcp/src/balance.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use linkerd2_error::Error;
22
use linkerd2_stack::layer;
3-
use rand::{rngs::SmallRng, SeedableRng};
3+
use rand::thread_rng;
44
use std::{hash::Hash, time::Duration};
55
pub use tower::{
66
balance::p2c::Balance,
@@ -20,10 +20,9 @@ where
2020
D::Service: tower::Service<T>,
2121
<D::Service as tower::Service<T>>::Error: Into<Error>,
2222
{
23-
let rng = SmallRng::from_entropy();
2423
layer::mk(move |discover| {
2524
let loaded =
2625
PeakEwmaDiscover::new(discover, default_rtt, decay, CompleteOnResponse::default());
27-
Balance::from_rng(loaded, rng.clone()).expect("RNG must be valid")
26+
Balance::from_rng(loaded, &mut thread_rng()).expect("RNG must be valid")
2827
})
2928
}

linkerd/service-profiles/src/split.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use linkerd2_addr::Addr;
55
use linkerd2_error::Error;
66
use linkerd2_stack::{layer, NewService};
77
use rand::distributions::{Distribution, WeightedIndex};
8-
use rand::{rngs::SmallRng, SeedableRng};
8+
use rand::{rngs::SmallRng, thread_rng, SeedableRng};
99
use std::{
1010
marker::PhantomData,
1111
pin::Pin,
@@ -17,18 +17,15 @@ use tracing::{debug, trace};
1717
pub fn layer<N, S, Req>() -> impl layer::Layer<N, Service = NewSplit<N, S, Req>> + Clone {
1818
// This RNG doesn't need to be cryptographically secure. Small and fast is
1919
// preferable.
20-
let rng = SmallRng::from_entropy();
2120
layer::mk(move |inner| NewSplit {
2221
inner,
23-
rng: rng.clone(),
2422
_service: PhantomData,
2523
})
2624
}
2725

2826
#[derive(Debug)]
2927
pub struct NewSplit<N, S, Req> {
3028
inner: N,
31-
rng: SmallRng,
3229
_service: PhantomData<fn(Req) -> S>,
3330
}
3431

@@ -57,7 +54,6 @@ impl<N: Clone, S, Req> Clone for NewSplit<N, S, Req> {
5754
fn clone(&self) -> Self {
5855
Self {
5956
inner: self.inner.clone(),
60-
rng: self.rng.clone(),
6157
_service: self._service,
6258
}
6359
}
@@ -115,7 +111,7 @@ where
115111
services,
116112
addrs,
117113
distribution: WeightedIndex::new(weights).unwrap(),
118-
rng: self.rng.clone(),
114+
rng: SmallRng::from_rng(&mut thread_rng()).expect("RNG must initialize"),
119115
}
120116
}
121117
};

linkerd/trace-context/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ hex = "0.3.2"
1313
http = "0.2"
1414
linkerd2-error = { path = "../error" }
1515
linkerd2-stack = { path = "../stack" }
16-
rand = { version = "0.7", features = ["small_rng"] }
16+
rand = { version = "0.7" }
1717
tower = { version = "0.3", default-features = false, features = ["util"] }
1818
tracing = "0.1.2"
1919
tokio = {version = "0.2", features = ["sync"]}

linkerd/trace-context/src/propagation.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{Flags, Id, InsufficientBytes};
22
use bytes::Bytes;
33
use http::header::HeaderValue;
44
use linkerd2_error::Error;
5-
use rand::{rngs::SmallRng, SeedableRng};
5+
use rand::thread_rng;
66
use std::convert::TryInto;
77
use std::fmt;
88
use tracing::{trace, warn};
@@ -148,7 +148,7 @@ fn parse_grpc_trace_context_field(
148148
}
149149

150150
fn increment_grpc_span_id<B>(request: &mut http::Request<B>, context: &TraceContext) -> Id {
151-
let span_id = Id::new_span_id(&mut SmallRng::from_entropy());
151+
let span_id = Id::new_span_id(&mut thread_rng());
152152

153153
trace!(message = "incremented span id", %span_id);
154154

@@ -195,7 +195,7 @@ fn unpack_http_trace_context<B>(request: &http::Request<B>) -> Option<TraceConte
195195
}
196196

197197
fn increment_http_span_id<B>(request: &mut http::Request<B>) -> Id {
198-
let span_id = Id::new_span_id(&mut SmallRng::from_entropy());
198+
let span_id = Id::new_span_id(&mut thread_rng());
199199

200200
trace!("incremented span id: {}", span_id);
201201

0 commit comments

Comments
 (0)