Skip to content

[rtsan][Apple] Add interceptor for _os_nospin_lock_lock #131034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

davidtrevelyan
Copy link
Contributor

@davidtrevelyan davidtrevelyan commented Mar 12, 2025

Follows the discussion here: #129309

Recently, the test TestRtsan.AccessingALargeAtomicVariableDiesWhenRealtime has been failing on newer MacOS versions, because the internal locking mechanism in std::atomic<T>::load (for types T that are larger than the hardware lock-free limit), has changed to a function that wasn't being intercepted by rtsan.

This PR introduces an interceptor for _os_nospin_lock_lock, which is the new internal locking mechanism.

Note: we'd probably do well to introduce interceptors for _os_nospin_lock_unlock (and os_unfair_lock_unlock) too, which also appear to have blocking implementations. This can follow in a separate PR.

@llvmbot
Copy link
Member

llvmbot commented Mar 12, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (davidtrevelyan)

Changes

Follows the discussion here: #129309

Recently, the test TestRtsan.AccessingALargeAtomicVariableDiesWhenRealtime has been failing on newer MacOS versions, because the internal locking mechanism in std::atomic&lt;T&gt;::load (for types T that are larger than the hardware lock-free limit), has changed to a function that wasn't being intercepted by rtsan.

This PR introduces an interceptor for _os_nospin_lock_lock, which is the new internal locking mechanism.


Full diff: https://github.com/llvm/llvm-project/pull/131034.diff

2 Files Affected:

  • (modified) compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp (+11)
  • (modified) compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp (+19)
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
index cd57107450a47..5bbefa3ce43f1 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
@@ -30,6 +30,12 @@
 extern "C" {
 typedef int32_t OSSpinLock;
 void OSSpinLockLock(volatile OSSpinLock *__lock);
+// A pointer to this type is in the interface for `_os_nospin_lock_lock`, but
+// it's an internal implementation detail of `os/lock.c` on Darwin, and
+// therefore not available in any headers. As a workaround, we forward declare
+// it here, which is enough to facilitate interception of _os_nospin_lock_lock.
+struct _os_nospin_lock_s;
+using _os_nospin_lock_t = _os_nospin_lock_s *;
 }
 #endif // TARGET_OS_MAC
 
@@ -711,6 +717,11 @@ INTERCEPTOR(void, os_unfair_lock_lock, os_unfair_lock_t lock) {
   __rtsan_notify_intercepted_call("os_unfair_lock_lock");
   return REAL(os_unfair_lock_lock)(lock);
 }
+
+INTERCEPTOR(void, _os_nospin_lock_lock, _os_nospin_lock_t lock) {
+  __rtsan_notify_intercepted_call("_os_nospin_lock_lock");
+  return REAL(_os_nospin_lock_lock)(lock);
+}
 #define RTSAN_MAYBE_INTERCEPT_OS_UNFAIR_LOCK_LOCK                              \
   INTERCEPT_FUNCTION(os_unfair_lock_lock)
 #else
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
index 47bcafff51a4d..9ef6887a9c848 100644
--- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
@@ -1122,6 +1122,25 @@ TEST(TestRtsanInterceptors, OsUnfairLockLockDiesWhenRealtime) {
   ExpectRealtimeDeath(Func, "os_unfair_lock_lock");
   ExpectNonRealtimeSurvival(Func);
 }
+
+// We intercept _os_nospin_lock_lock because it's the internal
+// locking mechanism for MacOS's atomic implementation for data
+// types that are larger than the hardware's maximum lock-free size.
+// However, it's a private implementation detail and not visible in any headers,
+// so we must duplicate the required type definitions to forward declaration
+// what we need here.
+extern "C" {
+struct _os_nospin_lock_s {
+  unsigned int oul_value;
+};
+void _os_nospin_lock_lock(_os_nospin_lock_s *);
+}
+TEST(TestRtsanInterceptors, OsNoSpinLockLockDiesWhenRealtime) {
+  _os_nospin_lock_s lock{};
+  auto Func = [&]() { _os_nospin_lock_lock(&lock); };
+  ExpectRealtimeDeath(Func, "_os_nospin_lock_lock");
+  ExpectNonRealtimeSurvival(Func);
+}
 #endif
 
 #if SANITIZER_LINUX

Copy link
Contributor

@cjappl cjappl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks for your legwork on this one

@cjappl
Copy link
Contributor

cjappl commented Mar 12, 2025

Also if this is merged and tests are happy, we may consider cherry picking to the llvm 20 release branch as this is "expected behavior" for that version.

It would be good to get in the next release if we feel good about it.

@thetruestblue
Copy link
Contributor

thetruestblue commented Mar 12, 2025

Thanks to both of you. Note for your future development that these locks come in bundles, as do unfair locks:
os_nospin_lock* : lock, unlock, & try_lock

@davidtrevelyan davidtrevelyan merged commit 481a55a into llvm:main Mar 13, 2025
13 checks passed
@davidtrevelyan davidtrevelyan deleted the fix/large-atomic-lock-interception branch March 13, 2025 10:18
@thetruestblue
Copy link
Contributor

thetruestblue commented Mar 14, 2025

In a follow-up you can you please wrap this with:

#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"

swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Mar 14, 2025
…#131034)"

This reverts commit 481a55a.

This change breaks compilation using Apple's internal SDK. This is a
temporary revert while we figure out the correct path forward. Reverting
upstream doesn't seem like the right thing to do given they don't have
access to the internal SDK.

rdar://147018451
@davidtrevelyan
Copy link
Contributor Author

@thetruestblue sure - no problem. Which section(s) of code in this patch are you referring to wrapping here? If you'd find it easier to submit a PR to describe this, I'd be very happy to take a look.

@delcypher are you by any chance able to share details about how this broke your build?

@delcypher
Copy link
Contributor

@davidtrevelyan Unfortunately it fails in a way that you won't be able to reproduce because the build fails when using Apple's internal SDKs which are different from the public ones. The error looks like this. I've had to redact a few things.

/Volumes/user_data/xc/tmp/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.Redacted.sdk/usr/include/libkern/OSSpinLockDeprecated.h:185:30: error: type alias redefinition with different types ('volatile OSSpinLock *' (aka 'volatile int *') vs '_os_nospin_lock_s *')
<redacted>
                             ^
/Volumes/user_data/dev/llvm/llvm.org/main/src/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp:38:7: note: previous definition is here
using _os_nospin_lock_t = _os_nospin_lock_s *;
      ^
/Volumes/user_data/dev/llvm/llvm.org/main/src/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp:705:19: error: conflicting types for '_os_nospin_lock_lock'
INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
                  ^
/Volumes/user_data/xc/tmp/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.Redacted.sdk/usr/include/libkern/OSSpinLockDeprecated.h:190:6: note: previous declaration is here
<redacted>
     ^
/Volumes/user_data/dev/llvm/llvm.org/main/src/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp:707:15: error: no matching function for call to '_os_nospin_lock_lock'
  return REAL(OSSpinLockLock)(lock);
         ~~~~~^~~~~~~~~~~~~~~~~~~~~
/Volumes/user_data/dev/llvm/llvm.org/main/src/llvm-project/compiler-rt/lib/rtsan/../interception/interception.h:266:18: note: expanded from macro 'REAL'
# define REAL(x) x
                 ^
/Volumes/user_data/xc/tmp/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.Redacted.sdk/usr/include/libkern/OSSpinLockDeprecated.h:192:30: note: expanded from macro 'OSSpinLockLock'
<redacted>
                             ^~~~~~~~~~~~~~~~~~~~
/Volumes/user_data/xc/tmp/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.Redacted.sdk/usr/include/libkern/OSSpinLockDeprecated.h:190:6: note: candidate function not viable: no known conversion from 'volatile OSSpinLock *' (aka 'volatile int *') to '_os_nospin_lock_t' (aka '_os_nospin_lock_s *') for 1st argument
<redacted>
     ^

@delcypher
Copy link
Contributor

@davidtrevelyan @thetruestblue Doing this fixed the build for me using the internal SDK.

diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
index 5bbefa3ce43f..71e47f97f8c0 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
@@ -35,7 +35,7 @@ void OSSpinLockLock(volatile OSSpinLock *__lock);
 // therefore not available in any headers. As a workaround, we forward declare
 // it here, which is enough to facilitate interception of _os_nospin_lock_lock.
 struct _os_nospin_lock_s;
-using _os_nospin_lock_t = _os_nospin_lock_s *;
+using _os_nospin_lock_t = volatile OSSpinLock *;
 }
 #endif // TARGET_OS_MAC

frederik-h pushed a commit to frederik-h/llvm-project that referenced this pull request Mar 18, 2025
Follows the discussion here:
llvm#129309

Recently, the test
`TestRtsan.AccessingALargeAtomicVariableDiesWhenRealtime` has been
failing on newer MacOS versions, because the internal locking mechanism
in `std::atomic<T>::load` (for types `T` that are larger than the
hardware lock-free limit), has changed to a function that wasn't being
intercepted by rtsan.

This PR introduces an interceptor for `_os_nospin_lock_lock`, which is
the new internal locking mechanism.

_Note: we'd probably do well to introduce interceptors for
`_os_nospin_lock_unlock` (and `os_unfair_lock_unlock`) too, which also
appear to have blocking implementations. This can follow in a separate
PR._
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Mar 18, 2025
@wrotki wrotki added this to the LLVM 20.X Release milestone Mar 25, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Mar 25, 2025
@wrotki
Copy link
Contributor

wrotki commented Mar 25, 2025

/cherry-pick 6f99abc

@wrotki
Copy link
Contributor

wrotki commented Mar 25, 2025

Cherry-picked to LLVM 20, as the tests are still failing on LLVM 20 release bot

@llvmbot
Copy link
Member

llvmbot commented Mar 25, 2025

Failed to cherry-pick: 6f99abc

https://github.com/llvm/llvm-project/actions/runs/14069840627

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

@wrotki
Copy link
Contributor

wrotki commented Mar 25, 2025

/cherry-pick 481a55a

@llvmbot
Copy link
Member

llvmbot commented Mar 25, 2025

/pull-request #132997

@llvmbot llvmbot moved this from Needs Triage to Done in LLVM Release Status Mar 25, 2025
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Mar 26, 2025
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Mar 27, 2025
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Apr 4, 2025
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Apr 7, 2025
thetruestblue pushed a commit to thetruestblue/llvm-project that referenced this pull request Apr 25, 2025
Follows the discussion here:
llvm#129309

Recently, the test
`TestRtsan.AccessingALargeAtomicVariableDiesWhenRealtime` has been
failing on newer MacOS versions, because the internal locking mechanism
in `std::atomic<T>::load` (for types `T` that are larger than the
hardware lock-free limit), has changed to a function that wasn't being
intercepted by rtsan.

This PR introduces an interceptor for `_os_nospin_lock_lock`, which is
the new internal locking mechanism.

_Note: we'd probably do well to introduce interceptors for
`_os_nospin_lock_unlock` (and `os_unfair_lock_unlock`) too, which also
appear to have blocking implementations. This can follow in a separate
PR._

(cherry picked from commit 481a55a)
@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

Failed to cherry-pick: 6f99abc

https://github.com/llvm/llvm-project/actions/runs/14737412738

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request May 10, 2025
Follows the discussion here:
llvm#129309

Recently, the test
`TestRtsan.AccessingALargeAtomicVariableDiesWhenRealtime` has been
failing on newer MacOS versions, because the internal locking mechanism
in `std::atomic<T>::load` (for types `T` that are larger than the
hardware lock-free limit), has changed to a function that wasn't being
intercepted by rtsan.

This PR introduces an interceptor for `_os_nospin_lock_lock`, which is
the new internal locking mechanism.

_Note: we'd probably do well to introduce interceptors for
`_os_nospin_lock_unlock` (and `os_unfair_lock_unlock`) too, which also
appear to have blocking implementations. This can follow in a separate
PR._

(cherry picked from commit 481a55a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

6 participants