diff options
author | Florian Weimer <fweimer@redhat.com> | 2017-01-27 06:53:19 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2017-01-27 06:53:20 +0100 |
commit | faf0e9c84119742dd9ebb79060faa22c52ae80a1 (patch) | |
tree | aaf2df0e207bde2a4dfe6c9623e013364642a076 /nptl/tst-robust-fork.c | |
parent | 5653ab12b4ae15b32d41de7c56b2a4626cd0437a (diff) | |
download | glibc-faf0e9c84119742dd9ebb79060faa22c52ae80a1.tar.gz glibc-faf0e9c84119742dd9ebb79060faa22c52ae80a1.tar.xz glibc-faf0e9c84119742dd9ebb79060faa22c52ae80a1.zip |
nptl: Add tst-robust-fork
Diffstat (limited to 'nptl/tst-robust-fork.c')
-rw-r--r-- | nptl/tst-robust-fork.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/nptl/tst-robust-fork.c b/nptl/tst-robust-fork.c new file mode 100644 index 0000000000..4a12ff000d --- /dev/null +++ b/nptl/tst-robust-fork.c @@ -0,0 +1,184 @@ +/* Test the interaction of fork and robust mutexes. + Copyright (C) 2017 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 + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <support/check.h> +#include <support/test-driver.h> +#include <support/xthread.h> +#include <support/xunistd.h> +#include <sys/mman.h> + +/* 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; +} + +#include <support/test-driver.c> |