From 6f46e86726727a7f134c55b09d029b27471c96a5 Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 23 Apr 2023 16:48:58 +0200 Subject: [PATCH] std: permute `ThreadId` values --- library/std/src/thread/mod.rs | 76 +++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 13b845b25c92d..18d1721b565b9 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1080,46 +1080,70 @@ pub struct ThreadId(NonZeroU64); impl ThreadId { // Generate a new unique thread ID. fn new() -> ThreadId { + /// A permutation function that always maps 0 to 0. + /// + /// Ported from . + /// This permutation is subject to change. **DO NOT** rely on it. + /// Always treat thread ids as opaque. + fn permute(x: u64) -> u64 { + let mut a = x; + let mut b = 0; + let mut c = 0; + for _ in 0..4 { + b ^= a.wrapping_add(c).rotate_left(7); + c ^= b.wrapping_add(a).rotate_left(9); + a ^= c.wrapping_add(b).rotate_left(13); + } + a + } + #[cold] fn exhausted() -> ! { panic!("failed to generate unique thread ID: bitspace exhausted") } - cfg_if::cfg_if! { - if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; + fn generate() -> u64 { + cfg_if::cfg_if! { + if #[cfg(target_has_atomic = "64")] { + use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; - static COUNTER: AtomicU64 = AtomicU64::new(0); + static COUNTER: AtomicU64 = AtomicU64::new(0); - let mut last = COUNTER.load(Relaxed); - loop { - let Some(id) = last.checked_add(1) else { - exhausted(); - }; + let mut last = COUNTER.load(Relaxed); + loop { + let Some(id) = last.checked_add(1) else { + exhausted(); + }; - match COUNTER.compare_exchange_weak(last, id, Relaxed, Relaxed) { - Ok(_) => return ThreadId(NonZeroU64::new(id).unwrap()), - Err(id) => last = id, + match COUNTER.compare_exchange_weak(last, id, Relaxed, Relaxed) { + Ok(_) => return id, + Err(id) => last = id, + } } - } - } else { - use crate::sync::{Mutex, PoisonError}; + } else { + use crate::sync::{Mutex, PoisonError}; - static COUNTER: Mutex = Mutex::new(0); + static COUNTER: Mutex = Mutex::new(0); - let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); - let Some(id) = counter.checked_add(1) else { - // in case the panic handler ends up calling `ThreadId::new()`, - // avoid reentrant lock acquire. - drop(counter); - exhausted(); - }; + let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); + let Some(id) = counter.checked_add(1) else { + // in case the panic handler ends up calling `ThreadId::new()`, + // avoid reentrant lock acquire. + drop(counter); + exhausted(); + }; - *counter = id; - drop(counter); - ThreadId(NonZeroU64::new(id).unwrap()) + *counter = id; + drop(counter); + id + } } } + + // Permute ids to stop users from relying on the exact value of + // `ThreadId`. Since `generate` never returns zero the id will not + // be zero either, as `permute` maps zero to itself. + ThreadId(NonZeroU64::new(permute(generate())).unwrap()) } /// This returns a numeric identifier for the thread identified by this