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

Follows the discussion here:
https://github.com/llvm/llvm-project/pull/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 481a55a3d9645a6bc1540d326319b78ad8ed8db1)
This commit is contained in:
davidtrevelyan 2025-03-13 10:18:25 +00:00 committed by Tom Stellard
parent 4370072022
commit f811c7df0a
2 changed files with 30 additions and 0 deletions

View File

@ -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
@ -642,6 +648,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

View File

@ -1058,6 +1058,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