/* Test the interaction of fork and robust mutexes. Copyright (C) 2017-2018 Free Software Foundation, Inc. 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 . */ #include #include #include #include #include #include #include #include /* Data shared between processes. */ struct shared { pthread_mutex_t parent_mutex; pthread_mutex_t child_mutex; }; /* These flags control which mutex settings are enabled in the parent and child (separately). */ enum mutex_bits { mutex_pshared = 1, mutex_robust = 2, mutex_pi = 4, mutex_check = 8, /* All bits combined. */ mutex_all_bits = 15, }; static void mutex_init (pthread_mutex_t *mutex, int bits) { pthread_mutexattr_t attr; xpthread_mutexattr_init (&attr); if (bits & mutex_pshared) xpthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED); if (bits & mutex_robust) xpthread_mutexattr_setrobust (&attr, PTHREAD_MUTEX_ROBUST); if (bits & mutex_pi) xpthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT); if (bits & mutex_check) xpthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK); xpthread_mutex_init (mutex, &attr); xpthread_mutexattr_destroy (&attr); } static void one_test (int parent_bits, int child_bits, int nonshared_bits, bool lock_nonshared, bool lock_child) { struct shared *shared = xmmap (NULL, sizeof (*shared), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1); mutex_init (&shared->parent_mutex, parent_bits); mutex_init (&shared->child_mutex, child_bits); /* Acquire the parent mutex in the parent. */ xpthread_mutex_lock (&shared->parent_mutex); pthread_mutex_t nonshared_mutex; mutex_init (&nonshared_mutex, nonshared_bits); if (lock_nonshared) xpthread_mutex_lock (&nonshared_mutex); pid_t pid = xfork (); if (pid == 0) { /* Child process. */ if (lock_child) xpthread_mutex_lock (&shared->child_mutex); else xmunmap (shared, sizeof (*shared)); if (lock_nonshared) /* Reinitialize the non-shared mutex if it was locked in the parent. */ mutex_init (&nonshared_mutex, nonshared_bits); xpthread_mutex_lock (&nonshared_mutex); /* For robust mutexes, the _exit call will perform the unlock instead. */ if (lock_child && !(child_bits & mutex_robust)) xpthread_mutex_unlock (&shared->child_mutex); _exit (0); } /* Parent process. */ { int status; xwaitpid (pid, &status, 0); TEST_VERIFY (status == 0); } if (parent_bits & mutex_check) /* Test for expected self-deadlock. This is only possible to detect if the mutex is error-checking. */ TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->parent_mutex) == EDEADLK); pid = xfork (); if (pid == 0) { /* Child process. We can perform some checks only if we are dealing with process-shared mutexes. */ if (parent_bits & mutex_pshared) /* It must not be possible to acquire the parent mutex. NB: This check touches a mutex which has been acquired in the parent at fork time, so it might be deemed undefined behavior, pending the resolution of Austin Groups issue 1112. */ TEST_VERIFY_EXIT (pthread_mutex_trylock (&shared->parent_mutex) == EBUSY); if (lock_child && (child_bits & mutex_robust)) { if (!(child_bits & mutex_pshared)) /* No further tests possible. */ _exit (0); TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->child_mutex) == EOWNERDEAD); xpthread_mutex_consistent (&shared->child_mutex); } else /* We did not acquire the lock in the first child process, or we unlocked the mutex again because the mutex is not a robust mutex. */ xpthread_mutex_lock (&shared->child_mutex); xpthread_mutex_unlock (&shared->child_mutex); _exit (0); } /* Parent process. */ { int status; xwaitpid (pid, &status, 0); TEST_VERIFY (status == 0); } if (lock_nonshared) xpthread_mutex_unlock (&nonshared_mutex); xpthread_mutex_unlock (&shared->parent_mutex); xpthread_mutex_destroy (&shared->parent_mutex); xpthread_mutex_destroy (&shared->child_mutex); xpthread_mutex_destroy (&nonshared_mutex); xmunmap (shared, sizeof (*shared)); } static int do_test (void) { for (int parent_bits = 0; parent_bits <= mutex_all_bits; ++parent_bits) for (int child_bits = 0; child_bits <= mutex_all_bits; ++child_bits) for (int nonshared_bits = 0; nonshared_bits <= mutex_all_bits; ++nonshared_bits) for (int lock_nonshared = 0; lock_nonshared < 2; ++lock_nonshared) for (int lock_child = 0; lock_child < 2; ++lock_child) { if (test_verbose) printf ("info: parent_bits=0x%x child_bits=0x%x" " nonshared_bits=0x%x%s%s\n", parent_bits, child_bits, nonshared_bits, lock_nonshared ? " lock_nonshared" : "", lock_child ? " lock_child" : ""); one_test (parent_bits, child_bits, nonshared_bits, lock_nonshared, lock_child); } return 0; } #define TIMEOUT 100 #include