1/* Test the interaction of fork and robust mutexes.
2 Copyright (C) 2017-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <stdbool.h>
21#include <stdio.h>
22#include <support/check.h>
23#include <support/test-driver.h>
24#include <support/xthread.h>
25#include <support/xunistd.h>
26#include <sys/mman.h>
27
28/* Data shared between processes. */
29struct shared
30{
31 pthread_mutex_t parent_mutex;
32 pthread_mutex_t child_mutex;
33};
34
35/* These flags control which mutex settings are enabled in the parent
36 and child (separately). */
37enum mutex_bits
38 {
39 mutex_pshared = 1,
40 mutex_robust = 2,
41 mutex_pi = 4,
42 mutex_check = 8,
43
44 /* All bits combined. */
45 mutex_all_bits = 15,
46 };
47
48static void
49mutex_init (pthread_mutex_t *mutex, int bits)
50{
51 pthread_mutexattr_t attr;
52 xpthread_mutexattr_init (&attr);
53 if (bits & mutex_pshared)
54 xpthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
55 if (bits & mutex_robust)
56 xpthread_mutexattr_setrobust (&attr, PTHREAD_MUTEX_ROBUST);
57 if (bits & mutex_pi)
58 xpthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT);
59 if (bits & mutex_check)
60 xpthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
61 xpthread_mutex_init (mutex, &attr);
62 xpthread_mutexattr_destroy (&attr);
63}
64
65static void
66one_test (int parent_bits, int child_bits, int nonshared_bits,
67 bool lock_nonshared, bool lock_child)
68{
69
70 struct shared *shared = xmmap (NULL, length: sizeof (*shared),
71 PROT_READ | PROT_WRITE,
72 MAP_ANONYMOUS | MAP_SHARED, fd: -1);
73 mutex_init (mutex: &shared->parent_mutex, bits: parent_bits);
74 mutex_init (mutex: &shared->child_mutex, bits: child_bits);
75
76 /* Acquire the parent mutex in the parent. */
77 xpthread_mutex_lock (mutex: &shared->parent_mutex);
78
79 pthread_mutex_t nonshared_mutex;
80 mutex_init (mutex: &nonshared_mutex, bits: nonshared_bits);
81 if (lock_nonshared)
82 xpthread_mutex_lock (mutex: &nonshared_mutex);
83
84 pid_t pid = xfork ();
85 if (pid == 0)
86 {
87 /* Child process. */
88 if (lock_child)
89 xpthread_mutex_lock (mutex: &shared->child_mutex);
90 else
91 xmunmap (addr: shared, length: sizeof (*shared));
92 if (lock_nonshared)
93 /* Reinitialize the non-shared mutex if it was locked in the
94 parent. */
95 mutex_init (mutex: &nonshared_mutex, bits: nonshared_bits);
96 xpthread_mutex_lock (mutex: &nonshared_mutex);
97 /* For robust mutexes, the _exit call will perform the unlock
98 instead. */
99 if (lock_child && !(child_bits & mutex_robust))
100 xpthread_mutex_unlock (mutex: &shared->child_mutex);
101 _exit (0);
102 }
103 /* Parent process. */
104 {
105 int status;
106 xwaitpid (pid, status: &status, flags: 0);
107 TEST_VERIFY (status == 0);
108 }
109
110 if (parent_bits & mutex_check)
111 /* Test for expected self-deadlock. This is only possible to
112 detect if the mutex is error-checking. */
113 TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->parent_mutex) == EDEADLK);
114
115 pid = xfork ();
116 if (pid == 0)
117 {
118 /* Child process. We can perform some checks only if we are
119 dealing with process-shared mutexes. */
120 if (parent_bits & mutex_pshared)
121 /* It must not be possible to acquire the parent mutex.
122
123 NB: This check touches a mutex which has been acquired in
124 the parent at fork time, so it might be deemed undefined
125 behavior, pending the resolution of Austin Groups issue
126 1112. */
127 TEST_VERIFY_EXIT (pthread_mutex_trylock (&shared->parent_mutex)
128 == EBUSY);
129 if (lock_child && (child_bits & mutex_robust))
130 {
131 if (!(child_bits & mutex_pshared))
132 /* No further tests possible. */
133 _exit (0);
134 TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->child_mutex)
135 == EOWNERDEAD);
136 xpthread_mutex_consistent (&shared->child_mutex);
137 }
138 else
139 /* We did not acquire the lock in the first child process, or
140 we unlocked the mutex again because the mutex is not a
141 robust mutex. */
142 xpthread_mutex_lock (mutex: &shared->child_mutex);
143 xpthread_mutex_unlock (mutex: &shared->child_mutex);
144 _exit (0);
145 }
146 /* Parent process. */
147 {
148 int status;
149 xwaitpid (pid, status: &status, flags: 0);
150 TEST_VERIFY (status == 0);
151 }
152
153 if (lock_nonshared)
154 xpthread_mutex_unlock (mutex: &nonshared_mutex);
155 xpthread_mutex_unlock (mutex: &shared->parent_mutex);
156 xpthread_mutex_destroy (&shared->parent_mutex);
157 xpthread_mutex_destroy (&shared->child_mutex);
158 xpthread_mutex_destroy (&nonshared_mutex);
159 xmunmap (addr: shared, length: sizeof (*shared));
160}
161
162static int
163do_test (void)
164{
165 for (int parent_bits = 0; parent_bits <= mutex_all_bits; ++parent_bits)
166 for (int child_bits = 0; child_bits <= mutex_all_bits; ++child_bits)
167 for (int nonshared_bits = 0; nonshared_bits <= mutex_all_bits;
168 ++nonshared_bits)
169 for (int lock_nonshared = 0; lock_nonshared < 2; ++lock_nonshared)
170 for (int lock_child = 0; lock_child < 2; ++lock_child)
171 {
172 if (test_verbose)
173 printf (format: "info: parent_bits=0x%x child_bits=0x%x"
174 " nonshared_bits=0x%x%s%s\n",
175 parent_bits, child_bits, nonshared_bits,
176 lock_nonshared ? " lock_nonshared" : "",
177 lock_child ? " lock_child" : "");
178 one_test (parent_bits, child_bits, nonshared_bits,
179 lock_nonshared, lock_child);
180 }
181 return 0;
182}
183
184#define TIMEOUT 100
185#include <support/test-driver.c>
186

source code of glibc/nptl/tst-robust-fork.c