/* Stack overflow handling.
- Copyright (C) 2002 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2004, 2006, 2008-2014 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert. */
(e.g., with malloc). Yes, this is a pain, but we don't know of any
better solution that is portable.
- No attempt has been made to deal with multithreaded applications.
+ No attempt has been made to deal with multithreaded applications. */
- If ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC, the current implementation
- assumes that, if the RLIMIT_STACK limit changes during execution,
- then c_stack_action is invoked immediately afterwards. */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
#ifndef __attribute__
-# if __GNUC__ < 3 || __STRICT_ANSI__
+# if __GNUC__ < 3
# define __attribute__(x)
# endif
#endif
#define _(msgid) gettext (msgid)
#include <errno.h>
-#ifndef ENOTSUP
-# define ENOTSUP EINVAL
-#endif
-#ifndef EOVERFLOW
-# define EOVERFLOW EINVAL
-#endif
#include <signal.h>
#if ! HAVE_STACK_T && ! defined stack_t
typedef struct sigaltstack stack_t;
#endif
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 16384
+#elif HAVE_LIBSIGSEGV && SIGSTKSZ < 16384
+/* libsigsegv 2.6 through 2.8 have a bug where some architectures use
+ more than the Linux default of an 8k alternate stack when deciding
+ if a fault was caused by stack overflow. */
+# undef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
#include <stdlib.h>
#include <string.h>
-#if HAVE_SYS_RESOURCE_H
-# include <sys/resource.h>
-#endif
-
+/* Posix 2001 declares ucontext_t in <ucontext.h>, Posix 200x in
+ <signal.h>. */
#if HAVE_UCONTEXT_H
# include <ucontext.h>
#endif
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifndef STDERR_FILENO
-# define STDERR_FILENO 2
-#endif
+#include <unistd.h>
-#if DEBUG
-# include <stdio.h>
+#if HAVE_LIBSIGSEGV
+# include <sigsegv.h>
#endif
#include "c-stack.h"
#include "exitfail.h"
+#include "ignore-value.h"
+
+#if defined SA_ONSTACK && defined SA_SIGINFO
+# define SIGINFO_WORKS 1
+#else
+# define SIGINFO_WORKS 0
+# ifndef SA_ONSTACK
+# define SA_ONSTACK 0
+# endif
+#endif
extern char *program_name;
appears to have been a stack overflow, or with a core dump
otherwise. This function is async-signal-safe. */
-static void die (int) __attribute__ ((noreturn));
-static void
+static _Noreturn void
die (int signo)
{
- char const *message =
- signo ? program_error_message : stack_overflow_message;
+ char const *message;
+#if !SIGINFO_WORKS && !HAVE_LIBSIGSEGV
+ /* We can't easily determine whether it is a stack overflow; so
+ assume that the rest of our program is perfect (!) and that
+ this segmentation violation is a stack overflow. */
+ signo = 0;
+#endif /* !SIGINFO_WORKS && !HAVE_LIBSIGSEGV */
segv_action (signo);
- write (STDERR_FILENO, program_name, strlen (program_name));
- write (STDERR_FILENO, ": ", 2);
- write (STDERR_FILENO, message, strlen (message));
- write (STDERR_FILENO, "\n", 1);
+ message = signo ? program_error_message : stack_overflow_message;
+ ignore_value (write (STDERR_FILENO, program_name, strlen (program_name)));
+ ignore_value (write (STDERR_FILENO, ": ", 2));
+ ignore_value (write (STDERR_FILENO, message, strlen (message)));
+ ignore_value (write (STDERR_FILENO, "\n", 1));
if (! signo)
_exit (exit_failure);
- kill (getpid (), signo);
+ raise (signo);
abort ();
}
-#if HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
+#if (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \
+ && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV
-/* Direction of the C runtime stack. This function is
- async-signal-safe. */
+/* Storage for the alternate signal stack. */
+static union
+{
+ char buffer[SIGSTKSZ];
-# if STACK_DIRECTION
-# define find_stack_direction(ptr) STACK_DIRECTION
-# else
-static int
-find_stack_direction (char const *addr)
+ /* These other members are for proper alignment. There's no
+ standard way to guarantee stack alignment, but this seems enough
+ in practice. */
+ long double ld;
+ long l;
+ void *p;
+} alternate_signal_stack;
+
+static void
+null_action (int signo __attribute__ ((unused)))
{
- char dummy;
- return ! addr ? find_stack_direction (&dummy) : addr < &dummy ? 1 : -1;
}
-# endif
-# if HAVE_XSI_STACK_OVERFLOW_HEURISTIC
-# define get_stack_location(argv) 0
-# else
-
-# if defined RLIMIT_STACK && defined _SC_PAGESIZE
+#endif /* SIGALTSTACK || LIBSIGSEGV */
-/* Return the minimum machine address deducible from ARGV. This
- includes the addresses of all the strings that ARGV points at, as
- well as the address of ARGV itself. */
+/* Only use libsigsegv if we need it; platforms like Solaris can
+ detect stack overflow without the overhead of an external
+ library. */
+#if HAVE_LIBSIGSEGV && ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
-static char const *
-min_address_from_argv (char * const *argv)
-{
- char const *min = (char const *) argv;
- char const *p;
- while ((p = *argv++))
- if (p < min)
- min = p;
- return min;
-}
+/* Nonzero if general segv handler could not be installed. */
+static volatile int segv_handler_missing;
-/* Return the maximum machine address deducible from ARGV. */
+/* Handle a segmentation violation and exit if it cannot be stack
+ overflow. This function is async-signal-safe. */
-static char const *
-max_address_from_argv (char * const *argv)
+static int segv_handler (void *address __attribute__ ((unused)),
+ int serious)
{
- char const *max = *argv;
- char const *max1;
- char const *p;
- while ((p = *argv++))
- if (max < p)
- max = p;
- max1 = (char const *) (argv + 1);
- return max && max1 < max ? max + strlen (max) + 1 : max1;
-}
-# endif
+# if DEBUG
+ {
+ char buf[1024];
+ sprintf (buf, "segv_handler serious=%d\n", serious);
+ write (STDERR_FILENO, buf, strlen (buf));
+ }
+# endif
-/* The base and size of the stack, determined at startup. */
-static char const * volatile stack_base;
-static size_t volatile stack_size;
+ /* If this fault is not serious, return 0 to let the stack overflow
+ handler take a shot at it. */
+ if (!serious)
+ return 0;
+ die (SIGSEGV);
+}
-/* Store the base and size of the stack into the static variables
- STACK_BASE and STACK_SIZE. The base is the numerically lowest
- address in the stack. Return -1 (setting errno) if this cannot be
- done. */
+/* Handle a segmentation violation that is likely to be a stack
+ overflow and exit. This function is async-signal-safe. */
-static int
-get_stack_location (char * const *argv)
+static _Noreturn void
+overflow_handler (int emergency,
+ stackoverflow_context_t context __attribute__ ((unused)))
{
-# if ! (defined RLIMIT_STACK && defined _SC_PAGESIZE)
+# if DEBUG
+ {
+ char buf[1024];
+ sprintf (buf, "overflow_handler emergency=%d segv_handler_missing=%d\n",
+ emergency, segv_handler_missing);
+ write (STDERR_FILENO, buf, strlen (buf));
+ }
+# endif
- errno = ENOTSUP;
- return -1;
+ die ((!emergency || segv_handler_missing) ? 0 : SIGSEGV);
+}
-# else
+int
+c_stack_action (void (*action) (int))
+{
+ segv_action = action ? action : null_action;
+ program_error_message = _("program error");
+ stack_overflow_message = _("stack overflow");
- struct rlimit rlimit;
- int r = getrlimit (RLIMIT_STACK, &rlimit);
- if (r == 0)
+ /* Always install the overflow handler. */
+ if (stackoverflow_install_handler (overflow_handler,
+ alternate_signal_stack.buffer,
+ sizeof alternate_signal_stack.buffer))
{
- char const *base;
- size_t size = rlimit.rlim_cur;
- extern char **environ;
- size_t page_size = sysconf (_SC_PAGESIZE);
- int stack_direction = find_stack_direction (0);
-
-# if HAVE_GETCONTEXT && HAVE_DECL_GETCONTEXT
- ucontext_t context;
- if (getcontext (&context) == 0)
- {
- base = context.uc_stack.ss_sp;
- if (stack_direction < 0)
- base -= size - context.uc_stack.ss_size;
- }
- else
-# endif
- {
- if (stack_direction < 0)
- {
- char const *a = max_address_from_argv (argv);
- char const *b = max_address_from_argv (environ);
- base = (a < b ? b : a) - size;
- base += - (size_t) base % page_size;
- }
- else
- {
- char const *a = min_address_from_argv (argv);
- char const *b = min_address_from_argv (environ);
- base = a < b ? a : b;
- base -= (size_t) base % page_size;
- }
- }
-
- if (size != rlimit.rlim_cur
- || rlimit.rlim_cur < 0
- || base + size < base
-# ifdef RLIM_SAVED_CUR
- || rlimit.rlim_cur == RLIM_SAVED_CUR
-# endif
-# ifdef RLIM_SAVED_MAX
- || rlimit.rlim_cur == RLIM_SAVED_MAX
-# endif
-# ifdef RLIM_INFINITY
- || rlimit.rlim_cur == RLIM_INFINITY
-# endif
- )
- {
- errno = EOVERFLOW;
- return -1;
- }
-
- stack_base = base;
- stack_size = size;
-
-# if DEBUG
- fprintf (stderr, "get_stack_location base=%p size=%lx\n",
- base, (unsigned long) size);
-# endif
+ errno = ENOTSUP;
+ return -1;
}
-
- return r;
-
-# endif
+ /* Try installing a general handler; if it fails, then treat all
+ segv as stack overflow. */
+ segv_handler_missing = sigsegv_install_handler (segv_handler);
+ return 0;
}
-# endif
-/* Storage for the alternate signal stack. */
-static union
-{
- char buffer[SIGSTKSZ];
+#elif HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING
- /* These other members are for proper alignment. There's no
- standard way to guarantee stack alignment, but this seems enough
- in practice. */
- long double ld;
- long l;
- void *p;
-} alternate_signal_stack;
-
-# if defined SA_ONSTACK && defined _SC_PAGESIZE
+# if SIGINFO_WORKS
/* Handle a segmentation violation and exit. This function is
async-signal-safe. */
-static void segv_handler (int, siginfo_t *, void *) __attribute__((noreturn));
-static void
+static _Noreturn void
segv_handler (int signo, siginfo_t *info,
- void *context __attribute__ ((unused)))
+ void *context __attribute__ ((unused)))
{
/* Clear SIGNO if it seems to have been a stack overflow. */
+# if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
+ /* We can't easily determine whether it is a stack overflow; so
+ assume that the rest of our program is perfect (!) and that
+ this segmentation violation is a stack overflow.
+
+ Note that although both Linux and Solaris provide
+ sigaltstack, SA_ONSTACK, and SA_SIGINFO, currently only
+ Solaris satisfies the XSI heuristic. This is because
+ Solaris populates uc_stack with the details of the
+ interrupted stack, while Linux populates it with the details
+ of the current stack. */
+ signo = 0;
+# else
if (0 < info->si_code)
{
/* If the faulting address is within the stack, or within one
- page of the stack end, assume that it is a stack
- overflow. */
-# if HAVE_XSI_STACK_OVERFLOW_HEURISTIC
+ page of the stack, assume that it is a stack overflow. */
ucontext_t const *user_context = context;
char const *stack_base = user_context->uc_stack.ss_sp;
size_t stack_size = user_context->uc_stack.ss_size;
-# endif
char const *faulting_address = info->si_addr;
- size_t s = faulting_address - stack_base;
size_t page_size = sysconf (_SC_PAGESIZE);
- if (find_stack_direction (0) < 0)
- s += page_size;
- if (s < stack_size + page_size)
- signo = 0;
+ size_t s = faulting_address - stack_base + page_size;
+ if (s < stack_size + 2 * page_size)
+ signo = 0;
-# if DEBUG
+# if DEBUG
{
- char buf[1024];
- sprintf (buf,
- "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
- faulting_address, stack_base, (unsigned long) stack_size,
- (unsigned long) page_size, signo);
- write (STDERR_FILENO, buf, strlen (buf));
+ char buf[1024];
+ sprintf (buf,
+ "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
+ faulting_address, stack_base, (unsigned long) stack_size,
+ (unsigned long) page_size, signo);
+ write (STDERR_FILENO, buf, strlen (buf));
}
-# endif
+# endif
}
+# endif
die (signo);
}
# endif
-static void
-null_action (int signo __attribute__ ((unused)))
-{
-}
-
-/* Assuming ARGV is the argument vector of `main', set up ACTION so
- that it is invoked on C stack overflow. Return -1 (setting errno)
- if this cannot be done.
-
- When ACTION is called, it is passed an argument equal to SIGSEGV
- for a segmentation violation that does not appear related to stack
- overflow, and is passed zero otherwise.
-
- A null ACTION acts like an action that does nothing.
-
- ACTION must be async-signal-safe. ACTION together with its callees
- must not require more than SIGSTKSZ bytes of stack space. */
-
int
-c_stack_action (char * const *argv __attribute__ ((unused)),
- void (*action) (int))
+c_stack_action (void (*action) (int))
{
- int r = get_stack_location (argv);
+ int r;
+ stack_t st;
+ struct sigaction act;
+ st.ss_flags = 0;
+# if SIGALTSTACK_SS_REVERSED
+ /* Irix mistakenly treats ss_sp as the upper bound, rather than
+ lower bound, of the alternate stack. */
+ st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ - sizeof (void *);
+ st.ss_size = sizeof alternate_signal_stack.buffer - sizeof (void *);
+# else
+ st.ss_sp = alternate_signal_stack.buffer;
+ st.ss_size = sizeof alternate_signal_stack.buffer;
+# endif
+ r = sigaltstack (&st, NULL);
if (r != 0)
return r;
- {
- stack_t st;
- st.ss_flags = 0;
- st.ss_sp = alternate_signal_stack.buffer;
- st.ss_size = sizeof alternate_signal_stack.buffer;
- r = sigaltstack (&st, 0);
- if (r != 0)
- return r;
- }
-
segv_action = action ? action : null_action;
program_error_message = _("program error");
stack_overflow_message = _("stack overflow");
- {
-# if ! (defined SA_ONSTACK && defined _SC_PAGESIZE)
- return signal (SIGSEGV, die) == SIG_ERR ? -1 : 0;
-# else
- struct sigaction act;
- sigemptyset (&act.sa_mask);
-
- /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
- this is not true on Solaris 8 at least. It doesn't hurt to use
- SA_NODEFER here, so leave it in. */
- act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
+ sigemptyset (&act.sa_mask);
- act.sa_sigaction = segv_handler;
+# if SIGINFO_WORKS
+ /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
+ this is not true on Solaris 8 at least. It doesn't hurt to use
+ SA_NODEFER here, so leave it in. */
+ act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
+ act.sa_sigaction = segv_handler;
+# else
+ act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+ act.sa_handler = die;
+# endif
- return sigaction (SIGSEGV, &act, 0);
+# if FAULT_YIELDS_SIGBUS
+ if (sigaction (SIGBUS, &act, NULL) < 0)
+ return -1;
# endif
- }
+ return sigaction (SIGSEGV, &act, NULL);
}
-#else /* ! (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK) */
+#else /* ! ((HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
+ && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV) */
int
-c_stack_action (char * const *argv __attribute__ ((unused)),
- void (*action) (int) __attribute__ ((unused)))
+c_stack_action (void (*action) (int) __attribute__ ((unused)))
{
errno = ENOTSUP;
return -1;
}
#endif
-
-\f
-
-#if DEBUG
-
-int volatile exit_failure;
-
-static long
-recurse (char *p)
-{
- char array[500];
- array[0] = 1;
- return *p + recurse (array);
-}
-
-char *program_name;
-
-int
-main (int argc __attribute__ ((unused)), char **argv)
-{
- program_name = argv[0];
- fprintf (stderr, "The last line of output should be \"stack overflow\".\n");
- if (c_stack_action (argv, 0) == 0)
- return recurse ("\1");
- perror ("c_stack_action");
- return 1;
-}
-
-#endif /* DEBUG */
-\f
-/*
-Local Variables:
-compile-command: "gcc -DDEBUG -DHAVE_CONFIG_H -I.. -g -O -Wall -W c-stack.c"
-End:
-*/