Blame lib/c-stack.c

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