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. */
29 struct 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).  */
37 enum 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 
48 static void
mutex_init(pthread_mutex_t * mutex,int bits)49 mutex_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 
65 static void
one_test(int parent_bits,int child_bits,int nonshared_bits,bool lock_nonshared,bool lock_child)66 one_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, sizeof (*shared),
71                                  PROT_READ | PROT_WRITE,
72                                  MAP_ANONYMOUS | MAP_SHARED, -1);
73   mutex_init (&shared->parent_mutex, parent_bits);
74   mutex_init (&shared->child_mutex, child_bits);
75 
76   /* Acquire the parent mutex in the parent.  */
77   xpthread_mutex_lock (&shared->parent_mutex);
78 
79   pthread_mutex_t nonshared_mutex;
80   mutex_init (&nonshared_mutex, nonshared_bits);
81   if (lock_nonshared)
82     xpthread_mutex_lock (&nonshared_mutex);
83 
84   pid_t pid = xfork ();
85   if (pid == 0)
86     {
87       /* Child process.  */
88       if (lock_child)
89         xpthread_mutex_lock (&shared->child_mutex);
90       else
91         xmunmap (shared, sizeof (*shared));
92       if (lock_nonshared)
93         /* Reinitialize the non-shared mutex if it was locked in the
94            parent.  */
95         mutex_init (&nonshared_mutex, nonshared_bits);
96       xpthread_mutex_lock (&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 (&shared->child_mutex);
101       _exit (0);
102     }
103   /* Parent process. */
104   {
105     int status;
106     xwaitpid (pid, &status, 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 (&shared->child_mutex);
143       xpthread_mutex_unlock (&shared->child_mutex);
144       _exit (0);
145     }
146   /* Parent process. */
147   {
148     int status;
149     xwaitpid (pid, &status, 0);
150     TEST_VERIFY (status == 0);
151   }
152 
153   if (lock_nonshared)
154     xpthread_mutex_unlock (&nonshared_mutex);
155   xpthread_mutex_unlock (&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 (shared, sizeof (*shared));
160 }
161 
162 static int
do_test(void)163 do_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 ("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