1/* Stack overflow handling.
2
3   Copyright (C) 2002 Free Software Foundation, Inc.
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 2, or (at your option)
8   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, write to the Free Software Foundation,
17   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19/* Written by Paul Eggert.  */
20
21/* This module assumes that each stack frame is smaller than a page.
22   If you use alloca, dynamic arrays, or large local variables, your
23   program may extend the stack by more than a page at a time.  If so,
24   the code below may incorrectly report a program error, or worse
25   yet, may not detect the overflow at all.  To avoid this problem,
26   don't use large local arrays.  */
27
28#if HAVE_CONFIG_H
29# include <config.h>
30#endif
31
32#include "gettext.h"
33#define _(msgid) gettext (msgid)
34
35#include <errno.h>
36#ifndef ENOTSUP
37# define ENOTSUP EINVAL
38#endif
39
40#if HAVE_INTTYPES_H
41# include <inttypes.h>
42#else
43# if HAVE_STDINT_H
44#  include <stdint.h>
45# endif
46#endif
47
48#include <signal.h>
49#include <stdlib.h>
50#include <string.h>
51
52#if HAVE_UNISTD_H
53# include <unistd.h>
54#endif
55#ifndef STDERR_FILENO
56# define STDERR_FILENO 2
57#endif
58
59#include "c-stack.h"
60#include "exitfail.h"
61
62extern char *program_name;
63
64#if HAVE_XSI_STACK_OVERFLOW_HEURISTIC
65
66# include <ucontext.h>
67
68
69/* Storage for the alternate signal stack.  */
70static union
71{
72  char buffer[SIGSTKSZ];
73
74  /* These other members are for proper alignment.  There's no
75     standard way to guarantee stack alignment, but this seems enough
76     in practice.  */
77  long double ld;
78  uintmax_t u;
79  void *p;
80} alternate_signal_stack;
81
82
83/* Direction of the C runtime stack.  This function is
84   async-signal-safe.  */
85
86# if STACK_DIRECTION
87#  define find_stack_direction(ptr) STACK_DIRECTION
88# else
89static int
90find_stack_direction (char const *addr)
91{
92  char dummy;
93  return ! addr ? find_stack_direction (&dummy) : addr < &dummy ? 1 : -1;
94}
95# endif
96
97/* The SIGSEGV handler.  */
98static void (* volatile segv_action) (int, siginfo_t *, void *);
99
100/* Handle a segmentation violation and exit.  This function is
101   async-signal-safe.  */
102
103static void
104segv_handler (int signo, siginfo_t *info, void *context)
105{
106  /* Clear SIGNO if it seems to have been a stack overflow.  */
107  if (0 < info->si_code)
108    {
109      /* If the faulting address is within the stack, or within one
110	 page of the stack end, assume that it is a stack
111	 overflow.  */
112      ucontext_t const *user_context = context;
113      char const *stack_min = user_context->uc_stack.ss_sp;
114      size_t stack_size = user_context->uc_stack.ss_size;
115      char const *faulting_address = info->si_addr;
116      size_t s = faulting_address - stack_min;
117      size_t page_size = sysconf (_SC_PAGESIZE);
118      if (find_stack_direction (0) < 0)
119	s += page_size;
120      if (s < stack_size + page_size)
121	signo = 0;
122    }
123
124  segv_action (signo, info, context);
125}
126
127#endif /* HAVE_XSI_STACK_OVERFLOW_HEURISTIC */
128
129
130/* Translated messages for program errors and stack overflow.  Do not
131   translate them in the signal handler, since gettext is not
132   async-signal-safe.  */
133static char const * volatile program_error_message;
134static char const * volatile stack_overflow_message;
135
136/* Output an error message, then exit with status EXIT_FAILURE if it
137   appears to have been a stack overflow, or with a core dump
138   otherwise.  This function is async-signal-safe.  */
139
140void
141c_stack_die (int signo, siginfo_t *info, void *context)
142{
143  char const *message =
144    signo ? program_error_message : stack_overflow_message;
145  write (STDERR_FILENO, program_name, strlen (program_name));
146  write (STDERR_FILENO, ": ", 2);
147  write (STDERR_FILENO, message, strlen (message));
148  write (STDERR_FILENO, "\n", 1);
149  if (! signo)
150    _exit (exit_failure);
151#if HAVE_SIGINFO_T
152  if (context && info && 0 <= info->si_code)
153    {
154      /* Re-raise the exception at the same address.  */
155      char *addr = info->si_addr;
156      *addr = 0;
157    }
158#endif
159  kill (getpid (), signo);
160}
161
162
163/* Set up ACTION so that it is invoked on C stack overflow.  Return -1
164   (setting errno) if this cannot be done.
165
166   ACTION must invoke only async-signal-safe functions.  ACTION
167   together with its callees must not require more than SIGSTKSZ bytes
168   of stack space.  */
169
170int
171c_stack_action (void (*action) (int, siginfo_t *, void *))
172{
173#if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
174  errno = ENOTSUP;
175  return -1;
176#else
177  struct sigaction act;
178  stack_t st;
179  int r;
180
181  st.ss_flags = 0;
182  st.ss_sp = alternate_signal_stack.buffer;
183  st.ss_size = sizeof alternate_signal_stack.buffer;
184  r = sigaltstack (&st, 0);
185  if (r != 0)
186    return r;
187
188  program_error_message = _("program error");
189  stack_overflow_message = _("stack overflow");
190  segv_action = action;
191
192  sigemptyset (&act.sa_mask);
193
194  /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but this
195     is not true on Solaris 8 at least.  It doesn't hurt to use
196     SA_NODEFER here, so leave it in.  */
197  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
198
199  act.sa_sigaction = segv_handler;
200  return sigaction (SIGSEGV, &act, 0);
201#endif
202}
203
204#if 0
205
206#include <stdio.h>
207
208int volatile exit_failure;
209
210static long
211recurse (char *p)
212{
213  char array[500];
214  array[0] = 1;
215  return *p + recurse (array);
216}
217
218char *program_name;
219
220int
221main (int argc, char **argv)
222{
223  program_name = argv[0];
224  c_stack_action (c_stack_die);
225  return recurse ("\1");
226}
227
228#endif
229
230/*
231Local Variables:
232compile-command: "gcc -D_GNU_SOURCE -DDEBUG \
233  -DHAVE_INTTYPES_H -DHAVE_SIGINFO_T \
234  -DHAVE_XSI_STACK_OVERFLOW_HEURISTIC -DHAVE_UNISTD_H \
235  -Wall -W -g c-stack.c -o c-stack"
236End:
237*/
238