Blame lib/c-stack.c

Packit 33f14e
/* Stack overflow handling.
Packit 33f14e
Packit 33f14e
   Copyright (C) 2002, 2004, 2006, 2008-2017 Free Software Foundation, Inc.
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
/* Written by Paul Eggert.  */
Packit 33f14e
Packit 33f14e
/* NOTES:
Packit 33f14e
Packit 33f14e
   A program that uses alloca, dynamic arrays, or large local
Packit 33f14e
   variables may extend the stack by more than a page at a time.  If
Packit 33f14e
   so, when the stack overflows the operating system may not detect
Packit 33f14e
   the overflow until the program uses the array, and this module may
Packit 33f14e
   incorrectly report a program error instead of a stack overflow.
Packit 33f14e
Packit 33f14e
   To avoid this problem, allocate only small objects on the stack; a
Packit 33f14e
   program should be OK if it limits single allocations to a page or
Packit 33f14e
   less.  Allocate larger arrays in static storage, or on the heap
Packit 33f14e
   (e.g., with malloc).  Yes, this is a pain, but we don't know of any
Packit 33f14e
   better solution that is portable.
Packit 33f14e
Packit 33f14e
   No attempt has been made to deal with multithreaded applications.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
#ifndef __attribute__
Packit 33f14e
# if __GNUC__ < 3
Packit 33f14e
#  define __attribute__(x)
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include "gettext.h"
Packit 33f14e
#define _(msgid) gettext (msgid)
Packit 33f14e
Packit 33f14e
#include <errno.h>
Packit 33f14e
Packit 33f14e
#include <signal.h>
Packit 33f14e
#if ! HAVE_STACK_T && ! defined stack_t
Packit 33f14e
typedef struct sigaltstack stack_t;
Packit 33f14e
#endif
Packit 33f14e
#ifndef SIGSTKSZ
Packit 33f14e
# define SIGSTKSZ 16384
Packit 33f14e
#elif HAVE_LIBSIGSEGV && SIGSTKSZ < 16384
Packit 33f14e
/* libsigsegv 2.6 through 2.8 have a bug where some architectures use
Packit 33f14e
   more than the Linux default of an 8k alternate stack when deciding
Packit 33f14e
   if a fault was caused by stack overflow.  */
Packit 33f14e
# undef SIGSTKSZ
Packit 33f14e
# define SIGSTKSZ 16384
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include <stdlib.h>
Packit 33f14e
#include <string.h>
Packit 33f14e
Packit 33f14e
/* Posix 2001 declares ucontext_t in <ucontext.h>, Posix 200x in
Packit 33f14e
   <signal.h>.  */
Packit 33f14e
#if HAVE_UCONTEXT_H
Packit 33f14e
# include <ucontext.h>
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include <unistd.h>
Packit 33f14e
Packit 33f14e
#if HAVE_LIBSIGSEGV
Packit 33f14e
# include <sigsegv.h>
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include "c-stack.h"
Packit 33f14e
#include "exitfail.h"
Packit 33f14e
#include "ignore-value.h"
Packit 33f14e
#include "getprogname.h"
Packit 33f14e
Packit 33f14e
#if defined SA_ONSTACK && defined SA_SIGINFO
Packit 33f14e
# define SIGINFO_WORKS 1
Packit 33f14e
#else
Packit 33f14e
# define SIGINFO_WORKS 0
Packit 33f14e
# ifndef SA_ONSTACK
Packit 33f14e
#  define SA_ONSTACK 0
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* The user-specified action to take when a SEGV-related program error
Packit 33f14e
   or stack overflow occurs.  */
Packit 33f14e
static void (* volatile segv_action) (int);
Packit 33f14e
Packit 33f14e
/* Translated messages for program errors and stack overflow.  Do not
Packit 33f14e
   translate them in the signal handler, since gettext is not
Packit 33f14e
   async-signal-safe.  */
Packit 33f14e
static char const * volatile program_error_message;
Packit 33f14e
static char const * volatile stack_overflow_message;
Packit 33f14e
Packit 33f14e
/* Output an error message, then exit with status EXIT_FAILURE if it
Packit 33f14e
   appears to have been a stack overflow, or with a core dump
Packit 33f14e
   otherwise.  This function is async-signal-safe.  */
Packit 33f14e
Packit 33f14e
static _Noreturn void
Packit 33f14e
die (int signo)
Packit 33f14e
{
Packit 33f14e
  char const *message;
Packit 33f14e
#if !SIGINFO_WORKS && !HAVE_LIBSIGSEGV
Packit 33f14e
  /* We can't easily determine whether it is a stack overflow; so
Packit 33f14e
     assume that the rest of our program is perfect (!) and that
Packit 33f14e
     this segmentation violation is a stack overflow.  */
Packit 33f14e
  signo = 0;
Packit 33f14e
#endif /* !SIGINFO_WORKS && !HAVE_LIBSIGSEGV */
Packit 33f14e
  segv_action (signo);
Packit 33f14e
  message = signo ? program_error_message : stack_overflow_message;
Packit 33f14e
  ignore_value (write (STDERR_FILENO, getprogname (), strlen (getprogname ())));
Packit 33f14e
  ignore_value (write (STDERR_FILENO, ": ", 2));
Packit 33f14e
  ignore_value (write (STDERR_FILENO, message, strlen (message)));
Packit 33f14e
  ignore_value (write (STDERR_FILENO, "\n", 1));
Packit 33f14e
  if (! signo)
Packit 33f14e
    _exit (exit_failure);
Packit 33f14e
  raise (signo);
Packit 33f14e
  abort ();
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#if (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \
Packit 33f14e
     && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV
Packit 33f14e
Packit 33f14e
/* Storage for the alternate signal stack.  */
Packit 33f14e
static union
Packit 33f14e
{
Packit 33f14e
  char buffer[SIGSTKSZ];
Packit 33f14e
Packit 33f14e
  /* These other members are for proper alignment.  There's no
Packit 33f14e
     standard way to guarantee stack alignment, but this seems enough
Packit 33f14e
     in practice.  */
Packit 33f14e
  long double ld;
Packit 33f14e
  long l;
Packit 33f14e
  void *p;
Packit 33f14e
} alternate_signal_stack;
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
null_action (int signo __attribute__ ((unused)))
Packit 33f14e
{
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#endif /* SIGALTSTACK || LIBSIGSEGV */
Packit 33f14e
Packit 33f14e
/* Only use libsigsegv if we need it; platforms like Solaris can
Packit 33f14e
   detect stack overflow without the overhead of an external
Packit 33f14e
   library.  */
Packit 33f14e
#if HAVE_LIBSIGSEGV && ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
Packit 33f14e
Packit 33f14e
/* Nonzero if general segv handler could not be installed.  */
Packit 33f14e
static volatile int segv_handler_missing;
Packit 33f14e
Packit 33f14e
/* Handle a segmentation violation and exit if it cannot be stack
Packit 33f14e
   overflow.  This function is async-signal-safe.  */
Packit 33f14e
Packit 33f14e
static int segv_handler (void *address __attribute__ ((unused)),
Packit 33f14e
                         int serious)
Packit 33f14e
{
Packit 33f14e
# if DEBUG
Packit 33f14e
  {
Packit 33f14e
    char buf[1024];
Packit 33f14e
    sprintf (buf, "segv_handler serious=%d\n", serious);
Packit 33f14e
    write (STDERR_FILENO, buf, strlen (buf));
Packit 33f14e
  }
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
  /* If this fault is not serious, return 0 to let the stack overflow
Packit 33f14e
     handler take a shot at it.  */
Packit 33f14e
  if (!serious)
Packit 33f14e
    return 0;
Packit 33f14e
  die (SIGSEGV);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Handle a segmentation violation that is likely to be a stack
Packit 33f14e
   overflow and exit.  This function is async-signal-safe.  */
Packit 33f14e
Packit 33f14e
static _Noreturn void
Packit 33f14e
overflow_handler (int emergency,
Packit 33f14e
                  stackoverflow_context_t context __attribute__ ((unused)))
Packit 33f14e
{
Packit 33f14e
# if DEBUG
Packit 33f14e
  {
Packit 33f14e
    char buf[1024];
Packit 33f14e
    sprintf (buf, "overflow_handler emergency=%d segv_handler_missing=%d\n",
Packit 33f14e
             emergency, segv_handler_missing);
Packit 33f14e
    write (STDERR_FILENO, buf, strlen (buf));
Packit 33f14e
  }
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
  die ((!emergency || segv_handler_missing) ? 0 : SIGSEGV);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
c_stack_action (void (*action) (int))
Packit 33f14e
{
Packit 33f14e
  segv_action = action ? action : null_action;
Packit 33f14e
  program_error_message = _("program error");
Packit 33f14e
  stack_overflow_message = _("stack overflow");
Packit 33f14e
Packit 33f14e
  /* Always install the overflow handler.  */
Packit 33f14e
  if (stackoverflow_install_handler (overflow_handler,
Packit 33f14e
                                     alternate_signal_stack.buffer,
Packit 33f14e
                                     sizeof alternate_signal_stack.buffer))
Packit 33f14e
    {
Packit 33f14e
      errno = ENOTSUP;
Packit 33f14e
      return -1;
Packit 33f14e
    }
Packit 33f14e
  /* Try installing a general handler; if it fails, then treat all
Packit 33f14e
     segv as stack overflow.  */
Packit 33f14e
  segv_handler_missing = sigsegv_install_handler (segv_handler);
Packit 33f14e
  return 0;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#elif HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING
Packit 33f14e
Packit 33f14e
# if SIGINFO_WORKS
Packit 33f14e
Packit 33f14e
/* Handle a segmentation violation and exit.  This function is
Packit 33f14e
   async-signal-safe.  */
Packit 33f14e
Packit 33f14e
static _Noreturn void
Packit 33f14e
segv_handler (int signo, siginfo_t *info,
Packit 33f14e
              void *context __attribute__ ((unused)))
Packit 33f14e
{
Packit 33f14e
  /* Clear SIGNO if it seems to have been a stack overflow.  */
Packit 33f14e
#  if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
Packit 33f14e
  /* We can't easily determine whether it is a stack overflow; so
Packit 33f14e
     assume that the rest of our program is perfect (!) and that
Packit 33f14e
     this segmentation violation is a stack overflow.
Packit 33f14e
Packit 33f14e
     Note that although both Linux and Solaris provide
Packit 33f14e
     sigaltstack, SA_ONSTACK, and SA_SIGINFO, currently only
Packit 33f14e
     Solaris satisfies the XSI heuristic.  This is because
Packit 33f14e
     Solaris populates uc_stack with the details of the
Packit 33f14e
     interrupted stack, while Linux populates it with the details
Packit 33f14e
     of the current stack.  */
Packit 33f14e
  signo = 0;
Packit 33f14e
#  else
Packit 33f14e
  if (0 < info->si_code)
Packit 33f14e
    {
Packit 33f14e
      /* If the faulting address is within the stack, or within one
Packit 33f14e
         page of the stack, assume that it is a stack overflow.  */
Packit 33f14e
      ucontext_t const *user_context = context;
Packit 33f14e
      char const *stack_base = user_context->uc_stack.ss_sp;
Packit 33f14e
      size_t stack_size = user_context->uc_stack.ss_size;
Packit 33f14e
      char const *faulting_address = info->si_addr;
Packit 33f14e
      size_t page_size = sysconf (_SC_PAGESIZE);
Packit 33f14e
      size_t s = faulting_address - stack_base + page_size;
Packit 33f14e
      if (s < stack_size + 2 * page_size)
Packit 33f14e
        signo = 0;
Packit 33f14e
Packit 33f14e
#   if DEBUG
Packit 33f14e
      {
Packit 33f14e
        char buf[1024];
Packit 33f14e
        sprintf (buf,
Packit 33f14e
                 "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
Packit 33f14e
                 faulting_address, stack_base, (unsigned long) stack_size,
Packit 33f14e
                 (unsigned long) page_size, signo);
Packit 33f14e
        write (STDERR_FILENO, buf, strlen (buf));
Packit 33f14e
      }
Packit 33f14e
#   endif
Packit 33f14e
    }
Packit 33f14e
#  endif
Packit 33f14e
Packit 33f14e
  die (signo);
Packit 33f14e
}
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
c_stack_action (void (*action) (int))
Packit 33f14e
{
Packit 33f14e
  int r;
Packit 33f14e
  stack_t st;
Packit 33f14e
  struct sigaction act;
Packit 33f14e
  st.ss_flags = 0;
Packit 33f14e
# if SIGALTSTACK_SS_REVERSED
Packit 33f14e
  /* Irix mistakenly treats ss_sp as the upper bound, rather than
Packit 33f14e
     lower bound, of the alternate stack.  */
Packit 33f14e
  st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ - sizeof (void *);
Packit 33f14e
  st.ss_size = sizeof alternate_signal_stack.buffer - sizeof (void *);
Packit 33f14e
# else
Packit 33f14e
  st.ss_sp = alternate_signal_stack.buffer;
Packit 33f14e
  st.ss_size = sizeof alternate_signal_stack.buffer;
Packit 33f14e
# endif
Packit 33f14e
  r = sigaltstack (&st, NULL);
Packit 33f14e
  if (r != 0)
Packit 33f14e
    return r;
Packit 33f14e
Packit 33f14e
  segv_action = action ? action : null_action;
Packit 33f14e
  program_error_message = _("program error");
Packit 33f14e
  stack_overflow_message = _("stack overflow");
Packit 33f14e
Packit 33f14e
  sigemptyset (&act.sa_mask);
Packit 33f14e
Packit 33f14e
# if SIGINFO_WORKS
Packit 33f14e
  /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
Packit 33f14e
     this is not true on Solaris 8 at least.  It doesn't hurt to use
Packit 33f14e
     SA_NODEFER here, so leave it in.  */
Packit 33f14e
  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
Packit 33f14e
  act.sa_sigaction = segv_handler;
Packit 33f14e
# else
Packit 33f14e
  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
Packit 33f14e
  act.sa_handler = die;
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
# if FAULT_YIELDS_SIGBUS
Packit 33f14e
  if (sigaction (SIGBUS, &act, NULL) < 0)
Packit 33f14e
    return -1;
Packit 33f14e
# endif
Packit 33f14e
  return sigaction (SIGSEGV, &act, NULL);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#else /* ! ((HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
Packit 33f14e
             && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV) */
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
c_stack_action (void (*action) (int)  __attribute__ ((unused)))
Packit 33f14e
{
Packit 33f14e
  errno = ENOTSUP;
Packit 33f14e
  return -1;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#endif