(root)/
m4-1.4.19/
lib/
fatal-signal.c
       1  /* Emergency actions in case of a fatal signal.
       2     Copyright (C) 2003-2004, 2006-2021 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2003.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program 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
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "fatal-signal.h"
      23  
      24  #include <stdbool.h>
      25  #include <stdlib.h>
      26  #include <signal.h>
      27  #include <unistd.h>
      28  
      29  #include "glthread/lock.h"
      30  #include "thread-optim.h"
      31  #include "sig-handler.h"
      32  
      33  #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
      34  
      35  /* ========================================================================= */
      36  
      37  
      38  /* The list of fatal signals.
      39     These are those signals whose default action is to terminate the process
      40     without a core dump, except
      41       SIGKILL - because it cannot be caught,
      42       SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
      43         often use them for their own purpose,
      44       SIGPROF SIGVTALRM - because they are used for profiling,
      45       SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
      46       SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
      47       SIGPWR - because it of too special use,
      48       SIGRTMIN...SIGRTMAX - because they are reserved for application use.
      49     plus
      50       SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM.  */
      51  
      52  static int fatal_signals[] =
      53    {
      54      /* ISO C 99 signals.  */
      55  #ifdef SIGINT
      56      SIGINT,
      57  #endif
      58  #ifdef SIGTERM
      59      SIGTERM,
      60  #endif
      61      /* POSIX:2001 signals.  */
      62  #ifdef SIGHUP
      63      SIGHUP,
      64  #endif
      65  #ifdef SIGPIPE
      66      SIGPIPE,
      67  #endif
      68      /* BSD signals.  */
      69  #ifdef SIGXCPU
      70      SIGXCPU,
      71  #endif
      72  #ifdef SIGXFSZ
      73      SIGXFSZ,
      74  #endif
      75      /* Native Windows signals.  */
      76  #ifdef SIGBREAK
      77      SIGBREAK,
      78  #endif
      79      0
      80    };
      81  
      82  #define num_fatal_signals (SIZEOF (fatal_signals) - 1)
      83  
      84  /* Eliminate signals whose signal handler is SIG_IGN.  */
      85  
      86  static void
      87  init_fatal_signals (void)
      88  {
      89    /* This function is multithread-safe even without synchronization, because
      90       if two threads execute it simultaneously, the fatal_signals[] array will
      91       not change any more after the first of the threads has completed this
      92       function.  */
      93    static bool fatal_signals_initialized = false;
      94    if (!fatal_signals_initialized)
      95      {
      96        size_t i;
      97  
      98        for (i = 0; i < num_fatal_signals; i++)
      99          {
     100            struct sigaction action;
     101  
     102            if (sigaction (fatal_signals[i], NULL, &action) >= 0
     103                && get_handler (&action) == SIG_IGN)
     104              fatal_signals[i] = -1;
     105          }
     106  
     107        fatal_signals_initialized = true;
     108      }
     109  }
     110  
     111  
     112  /* ========================================================================= */
     113  
     114  
     115  typedef _GL_ASYNC_SAFE void (*action_t) (int sig);
     116  
     117  /* Type of an entry in the actions array.
     118     The 'action' field is accessed from within the fatal_signal_handler(),
     119     therefore we mark it as 'volatile'.  */
     120  typedef struct
     121  {
     122    volatile action_t action;
     123  }
     124  actions_entry_t;
     125  
     126  /* The registered cleanup actions.  */
     127  static actions_entry_t static_actions[32];
     128  static actions_entry_t * volatile actions = static_actions;
     129  static sig_atomic_t volatile actions_count = 0;
     130  static size_t actions_allocated = SIZEOF (static_actions);
     131  
     132  
     133  /* The saved signal handlers.
     134     Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34.  */
     135  static struct sigaction saved_sigactions[64];
     136  
     137  
     138  /* Uninstall the handlers.  */
     139  static _GL_ASYNC_SAFE void
     140  uninstall_handlers (void)
     141  {
     142    size_t i;
     143  
     144    for (i = 0; i < num_fatal_signals; i++)
     145      if (fatal_signals[i] >= 0)
     146        {
     147          int sig = fatal_signals[i];
     148          if (saved_sigactions[sig].sa_handler == SIG_IGN)
     149            saved_sigactions[sig].sa_handler = SIG_DFL;
     150          sigaction (sig, &saved_sigactions[sig], NULL);
     151        }
     152  }
     153  
     154  
     155  /* The signal handler.  It gets called asynchronously.  */
     156  static _GL_ASYNC_SAFE void
     157  fatal_signal_handler (int sig)
     158  {
     159    for (;;)
     160      {
     161        /* Get the last registered cleanup action, in a reentrant way.  */
     162        action_t action;
     163        size_t n = actions_count;
     164        if (n == 0)
     165          break;
     166        n--;
     167        actions_count = n;
     168        action = actions[n].action;
     169        /* Execute the action.  */
     170        action (sig);
     171      }
     172  
     173    /* Now execute the signal's default action.
     174       If the signal being delivered was blocked, the re-raised signal would be
     175       delivered when this handler returns.  But the way we install this handler,
     176       no signal is blocked, and the re-raised signal is delivered already
     177       during raise().  */
     178    uninstall_handlers ();
     179    raise (sig);
     180  }
     181  
     182  
     183  /* Install the handlers.  */
     184  static void
     185  install_handlers (void)
     186  {
     187    size_t i;
     188    struct sigaction action;
     189  
     190    action.sa_handler = &fatal_signal_handler;
     191    /* If we get a fatal signal while executing fatal_signal_handler, enter
     192       fatal_signal_handler recursively, since it is reentrant.  Hence no
     193       SA_RESETHAND.  */
     194    action.sa_flags = SA_NODEFER;
     195    sigemptyset (&action.sa_mask);
     196    for (i = 0; i < num_fatal_signals; i++)
     197      if (fatal_signals[i] >= 0)
     198        {
     199          int sig = fatal_signals[i];
     200  
     201          if (!(sig < sizeof (saved_sigactions) / sizeof (saved_sigactions[0])))
     202            abort ();
     203          sigaction (sig, &action, &saved_sigactions[sig]);
     204        }
     205  }
     206  
     207  
     208  /* Lock that makes at_fatal_signal multi-thread safe.  */
     209  gl_lock_define_initialized (static, at_fatal_signal_lock)
     210  
     211  /* Register a cleanup function to be executed when a catchable fatal signal
     212     occurs.  */
     213  int
     214  at_fatal_signal (action_t action)
     215  {
     216    bool mt = gl_multithreaded ();
     217  
     218    if (mt) gl_lock_lock (at_fatal_signal_lock);
     219  
     220    static bool cleanup_initialized = false;
     221    if (!cleanup_initialized)
     222      {
     223        init_fatal_signals ();
     224        install_handlers ();
     225        cleanup_initialized = true;
     226      }
     227  
     228    int ret = 0;
     229  
     230    if (actions_count == actions_allocated)
     231      {
     232        /* Extend the actions array.  Note that we cannot use xrealloc(),
     233           because then the cleanup() function could access an already
     234           deallocated array.  */
     235        actions_entry_t *old_actions = actions;
     236        size_t old_actions_allocated = actions_allocated;
     237        size_t new_actions_allocated = 2 * actions_allocated;
     238        actions_entry_t *new_actions =
     239          (actions_entry_t *)
     240          malloc (new_actions_allocated * sizeof (actions_entry_t));
     241        if (new_actions == NULL)
     242          {
     243            ret = -1;
     244            goto done;
     245          }
     246  
     247        size_t k;
     248        /* Don't use memcpy() here, because memcpy takes non-volatile arguments
     249           and is therefore not guaranteed to complete all memory stores before
     250           the next statement.  */
     251        for (k = 0; k < old_actions_allocated; k++)
     252          new_actions[k] = old_actions[k];
     253        actions = new_actions;
     254        actions_allocated = new_actions_allocated;
     255        /* Now we can free the old actions array.  */
     256        /* No, we can't do that.  If fatal_signal_handler is running in a
     257           different thread and has already fetched the actions pointer (getting
     258           old_actions) but not yet accessed its n-th element, that thread may
     259           crash when accessing an element of the already freed old_actions
     260           array.  */
     261        #if 0
     262        if (old_actions != static_actions)
     263          free (old_actions);
     264        #endif
     265      }
     266    /* The two uses of 'volatile' in the types above (and ISO C 99 section
     267       5.1.2.3.(5)) ensure that we increment the actions_count only after
     268       the new action has been written to the memory location
     269       actions[actions_count].  */
     270    actions[actions_count].action = action;
     271    actions_count++;
     272  
     273   done:
     274    if (mt) gl_lock_unlock (at_fatal_signal_lock);
     275  
     276    return ret;
     277  }
     278  
     279  
     280  /* ========================================================================= */
     281  
     282  
     283  static sigset_t fatal_signal_set;
     284  
     285  static void
     286  do_init_fatal_signal_set (void)
     287  {
     288    size_t i;
     289  
     290    init_fatal_signals ();
     291  
     292    sigemptyset (&fatal_signal_set);
     293    for (i = 0; i < num_fatal_signals; i++)
     294      if (fatal_signals[i] >= 0)
     295        sigaddset (&fatal_signal_set, fatal_signals[i]);
     296  }
     297  
     298  /* Ensure that do_init_fatal_signal_set is called once only.  */
     299  gl_once_define(static, fatal_signal_set_once)
     300  
     301  static void
     302  init_fatal_signal_set (void)
     303  {
     304    gl_once (fatal_signal_set_once, do_init_fatal_signal_set);
     305  }
     306  
     307  /* Lock and counter that allow block_fatal_signals/unblock_fatal_signals pairs
     308     to occur in different threads and even overlap in time.  */
     309  gl_lock_define_initialized (static, fatal_signals_block_lock)
     310  static unsigned int fatal_signals_block_counter = 0;
     311  
     312  /* Temporarily delay the catchable fatal signals.  */
     313  void
     314  block_fatal_signals (void)
     315  {
     316    bool mt = gl_multithreaded ();
     317  
     318    if (mt) gl_lock_lock (fatal_signals_block_lock);
     319  
     320    if (fatal_signals_block_counter++ == 0)
     321      {
     322        init_fatal_signal_set ();
     323        sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
     324      }
     325  
     326    if (mt) gl_lock_unlock (fatal_signals_block_lock);
     327  }
     328  
     329  /* Stop delaying the catchable fatal signals.  */
     330  void
     331  unblock_fatal_signals (void)
     332  {
     333    bool mt = gl_multithreaded ();
     334  
     335    if (mt) gl_lock_lock (fatal_signals_block_lock);
     336  
     337    if (fatal_signals_block_counter == 0)
     338      /* There are more calls to unblock_fatal_signals() than to
     339         block_fatal_signals().  */
     340      abort ();
     341    if (--fatal_signals_block_counter == 0)
     342      {
     343        init_fatal_signal_set ();
     344        sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
     345      }
     346  
     347    if (mt) gl_lock_unlock (fatal_signals_block_lock);
     348  }
     349  
     350  
     351  unsigned int
     352  get_fatal_signals (int signals[64])
     353  {
     354    init_fatal_signal_set ();
     355  
     356    {
     357      int *p = signals;
     358      size_t i;
     359  
     360      for (i = 0; i < num_fatal_signals; i++)
     361        if (fatal_signals[i] >= 0)
     362          *p++ = fatal_signals[i];
     363      return p - signals;
     364    }
     365  }
     366  
     367  const sigset_t *
     368  get_fatal_signal_set (void)
     369  {
     370    init_fatal_signal_set ();
     371    return &fatal_signal_set;
     372  }