Skip to content

Commit

Permalink
Ensure the stack pointer is always used during RandomState init so di…
Browse files Browse the repository at this point in the history
…fferent threads have different hashers. (#14)
  • Loading branch information
orlp authored Dec 19, 2024
1 parent 4f19fe6 commit 2de875f
Showing 1 changed file with 17 additions and 12 deletions.
29 changes: 17 additions & 12 deletions src/seed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,30 @@ pub mod fast {

impl Default for RandomState {
fn default() -> Self {
let per_hasher_seed;
// We initialize the per-hasher seed with the stack pointer to ensure
// different threads have different seeds, with as side benefit that
// stack address randomization gives us further non-determinism.
let mut per_hasher_seed = 0;
let stack_ptr = core::ptr::addr_of!(per_hasher_seed) as u64;
per_hasher_seed = stack_ptr;

// If we have the standard library available we use a thread-local
// counter for the per-hasher seed.
// state to ensure RandomStates are different with high probability,
// even if the call stack is the same.
#[cfg(feature = "std")]
{
use std::cell::Cell;
thread_local! {
static PER_HASHER_NONDETERMINISM: Cell<u64> = const { Cell::new(0) };
}

let mut nondeterminism = PER_HASHER_NONDETERMINISM.get();
nondeterminism = nondeterminism.wrapping_add(ARBITRARY1 | 1); // Ensure number is odd for maximum period.
PER_HASHER_NONDETERMINISM.set(nondeterminism);
per_hasher_seed = folded_multiply(nondeterminism, ARBITRARY2);
let nondeterminism = PER_HASHER_NONDETERMINISM.get();
per_hasher_seed = folded_multiply(per_hasher_seed, ARBITRARY1 ^ nondeterminism);
PER_HASHER_NONDETERMINISM.set(per_hasher_seed);
};

// If we don't have the standard library we use our current stack
// address in combination with a global PER_HASHER_NONDETERMINISM to
// create a new value that is very likely to have never been used as
// a random state before.
// If we don't have the standard library we instead use a global
// atomic instead of a thread-local state.
//
// PER_HASHER_NONDETERMINISM is loaded and updated in a racy manner,
// but this doesn't matter in practice - it is impossible that two
Expand All @@ -62,11 +65,13 @@ pub mod fast {
static PER_HASHER_NONDETERMINISM: AtomicUsize = AtomicUsize::new(0);

let nondeterminism = PER_HASHER_NONDETERMINISM.load(Ordering::Relaxed) as u64;
let stack_ptr = &nondeterminism as *const _ as u64;
per_hasher_seed = folded_multiply(nondeterminism ^ stack_ptr, ARBITRARY2);
per_hasher_seed = folded_multiply(per_hasher_seed, ARBITRARY1 ^ nondeterminism);
PER_HASHER_NONDETERMINISM.store(per_hasher_seed as usize, Ordering::Relaxed);
}

// One extra mixing step to ensure good random bits.
per_hasher_seed = folded_multiply(per_hasher_seed, ARBITRARY1);

Self {
per_hasher_seed,
global_seed: global::GlobalSeed::new(),
Expand Down

0 comments on commit 2de875f

Please sign in to comment.