Skip to content
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

[rtsan][compiler-rt] Prevent UB hang in rtsan lock unit tests #104733

Merged
merged 2 commits into from
Aug 23, 2024

Conversation

cjappl
Copy link
Contributor

@cjappl cjappl commented Aug 19, 2024

It is undefined behavior to lock or unlock an uninitialized lock, and unlock a lock which isn't locked.

Later in one of my dev branches this was hanging on ubuntu.

This review introduces a fixture to set up and tear down the locks where appropriate, and separates them into two tests (realtime death and non realtime survival) so each test is guaranteed a fresh lock.

@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2024

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

Author: Chris Apple (cjappl)

Changes

It is undefined behavior to lock or unlock an uninitialized lock, and unlock a lock which isn't locked.

Later in one of my dev branches this was hanging on ubuntu.

This review introduces a fixture to set up and tear down the locks where appropriate, and separates them into two tests (realtime death and non realtime survival) so each test is guaranteed a fresh lock.


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

1 Files Affected:

  • (modified) compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp (+107-23)
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp
index f5b016089087df..966e5514062881 100644
--- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp
@@ -328,26 +328,64 @@ TEST(TestRtsanInterceptors, PthreadCreateDiesWhenRealtime) {
   ExpectNonRealtimeSurvival(Func);
 }
 
-TEST(TestRtsanInterceptors, PthreadMutexLockDiesWhenRealtime) {
-  auto Func = []() {
-    pthread_mutex_t mutex{};
+class PthreadMutexLockTest : public ::testing::Test {
+protected:
+  void SetUp() override {
+    pthread_mutex_init(&mutex, nullptr);
+    is_locked = false;
+  }
+
+  void TearDown() override {
+    if (is_locked) {
+      Unlock();
+    }
+    pthread_mutex_destroy(&mutex);
+  }
+
+  void Lock() {
+    ASSERT_TRUE(!is_locked);
     pthread_mutex_lock(&mutex);
-  };
+    is_locked = true;
+  }
+
+  void Unlock() {
+    ASSERT_TRUE(is_locked);
+    pthread_mutex_unlock(&mutex);
+    is_locked = false;
+  }
+
+private:
+  pthread_mutex_t mutex;
+  bool is_locked;
+};
+
+TEST_F(PthreadMutexLockTest, PthreadMutexLockDiesWhenRealtime) {
+  auto Func = [this]() { Lock(); };
 
   ExpectRealtimeDeath(Func, "pthread_mutex_lock");
+}
+
+TEST_F(PthreadMutexLockTest, PthreadMutexLockSurvivesWhenNotRealtime) {
+  auto Func = [this]() { Lock(); };
+
   ExpectNonRealtimeSurvival(Func);
 }
 
-TEST(TestRtsanInterceptors, PthreadMutexUnlockDiesWhenRealtime) {
-  auto Func = []() {
-    pthread_mutex_t mutex{};
-    pthread_mutex_unlock(&mutex);
-  };
+TEST_F(PthreadMutexLockTest, PthreadMutexUnlockDiesWhenRealtime) {
+  Lock();
+  auto Func = [this]() { Unlock(); };
 
   ExpectRealtimeDeath(Func, "pthread_mutex_unlock");
   ExpectNonRealtimeSurvival(Func);
 }
 
+TEST_F(PthreadMutexLockTest, PthreadMutexUnlockSurvivesWhenNotRealtime) {
+  Lock();
+  auto Func = [this]() { Unlock(); };
+
+  ExpectNonRealtimeSurvival(Func);
+}
+
 TEST(TestRtsanInterceptors, PthreadMutexJoinDiesWhenRealtime) {
   auto Func = []() {
     pthread_t thread{};
@@ -431,30 +469,76 @@ TEST(TestRtsanInterceptors, PthreadCondWaitDiesWhenRealtime) {
   pthread_mutex_destroy(&mutex);
 }
 
-TEST(TestRtsanInterceptors, PthreadRwlockRdlockDiesWhenRealtime) {
-  auto Func = []() {
-    pthread_rwlock_t rw_lock;
+class PthreadRwlockTest : public ::testing::Test {
+protected:
+  void SetUp() override {
+    pthread_rwlock_init(&rw_lock, nullptr);
+    is_locked = false;
+  }
+
+  void TearDown() override {
+    if (is_locked) {
+      Unlock();
+    }
+    pthread_rwlock_destroy(&rw_lock);
+  }
+
+  void RdLock() {
+    ASSERT_TRUE(!is_locked);
     pthread_rwlock_rdlock(&rw_lock);
-  };
+    is_locked = true;
+  }
+
+  void WrLock() {
+    ASSERT_TRUE(!is_locked);
+    pthread_rwlock_wrlock(&rw_lock);
+    is_locked = true;
+  }
+
+  void Unlock() {
+    ASSERT_TRUE(is_locked);
+    pthread_rwlock_unlock(&rw_lock);
+    is_locked = false;
+  }
+
+private:
+  pthread_rwlock_t rw_lock;
+  bool is_locked;
+};
+
+TEST_F(PthreadRwlockTest, PthreadRwlockRdlockDiesWhenRealtime) {
+  auto Func = [this]() { RdLock(); };
   ExpectRealtimeDeath(Func, "pthread_rwlock_rdlock");
+}
+
+TEST_F(PthreadRwlockTest, PthreadRwlockRdlockSurvivesWhenNonRealtime) {
+  auto Func = [this]() { RdLock(); };
   ExpectNonRealtimeSurvival(Func);
 }
 
-TEST(TestRtsanInterceptors, PthreadRwlockUnlockDiesWhenRealtime) {
-  auto Func = []() {
-    pthread_rwlock_t rw_lock;
-    pthread_rwlock_unlock(&rw_lock);
-  };
+TEST_F(PthreadRwlockTest, PthreadRwlockUnlockDiesWhenRealtime) {
+  RdLock();
+
+  auto Func = [this]() { Unlock(); };
   ExpectRealtimeDeath(Func, "pthread_rwlock_unlock");
+}
+
+TEST_F(PthreadRwlockTest, PthreadRwlockUnlockSurvivesWhenNonRealtime) {
+  RdLock();
+
+  auto Func = [this]() { Unlock(); };
   ExpectNonRealtimeSurvival(Func);
 }
 
-TEST(TestRtsanInterceptors, PthreadRwlockWrlockDiesWhenRealtime) {
-  auto Func = []() {
-    pthread_rwlock_t rw_lock;
-    pthread_rwlock_wrlock(&rw_lock);
-  };
+TEST_F(PthreadRwlockTest, PthreadRwlockWrlockDiesWhenRealtime) {
+  auto Func = [this]() { WrLock(); };
+
   ExpectRealtimeDeath(Func, "pthread_rwlock_wrlock");
+}
+
+TEST_F(PthreadRwlockTest, PthreadRwlockWrlockSurvivesWhenNonRealtime) {
+  auto Func = [this]() { WrLock(); };
+
   ExpectNonRealtimeSurvival(Func);
 }
 

}

void Lock() {
ASSERT_TRUE(!is_locked);
pthread_mutex_lock(&mutex);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Notice here, for instance, we were locking an uninitialized lock

TEST(TestRtsanInterceptors, PthreadMutexUnlockDiesWhenRealtime) {
auto Func = []() {
pthread_mutex_t mutex{};
pthread_mutex_unlock(&mutex);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unlocking an uninited, and not-locked lock

@cjappl cjappl requested review from nikic, MaskRay and vitalybuka August 19, 2024 03:35
@cjappl
Copy link
Contributor Author

cjappl commented Aug 19, 2024

CC @davidtrevelyan

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

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

(I'm starting a 3-week vacation this Friday and will have limited availability.)

compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp Outdated Show resolved Hide resolved
@cjappl cjappl merged commit 64afbf0 into llvm:main Aug 23, 2024
7 checks passed
@cjappl cjappl deleted the nohang_rwlock_tests branch August 23, 2024 21:09
5chmidti pushed a commit that referenced this pull request Aug 24, 2024
It is undefined behavior to lock or unlock an uninitialized lock, and
unlock a lock which isn't locked.

Introduce a fixture to set up and tear down the locks where
appropriate, and separates them into two tests (realtime death and non
realtime survival) so each test is guaranteed a fresh lock.
dmpolukhin pushed a commit to dmpolukhin/llvm-project that referenced this pull request Sep 2, 2024
…04733)

It is undefined behavior to lock or unlock an uninitialized lock, and
unlock a lock which isn't locked.

Introduce a fixture to set up and tear down the locks where
appropriate, and separates them into two tests (realtime death and non
realtime survival) so each test is guaranteed a fresh lock.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants