[PATCH v2] nptl: Release robust mutex after trylock returns ENOTRECOVERABLE (BZ #27458)

"後藤 遼(新製品開発3)" ryou_gotou@aiphone.co.jp
Tue Sep 29 02:02:50 GMT 2026


pthread_mutex_trylock on a not-recoverable robust non-PI mutex acquires
the lock word using a compare-and-exchange, then returns
ENOTRECOVERABLE without releasing it.  The CAS returns the old lock
word value, which is zero on success, so testing oldval against the
thread ID never unlocks the mutex.  A subsequent trylock returns EBUSY,
and a lock attempt can block indefinitely.

Release the lock when oldval is zero.  Add a regression test covering
normal, recursive, and error-checking robust mutexes with both
process-private and process-shared attributes, with and without PI.
Skip PI combinations on systems that do not support PI robust mutexes.

Tested on x86_64-linux-gnu (WSL2):
	nptl/tst-robust-trylock-notrecoverable (12 combinations, PASS).

Signed-off-by: Ryo Goto <ryou_gotou@aiphone.co.jp>

---
Copyright status: no FSF assignment; contributing under DCO.

Changes since v1:
- Rebased the test registration after tst-robust12.
- Skip PI cases when pthread_mutex_init returns ENOTSUP.
- Cover recursive and error-checking robust mutexes as well.

 nptl/Makefile                            |   1 +
 nptl/pthread_mutex_trylock.c             |   2 +-
 nptl/tst-robust-trylock-notrecoverable.c | 108 ++++++++++++++++++++++++
 3 files changed, 110 insertions(+), 1 deletion(-)
 create mode 100644 nptl/tst-robust-trylock-notrecoverable.c

diff --git a/nptl/Makefile b/nptl/Makefile
index dfdfd261..9a8ec659 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -329,6 +329,7 @@ tests = \
   tst-pthread_gettid_np \
   tst-robust-fork \
   tst-robust12 \
+  tst-robust-trylock-notrecoverable \
   tst-robustpi1 \
   tst-robustpi2 \
   tst-robustpi3 \
diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c
index 15786fda..e1662d00 100644
--- a/nptl/pthread_mutex_trylock.c
+++ b/nptl/pthread_mutex_trylock.c
@@ -170,7 +170,7 @@ ___pthread_mutex_trylock (pthread_mutex_t *mutex)
 	    {
 	      /* This mutex is now not recoverable.  */
 	      mutex->__data.__count = 0;
-	      if (oldval == id)
+	      if (oldval == 0)
 		lll_unlock (mutex->__data.__lock,
 			    PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
 	      /* FIXME This violates the mutex destruction requirements.  See
diff --git a/nptl/tst-robust-trylock-notrecoverable.c b/nptl/tst-robust-trylock-notrecoverable.c
new file mode 100644
index 00000000..f732f42a
--- /dev/null
+++ b/nptl/tst-robust-trylock-notrecoverable.c
@@ -0,0 +1,108 @@
+/* Test that pthread_mutex_trylock releases an unrecoverable robust mutex.
+   Copyright The GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/timespec.h>
+#include <support/xthread.h>
+#include <support/xtime.h>
+
+static pthread_mutex_t mutex;
+
+static void *
+lock_and_exit (void *arg)
+{
+  (void) arg;
+  TEST_COMPARE (pthread_mutex_lock (&mutex), 0);
+  return NULL;
+}
+
+static void *
+trylock_in_thread (void *arg)
+{
+  int *result = arg;
+  *result = pthread_mutex_trylock (&mutex);
+  return NULL;
+}
+
+static void
+run_test (bool pshared, bool pi, int type)
+{
+  printf ("info: pshared=%d pi=%d type=%d\n", pshared, pi, type);
+
+  pthread_mutexattr_t attr;
+  xpthread_mutexattr_init (&attr);
+  xpthread_mutexattr_setrobust (&attr, PTHREAD_MUTEX_ROBUST);
+  xpthread_mutexattr_settype (&attr, type);
+  if (pshared)
+    xpthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
+  if (pi)
+    xpthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT);
+
+  int init_result = pthread_mutex_init (&mutex, &attr);
+  xpthread_mutexattr_destroy (&attr);
+  if (pi && init_result == ENOTSUP)
+    {
+      puts ("info: PI robust mutexes not supported; skipping this combination");
+      return;
+    }
+  TEST_COMPARE (init_result, 0);
+  if (init_result != 0)
+    return;
+
+  /* The owner dies, and its successor unlocks without making the mutex
+     consistent.  All subsequent lock attempts must fail.  */
+  xpthread_join (xpthread_create (NULL, lock_and_exit, NULL));
+  TEST_COMPARE (pthread_mutex_lock (&mutex), EOWNERDEAD);
+  TEST_COMPARE (pthread_mutex_unlock (&mutex), 0);
+
+  TEST_COMPARE (pthread_mutex_lock (&mutex), ENOTRECOVERABLE);
+  TEST_COMPARE (pthread_mutex_trylock (&mutex), ENOTRECOVERABLE);
+  /* With a recursive mutex, an incorrectly retained lock can return 0
+     rather than EBUSY on a second trylock by the same thread.  */
+  TEST_COMPARE (pthread_mutex_trylock (&mutex), ENOTRECOVERABLE);
+
+  int result = -1;
+  xpthread_join (xpthread_create (NULL, trylock_in_thread, &result));
+  TEST_COMPARE (result, ENOTRECOVERABLE);
+
+  /* A bounded lock attempt detects a lock word retained by trylock.  */
+  struct timespec timeout = timespec_add (xclock_now (CLOCK_REALTIME),
+					  make_timespec (1, 0));
+  TEST_COMPARE (pthread_mutex_timedlock (&mutex, &timeout), ENOTRECOVERABLE);
+  TEST_COMPARE (pthread_mutex_destroy (&mutex), 0);
+}
+
+static int
+do_test (void)
+{
+  static const int types[] = {
+    PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK
+  };
+
+  for (size_t index = 0; index < sizeof (types) / sizeof (types[0]); ++index)
+    for (int pshared = 0; pshared < 2; ++pshared)
+      for (int pi = 0; pi < 2; ++pi)
+	  run_test (pshared, pi, types[index]);
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.43.0



More information about the Libc-alpha mailing list