Blame lib/sigaction.c

Packit 33f14e
/* POSIX compatible signal blocking.
Packit 33f14e
   Copyright (C) 2008-2017 Free Software Foundation, Inc.
Packit 33f14e
   Written by Eric Blake <ebb9@byu.net>, 2008.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
/* Specification.  */
Packit 33f14e
#include <signal.h>
Packit 33f14e
Packit 33f14e
#include <errno.h>
Packit 33f14e
#include <stdint.h>
Packit 33f14e
#include <stdlib.h>
Packit 33f14e
Packit 33f14e
/* This implementation of sigaction is tailored to native Windows behavior:
Packit 33f14e
   signal() has SysV semantics (ie. the handler is uninstalled before
Packit 33f14e
   it is invoked).  This is an inherent data race if an asynchronous
Packit 33f14e
   signal is sent twice in a row before we can reinstall our handler,
Packit 33f14e
   but there's nothing we can do about it.  Meanwhile, sigprocmask()
Packit 33f14e
   is not present, and while we can use the gnulib replacement to
Packit 33f14e
   provide critical sections, it too suffers from potential data races
Packit 33f14e
   in the face of an ill-timed asynchronous signal.  And we compound
Packit 33f14e
   the situation by reading static storage in a signal handler, which
Packit 33f14e
   POSIX warns is not generically async-signal-safe.  Oh well.
Packit 33f14e
Packit 33f14e
   Additionally:
Packit 33f14e
     - We don't implement SA_NOCLDSTOP or SA_NOCLDWAIT, because SIGCHLD
Packit 33f14e
       is not defined.
Packit 33f14e
     - We don't implement SA_ONSTACK, because sigaltstack() is not present.
Packit 33f14e
     - We ignore SA_RESTART, because blocking native Windows API calls are
Packit 33f14e
       not interrupted anyway when an asynchronous signal occurs, and the
Packit 33f14e
       MSVCRT runtime never sets errno to EINTR.
Packit 33f14e
     - We don't implement SA_SIGINFO because it is impossible to do so
Packit 33f14e
       portably.
Packit 33f14e
Packit 33f14e
   POSIX states that an application should not mix signal() and
Packit 33f14e
   sigaction().  We support the use of signal() within the gnulib
Packit 33f14e
   sigprocmask() substitute, but all other application code linked
Packit 33f14e
   with this module should stick with only sigaction().  */
Packit 33f14e
Packit 33f14e
/* Check some of our assumptions.  */
Packit 33f14e
#if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT
Packit 33f14e
# error "Revisit the assumptions made in the sigaction module"
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* Out-of-range substitutes make a good fallback for uncatchable
Packit 33f14e
   signals.  */
Packit 33f14e
#ifndef SIGKILL
Packit 33f14e
# define SIGKILL (-1)
Packit 33f14e
#endif
Packit 33f14e
#ifndef SIGSTOP
Packit 33f14e
# define SIGSTOP (-1)
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
Packit 33f14e
   for the signal SIGABRT.  Only one signal handler is stored for both
Packit 33f14e
   SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
Packit 33f14e
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 33f14e
# undef SIGABRT_COMPAT
Packit 33f14e
# define SIGABRT_COMPAT 6
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* A signal handler.  */
Packit 33f14e
typedef void (*handler_t) (int signal);
Packit 33f14e
Packit 33f14e
/* Set of current actions.  If sa_handler for an entry is NULL, then
Packit 33f14e
   that signal is not currently handled by the sigaction handler.  */
Packit 33f14e
static struct sigaction volatile action_array[NSIG] /* = 0 */;
Packit 33f14e
Packit 33f14e
/* Signal handler that is installed for signals.  */
Packit 33f14e
static void
Packit 33f14e
sigaction_handler (int sig)
Packit 33f14e
{
Packit 33f14e
  handler_t handler;
Packit 33f14e
  sigset_t mask;
Packit 33f14e
  sigset_t oldmask;
Packit 33f14e
  int saved_errno = errno;
Packit 33f14e
  if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler)
Packit 33f14e
    {
Packit 33f14e
      /* Unexpected situation; be careful to avoid recursive abort.  */
Packit 33f14e
      if (sig == SIGABRT)
Packit 33f14e
        signal (SIGABRT, SIG_DFL);
Packit 33f14e
      abort ();
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* Reinstall the signal handler when required; otherwise update the
Packit 33f14e
     bookkeeping so that the user's handler may call sigaction and get
Packit 33f14e
     accurate results.  We know the signal isn't currently blocked, or
Packit 33f14e
     we wouldn't be in its handler, therefore we know that we are not
Packit 33f14e
     interrupting a sigaction() call.  There is a race where any
Packit 33f14e
     asynchronous instance of the same signal occurring before we
Packit 33f14e
     reinstall the handler will trigger the default handler; oh
Packit 33f14e
     well.  */
Packit 33f14e
  handler = action_array[sig].sa_handler;
Packit 33f14e
  if ((action_array[sig].sa_flags & SA_RESETHAND) == 0)
Packit 33f14e
    signal (sig, sigaction_handler);
Packit 33f14e
  else
Packit 33f14e
    action_array[sig].sa_handler = NULL;
Packit 33f14e
Packit 33f14e
  /* Block appropriate signals.  */
Packit 33f14e
  mask = action_array[sig].sa_mask;
Packit 33f14e
  if ((action_array[sig].sa_flags & SA_NODEFER) == 0)
Packit 33f14e
    sigaddset (&mask, sig);
Packit 33f14e
  sigprocmask (SIG_BLOCK, &mask, &oldmask);
Packit 33f14e
Packit 33f14e
  /* Invoke the user's handler, then restore prior mask.  */
Packit 33f14e
  errno = saved_errno;
Packit 33f14e
  handler (sig);
Packit 33f14e
  saved_errno = errno;
Packit 33f14e
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 33f14e
  errno = saved_errno;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Change and/or query the action that will be taken on delivery of
Packit 33f14e
   signal SIG.  If not NULL, ACT describes the new behavior.  If not
Packit 33f14e
   NULL, OACT is set to the prior behavior.  Return 0 on success, or
Packit 33f14e
   set errno and return -1 on failure.  */
Packit 33f14e
int
Packit 33f14e
sigaction (int sig, const struct sigaction *restrict act,
Packit 33f14e
           struct sigaction *restrict oact)
Packit 33f14e
{
Packit 33f14e
  sigset_t mask;
Packit 33f14e
  sigset_t oldmask;
Packit 33f14e
  int saved_errno;
Packit 33f14e
Packit 33f14e
  if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP
Packit 33f14e
      || (act && act->sa_handler == SIG_ERR))
Packit 33f14e
    {
Packit 33f14e
      errno = EINVAL;
Packit 33f14e
      return -1;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
#ifdef SIGABRT_COMPAT
Packit 33f14e
  if (sig == SIGABRT_COMPAT)
Packit 33f14e
    sig = SIGABRT;
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
  /* POSIX requires sigaction() to be async-signal-safe.  In other
Packit 33f14e
     words, if an asynchronous signal can occur while we are anywhere
Packit 33f14e
     inside this function, the user's handler could then call
Packit 33f14e
     sigaction() recursively and expect consistent results.  We meet
Packit 33f14e
     this rule by using sigprocmask to block all signals before
Packit 33f14e
     modifying any data structure that could be read from a signal
Packit 33f14e
     handler; this works since we know that the gnulib sigprocmask
Packit 33f14e
     replacement does not try to use sigaction() from its handler.  */
Packit 33f14e
  if (!act && !oact)
Packit 33f14e
    return 0;
Packit 33f14e
  sigfillset (&mask);
Packit 33f14e
  sigprocmask (SIG_BLOCK, &mask, &oldmask);
Packit 33f14e
  if (oact)
Packit 33f14e
    {
Packit 33f14e
      if (action_array[sig].sa_handler)
Packit 33f14e
        *oact = action_array[sig];
Packit 33f14e
      else
Packit 33f14e
        {
Packit 33f14e
          /* Safe to change the handler at will here, since all
Packit 33f14e
             signals are currently blocked.  */
Packit 33f14e
          oact->sa_handler = signal (sig, SIG_DFL);
Packit 33f14e
          if (oact->sa_handler == SIG_ERR)
Packit 33f14e
            goto failure;
Packit 33f14e
          signal (sig, oact->sa_handler);
Packit 33f14e
          oact->sa_flags = SA_RESETHAND | SA_NODEFER;
Packit 33f14e
          sigemptyset (&oact->sa_mask);
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (act)
Packit 33f14e
    {
Packit 33f14e
      /* Safe to install the handler before updating action_array,
Packit 33f14e
         since all signals are currently blocked.  */
Packit 33f14e
      if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN)
Packit 33f14e
        {
Packit 33f14e
          if (signal (sig, act->sa_handler) == SIG_ERR)
Packit 33f14e
            goto failure;
Packit 33f14e
          action_array[sig].sa_handler = NULL;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        {
Packit 33f14e
          if (signal (sig, sigaction_handler) == SIG_ERR)
Packit 33f14e
            goto failure;
Packit 33f14e
          action_array[sig] = *act;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 33f14e
  return 0;
Packit 33f14e
Packit 33f14e
 failure:
Packit 33f14e
  saved_errno = errno;
Packit 33f14e
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 33f14e
  errno = saved_errno;
Packit 33f14e
  return -1;
Packit 33f14e
}