Blame nptl/tst-robust-fork.c

Packit 6c4009
/* Test the interaction of fork and robust mutexes.
Packit 6c4009
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/test-driver.h>
Packit 6c4009
#include <support/xthread.h>
Packit 6c4009
#include <support/xunistd.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
Packit 6c4009
/* Data shared between processes. */
Packit 6c4009
struct shared
Packit 6c4009
{
Packit 6c4009
  pthread_mutex_t parent_mutex;
Packit 6c4009
  pthread_mutex_t child_mutex;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* These flags control which mutex settings are enabled in the parent
Packit 6c4009
   and child (separately).  */
Packit 6c4009
enum mutex_bits
Packit 6c4009
  {
Packit 6c4009
    mutex_pshared = 1,
Packit 6c4009
    mutex_robust = 2,
Packit 6c4009
    mutex_pi = 4,
Packit 6c4009
    mutex_check = 8,
Packit 6c4009
Packit 6c4009
    /* All bits combined.  */
Packit 6c4009
    mutex_all_bits = 15,
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
mutex_init (pthread_mutex_t *mutex, int bits)
Packit 6c4009
{
Packit 6c4009
  pthread_mutexattr_t attr;
Packit 6c4009
  xpthread_mutexattr_init (&attr);
Packit 6c4009
  if (bits & mutex_pshared)
Packit 6c4009
    xpthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
Packit 6c4009
  if (bits & mutex_robust)
Packit 6c4009
    xpthread_mutexattr_setrobust (&attr, PTHREAD_MUTEX_ROBUST);
Packit 6c4009
  if (bits & mutex_pi)
Packit 6c4009
    xpthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT);
Packit 6c4009
  if (bits & mutex_check)
Packit 6c4009
    xpthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
Packit 6c4009
  xpthread_mutex_init (mutex, &attr);
Packit 6c4009
  xpthread_mutexattr_destroy (&attr);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
one_test (int parent_bits, int child_bits, int nonshared_bits,
Packit 6c4009
          bool lock_nonshared, bool lock_child)
Packit 6c4009
{
Packit 6c4009
Packit 6c4009
  struct shared *shared = xmmap (NULL, sizeof (*shared),
Packit 6c4009
                                 PROT_READ | PROT_WRITE,
Packit 6c4009
                                 MAP_ANONYMOUS | MAP_SHARED, -1);
Packit 6c4009
  mutex_init (&shared->parent_mutex, parent_bits);
Packit 6c4009
  mutex_init (&shared->child_mutex, child_bits);
Packit 6c4009
Packit 6c4009
  /* Acquire the parent mutex in the parent.  */
Packit 6c4009
  xpthread_mutex_lock (&shared->parent_mutex);
Packit 6c4009
Packit 6c4009
  pthread_mutex_t nonshared_mutex;
Packit 6c4009
  mutex_init (&nonshared_mutex, nonshared_bits);
Packit 6c4009
  if (lock_nonshared)
Packit 6c4009
    xpthread_mutex_lock (&nonshared_mutex);
Packit 6c4009
Packit 6c4009
  pid_t pid = xfork ();
Packit 6c4009
  if (pid == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Child process.  */
Packit 6c4009
      if (lock_child)
Packit 6c4009
        xpthread_mutex_lock (&shared->child_mutex);
Packit 6c4009
      else
Packit 6c4009
        xmunmap (shared, sizeof (*shared));
Packit 6c4009
      if (lock_nonshared)
Packit 6c4009
        /* Reinitialize the non-shared mutex if it was locked in the
Packit 6c4009
           parent.  */
Packit 6c4009
        mutex_init (&nonshared_mutex, nonshared_bits);
Packit 6c4009
      xpthread_mutex_lock (&nonshared_mutex);
Packit 6c4009
      /* For robust mutexes, the _exit call will perform the unlock
Packit 6c4009
         instead.  */
Packit 6c4009
      if (lock_child && !(child_bits & mutex_robust))
Packit 6c4009
        xpthread_mutex_unlock (&shared->child_mutex);
Packit 6c4009
      _exit (0);
Packit 6c4009
    }
Packit 6c4009
  /* Parent process. */
Packit 6c4009
  {
Packit 6c4009
    int status;
Packit 6c4009
    xwaitpid (pid, &status, 0);
Packit 6c4009
    TEST_VERIFY (status == 0);
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  if (parent_bits & mutex_check)
Packit 6c4009
    /* Test for expected self-deadlock.  This is only possible to
Packit 6c4009
       detect if the mutex is error-checking.  */
Packit 6c4009
    TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->parent_mutex) == EDEADLK);
Packit 6c4009
Packit 6c4009
  pid = xfork ();
Packit 6c4009
  if (pid == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Child process.  We can perform some checks only if we are
Packit 6c4009
         dealing with process-shared mutexes.  */
Packit 6c4009
      if (parent_bits & mutex_pshared)
Packit 6c4009
        /* It must not be possible to acquire the parent mutex.
Packit 6c4009
Packit 6c4009
           NB: This check touches a mutex which has been acquired in
Packit 6c4009
           the parent at fork time, so it might be deemed undefined
Packit 6c4009
           behavior, pending the resolution of Austin Groups issue
Packit 6c4009
           1112.  */
Packit 6c4009
        TEST_VERIFY_EXIT (pthread_mutex_trylock (&shared->parent_mutex)
Packit 6c4009
                          == EBUSY);
Packit 6c4009
      if (lock_child && (child_bits & mutex_robust))
Packit 6c4009
        {
Packit 6c4009
          if (!(child_bits & mutex_pshared))
Packit 6c4009
            /* No further tests possible.  */
Packit 6c4009
            _exit (0);
Packit 6c4009
          TEST_VERIFY_EXIT (pthread_mutex_lock (&shared->child_mutex)
Packit 6c4009
                            == EOWNERDEAD);
Packit 6c4009
          xpthread_mutex_consistent (&shared->child_mutex);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        /* We did not acquire the lock in the first child process, or
Packit 6c4009
           we unlocked the mutex again because the mutex is not a
Packit 6c4009
           robust mutex.  */
Packit 6c4009
        xpthread_mutex_lock (&shared->child_mutex);
Packit 6c4009
      xpthread_mutex_unlock (&shared->child_mutex);
Packit 6c4009
      _exit (0);
Packit 6c4009
    }
Packit 6c4009
  /* Parent process. */
Packit 6c4009
  {
Packit 6c4009
    int status;
Packit 6c4009
    xwaitpid (pid, &status, 0);
Packit 6c4009
    TEST_VERIFY (status == 0);
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  if (lock_nonshared)
Packit 6c4009
    xpthread_mutex_unlock (&nonshared_mutex);
Packit 6c4009
  xpthread_mutex_unlock (&shared->parent_mutex);
Packit 6c4009
  xpthread_mutex_destroy (&shared->parent_mutex);
Packit 6c4009
  xpthread_mutex_destroy (&shared->child_mutex);
Packit 6c4009
  xpthread_mutex_destroy (&nonshared_mutex);
Packit 6c4009
  xmunmap (shared, sizeof (*shared));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  for (int parent_bits = 0; parent_bits <= mutex_all_bits; ++parent_bits)
Packit 6c4009
    for (int child_bits = 0; child_bits <= mutex_all_bits; ++child_bits)
Packit 6c4009
      for (int nonshared_bits = 0; nonshared_bits <= mutex_all_bits;
Packit 6c4009
           ++nonshared_bits)
Packit 6c4009
        for (int lock_nonshared = 0; lock_nonshared < 2; ++lock_nonshared)
Packit 6c4009
          for (int lock_child = 0; lock_child < 2; ++lock_child)
Packit 6c4009
            {
Packit 6c4009
              if (test_verbose)
Packit 6c4009
                printf ("info: parent_bits=0x%x child_bits=0x%x"
Packit 6c4009
                        " nonshared_bits=0x%x%s%s\n",
Packit 6c4009
                        parent_bits, child_bits, nonshared_bits,
Packit 6c4009
                        lock_nonshared ? " lock_nonshared" : "",
Packit 6c4009
                        lock_child ? " lock_child" : "");
Packit 6c4009
              one_test (parent_bits, child_bits, nonshared_bits,
Packit 6c4009
                        lock_nonshared, lock_child);
Packit 6c4009
            }
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define TIMEOUT 100
Packit 6c4009
#include <support/test-driver.c>