From c46a7dbb6126c368766a0e8a02fc04853c241643 Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:51:36 +0100 Subject: [PATCH] Tracking allocator: mark `Spinlock::unlock()` as unsafe and provide a safety contract (#2156) --- polkadot/node/tracking-allocator/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/polkadot/node/tracking-allocator/src/lib.rs b/polkadot/node/tracking-allocator/src/lib.rs index 8441bd1ffafa..ab8597b5c382 100644 --- a/polkadot/node/tracking-allocator/src/lib.rs +++ b/polkadot/node/tracking-allocator/src/lib.rs @@ -72,8 +72,11 @@ impl Spinlock { } } + // SAFETY: It should be only called from the guard's destructor. Calling it explicitly while + // the guard is alive is undefined behavior, as it breaks the security contract of `Deref` and + // `DerefMut`, which implies that lock is held at the moment of dereferencing. #[inline] - fn unlock(&self) { + unsafe fn unlock(&self) { self.lock.store(false, Ordering::Release); } } @@ -97,7 +100,9 @@ impl DerefMut for SpinlockGuard<'_, T> { impl Drop for SpinlockGuard<'_, T> { fn drop(&mut self) { - self.lock.unlock(); + // SAFETY: Calling `unlock` is only safe when it's guaranteed no guard outlives the + // unlocking point; here, the guard is dropped, so it is safe. + unsafe { self.lock.unlock() } } }