(root)/
glibc-2.38/
nptl/
tst-robust-fork.c
       1  /* Test the interaction of fork and robust mutexes.
       2     Copyright (C) 2017-2023 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
      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
      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
     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>