1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Mutex;

use log::trace;
use malloc_size_of_derive::MallocSizeOf;
/// A random number generator which shares one instance of an `OsRng`.
///
/// A problem with `OsRng`, which is inherited by `StdRng` and so
/// `ThreadRng`, is that it reads from `/dev/random`, and so consumes
/// a file descriptor. For multi-threaded applications like Servo,
/// it is easy to exhaust the supply of file descriptors this way.
///
/// This crate fixes that, by only using one `OsRng`, which is just
/// used to seed and re-seed an `ServoRng`.
use rand::distributions::{Distribution, Standard};
use rand::rngs::adapter::ReseedingRng;
use rand::rngs::OsRng;
pub use rand::seq::SliceRandom;
pub use rand::{Rng, RngCore, SeedableRng};
use rand_isaac::isaac::IsaacCore;
use uuid::{Builder, Uuid};

// The shared RNG which may hold on to a file descriptor
static OS_RNG: Mutex<OsRng> = Mutex::new(OsRng);

// Generate 32K of data between reseedings
const RESEED_THRESHOLD: u64 = 32_768;

// An in-memory RNG that only uses the shared file descriptor for seeding and reseeding.
#[derive(MallocSizeOf)]
pub struct ServoRng {
    #[ignore_malloc_size_of = "Defined in rand"]
    rng: ReseedingRng<IsaacCore, ServoReseeder>,
}

impl RngCore for ServoRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        self.rng.next_u32()
    }

    #[inline]
    fn next_u64(&mut self) -> u64 {
        self.rng.next_u64()
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        self.rng.fill_bytes(bytes)
    }

    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
        self.rng.try_fill_bytes(bytes)
    }
}

#[derive(Default)]
pub struct Seed([u8; 32]);

impl AsMut<[u8]> for Seed {
    fn as_mut(&mut self) -> &mut [u8] {
        &mut self.0
    }
}

impl SeedableRng for ServoRng {
    type Seed = Seed;

    // This function is used in the reseeding process of rand hence why the RESEED_THRESHOLD is
    // used.
    fn from_seed(seed: Seed) -> ServoRng {
        trace!("Creating a new ServoRng.");
        let isaac_rng = IsaacCore::from_seed(seed.0);
        let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
        ServoRng { rng: reseeding_rng }
    }
}

impl ServoRng {
    /// Create a manually-reseeding instance of `ServoRng`.
    ///
    /// Note that this RNG does not reseed itself, so care is needed to reseed the RNG
    /// is required to be cryptographically sound.
    pub fn new_manually_reseeded(seed: u64) -> ServoRng {
        trace!("Creating a new manually-reseeded ServoRng.");
        let isaac_rng = IsaacCore::seed_from_u64(seed);
        let reseeding_rng = ReseedingRng::new(isaac_rng, 0, ServoReseeder);
        ServoRng { rng: reseeding_rng }
    }
}

impl Default for ServoRng {
    /// Create an auto-reseeding instance of `ServoRng`.
    ///
    /// This uses the shared `OsRng`, so avoids consuming
    /// a file descriptor.
    fn default() -> Self {
        trace!("Creating new ServoRng.");
        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
        let isaac_rng = IsaacCore::from_rng(&mut *os_rng).unwrap();
        let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
        ServoRng { rng: reseeding_rng }
    }
}

// The reseeder for the in-memory RNG.
struct ServoReseeder;

impl RngCore for ServoReseeder {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
        os_rng.next_u32()
    }

    #[inline]
    fn next_u64(&mut self) -> u64 {
        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
        os_rng.next_u64()
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
        os_rng.fill_bytes(bytes)
    }

    #[inline]
    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
        os_rng.try_fill_bytes(bytes)
    }
}

impl Default for ServoReseeder {
    fn default() -> ServoReseeder {
        ServoReseeder
    }
}

// A thread-local RNG, designed as a drop-in replacement for rand::ThreadRng.
#[derive(Clone)]
pub struct ServoThreadRng {
    rng: Rc<RefCell<ServoRng>>,
}

// A thread-local RNG, designed as a drop-in replacement for rand::thread_rng.
pub fn thread_rng() -> ServoThreadRng {
    SERVO_THREAD_RNG.with(|t| t.clone())
}

thread_local! {
    static SERVO_THREAD_RNG: ServoThreadRng = ServoThreadRng { rng: Rc::new(RefCell::new(ServoRng::default())) };
}

impl RngCore for ServoThreadRng {
    fn next_u32(&mut self) -> u32 {
        self.rng.borrow_mut().next_u32()
    }

    fn next_u64(&mut self) -> u64 {
        self.rng.borrow_mut().next_u64()
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        self.rng.borrow_mut().fill_bytes(bytes)
    }

    #[inline]
    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
        (self.rng.borrow_mut()).try_fill_bytes(bytes)
    }
}

// Generates a random value using the thread-local random number generator.
// A drop-in replacement for rand::random.
#[inline]
pub fn random<T>() -> T
where
    Standard: Distribution<T>,
{
    thread_rng().gen()
}

// TODO(eijebong): Replace calls to this by random once `uuid::Uuid` implements `rand::Rand` again.
#[inline]
pub fn random_uuid() -> Uuid {
    let mut bytes = [0; 16];
    thread_rng().fill_bytes(&mut bytes);
    Builder::from_random_bytes(bytes).into_uuid()
}