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 8441bd1ffaf..ab8597b5c38 100644
--- a/polkadot/node/tracking-allocator/src/lib.rs
+++ b/polkadot/node/tracking-allocator/src/lib.rs
@@ -72,8 +72,11 @@ impl<T> Spinlock<T> {
 		}
 	}
 
+	// 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<T> DerefMut for SpinlockGuard<'_, T> {
 
 impl<T> 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() }
 	}
 }
 
-- 
GitLab