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