xref: /freebsd/contrib/diff/lib/c-stack.c (revision 18fd37a7)
118fd37a7SXin LI /* Stack overflow handling.
218fd37a7SXin LI 
318fd37a7SXin LI    Copyright (C) 2002, 2004 Free Software Foundation, Inc.
418fd37a7SXin LI 
518fd37a7SXin LI    This program is free software; you can redistribute it and/or modify
618fd37a7SXin LI    it under the terms of the GNU General Public License as published by
718fd37a7SXin LI    the Free Software Foundation; either version 2, or (at your option)
818fd37a7SXin LI    any later version.
918fd37a7SXin LI 
1018fd37a7SXin LI    This program is distributed in the hope that it will be useful,
1118fd37a7SXin LI    but WITHOUT ANY WARRANTY; without even the implied warranty of
1218fd37a7SXin LI    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1318fd37a7SXin LI    GNU General Public License for more details.
1418fd37a7SXin LI 
1518fd37a7SXin LI    You should have received a copy of the GNU General Public License
1618fd37a7SXin LI    along with this program; if not, write to the Free Software Foundation,
1718fd37a7SXin LI    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
1818fd37a7SXin LI 
1918fd37a7SXin LI /* Written by Paul Eggert.  */
2018fd37a7SXin LI 
2118fd37a7SXin LI /* NOTES:
2218fd37a7SXin LI 
2318fd37a7SXin LI    A program that uses alloca, dynamic arrays, or large local
2418fd37a7SXin LI    variables may extend the stack by more than a page at a time.  If
2518fd37a7SXin LI    so, when the stack overflows the operating system may not detect
2618fd37a7SXin LI    the overflow until the program uses the array, and this module may
2718fd37a7SXin LI    incorrectly report a program error instead of a stack overflow.
2818fd37a7SXin LI 
2918fd37a7SXin LI    To avoid this problem, allocate only small objects on the stack; a
3018fd37a7SXin LI    program should be OK if it limits single allocations to a page or
3118fd37a7SXin LI    less.  Allocate larger arrays in static storage, or on the heap
3218fd37a7SXin LI    (e.g., with malloc).  Yes, this is a pain, but we don't know of any
3318fd37a7SXin LI    better solution that is portable.
3418fd37a7SXin LI 
3518fd37a7SXin LI    No attempt has been made to deal with multithreaded applications.  */
3618fd37a7SXin LI 
3718fd37a7SXin LI #if HAVE_CONFIG_H
3818fd37a7SXin LI # include <config.h>
3918fd37a7SXin LI #endif
4018fd37a7SXin LI 
4118fd37a7SXin LI #ifndef __attribute__
4218fd37a7SXin LI # if __GNUC__ < 3 || __STRICT_ANSI__
4318fd37a7SXin LI #  define __attribute__(x)
4418fd37a7SXin LI # endif
4518fd37a7SXin LI #endif
4618fd37a7SXin LI 
4718fd37a7SXin LI #include "gettext.h"
4818fd37a7SXin LI #define _(msgid) gettext (msgid)
4918fd37a7SXin LI 
5018fd37a7SXin LI #include <errno.h>
5118fd37a7SXin LI #ifndef ENOTSUP
5218fd37a7SXin LI # define ENOTSUP EINVAL
5318fd37a7SXin LI #endif
5418fd37a7SXin LI #ifndef EOVERFLOW
5518fd37a7SXin LI # define EOVERFLOW EINVAL
5618fd37a7SXin LI #endif
5718fd37a7SXin LI 
5818fd37a7SXin LI #include <signal.h>
5918fd37a7SXin LI #if ! HAVE_STACK_T && ! defined stack_t
6018fd37a7SXin LI typedef struct sigaltstack stack_t;
6118fd37a7SXin LI #endif
6218fd37a7SXin LI 
6318fd37a7SXin LI #include <stdlib.h>
6418fd37a7SXin LI #include <string.h>
6518fd37a7SXin LI 
6618fd37a7SXin LI #if HAVE_SYS_RESOURCE_H
6718fd37a7SXin LI /* Include sys/time.h here, because...
6818fd37a7SXin LI    SunOS-4.1.x <sys/resource.h> fails to include <sys/time.h>.
6918fd37a7SXin LI    This gives "incomplete type" errors for ru_utime and tu_stime.  */
7018fd37a7SXin LI # if HAVE_SYS_TIME_H
7118fd37a7SXin LI #  include <sys/time.h>
7218fd37a7SXin LI # endif
7318fd37a7SXin LI # include <sys/resource.h>
7418fd37a7SXin LI #endif
7518fd37a7SXin LI 
7618fd37a7SXin LI #if HAVE_UCONTEXT_H
7718fd37a7SXin LI # include <ucontext.h>
7818fd37a7SXin LI #endif
7918fd37a7SXin LI 
8018fd37a7SXin LI #if HAVE_UNISTD_H
8118fd37a7SXin LI # include <unistd.h>
8218fd37a7SXin LI #endif
8318fd37a7SXin LI #ifndef STDERR_FILENO
8418fd37a7SXin LI # define STDERR_FILENO 2
8518fd37a7SXin LI #endif
8618fd37a7SXin LI 
8718fd37a7SXin LI #if DEBUG
8818fd37a7SXin LI # include <stdio.h>
8918fd37a7SXin LI #endif
9018fd37a7SXin LI 
9118fd37a7SXin LI #include "c-stack.h"
9218fd37a7SXin LI #include "exitfail.h"
9318fd37a7SXin LI 
9418fd37a7SXin LI #if (HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined SA_NODEFER \
9518fd37a7SXin LI      && defined SA_ONSTACK && defined SA_RESETHAND && defined SA_SIGINFO)
9618fd37a7SXin LI # define SIGACTION_WORKS 1
9718fd37a7SXin LI #else
9818fd37a7SXin LI # define SIGACTION_WORKS 0
9918fd37a7SXin LI #endif
10018fd37a7SXin LI 
10118fd37a7SXin LI extern char *program_name;
10218fd37a7SXin LI 
10318fd37a7SXin LI /* The user-specified action to take when a SEGV-related program error
10418fd37a7SXin LI    or stack overflow occurs.  */
10518fd37a7SXin LI static void (* volatile segv_action) (int);
10618fd37a7SXin LI 
10718fd37a7SXin LI /* Translated messages for program errors and stack overflow.  Do not
10818fd37a7SXin LI    translate them in the signal handler, since gettext is not
10918fd37a7SXin LI    async-signal-safe.  */
11018fd37a7SXin LI static char const * volatile program_error_message;
11118fd37a7SXin LI static char const * volatile stack_overflow_message;
11218fd37a7SXin LI 
11318fd37a7SXin LI /* Output an error message, then exit with status EXIT_FAILURE if it
11418fd37a7SXin LI    appears to have been a stack overflow, or with a core dump
11518fd37a7SXin LI    otherwise.  This function is async-signal-safe.  */
11618fd37a7SXin LI 
11718fd37a7SXin LI static void die (int) __attribute__ ((noreturn));
11818fd37a7SXin LI static void
die(int signo)11918fd37a7SXin LI die (int signo)
12018fd37a7SXin LI {
12118fd37a7SXin LI   char const *message;
12218fd37a7SXin LI   segv_action (signo);
12318fd37a7SXin LI   message = signo ? program_error_message : stack_overflow_message;
12418fd37a7SXin LI   write (STDERR_FILENO, program_name, strlen (program_name));
12518fd37a7SXin LI   write (STDERR_FILENO, ": ", 2);
12618fd37a7SXin LI   write (STDERR_FILENO, message, strlen (message));
12718fd37a7SXin LI   write (STDERR_FILENO, "\n", 1);
12818fd37a7SXin LI   if (! signo)
12918fd37a7SXin LI     _exit (exit_failure);
13018fd37a7SXin LI   kill (getpid (), signo);
13118fd37a7SXin LI   abort ();
13218fd37a7SXin LI }
13318fd37a7SXin LI 
13418fd37a7SXin LI #if HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
13518fd37a7SXin LI 
13618fd37a7SXin LI /* Direction of the C runtime stack.  This function is
13718fd37a7SXin LI    async-signal-safe.  */
13818fd37a7SXin LI 
13918fd37a7SXin LI # if STACK_DIRECTION
14018fd37a7SXin LI #  define find_stack_direction(ptr) STACK_DIRECTION
14118fd37a7SXin LI # else
14218fd37a7SXin LI static int
find_stack_direction(char const * addr)14318fd37a7SXin LI find_stack_direction (char const *addr)
14418fd37a7SXin LI {
14518fd37a7SXin LI   char dummy;
14618fd37a7SXin LI   return ! addr ? find_stack_direction (&dummy) : addr < &dummy ? 1 : -1;
14718fd37a7SXin LI }
14818fd37a7SXin LI # endif
14918fd37a7SXin LI 
15018fd37a7SXin LI /* Storage for the alternate signal stack.  */
15118fd37a7SXin LI static union
15218fd37a7SXin LI {
15318fd37a7SXin LI   char buffer[SIGSTKSZ];
15418fd37a7SXin LI 
15518fd37a7SXin LI   /* These other members are for proper alignment.  There's no
15618fd37a7SXin LI      standard way to guarantee stack alignment, but this seems enough
15718fd37a7SXin LI      in practice.  */
15818fd37a7SXin LI   long double ld;
15918fd37a7SXin LI   long l;
16018fd37a7SXin LI   void *p;
16118fd37a7SXin LI } alternate_signal_stack;
16218fd37a7SXin LI 
16318fd37a7SXin LI # if SIGACTION_WORKS
16418fd37a7SXin LI 
16518fd37a7SXin LI /* Handle a segmentation violation and exit.  This function is
16618fd37a7SXin LI    async-signal-safe.  */
16718fd37a7SXin LI 
16818fd37a7SXin LI static void segv_handler (int, siginfo_t *, void *) __attribute__((noreturn));
16918fd37a7SXin LI static void
segv_handler(int signo,siginfo_t * info,void * context)17018fd37a7SXin LI segv_handler (int signo, siginfo_t *info,
17118fd37a7SXin LI 	      void *context __attribute__ ((unused)))
17218fd37a7SXin LI {
17318fd37a7SXin LI   /* Clear SIGNO if it seems to have been a stack overflow.  */
17418fd37a7SXin LI   if (0 < info->si_code)
17518fd37a7SXin LI     {
17618fd37a7SXin LI #  if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
17718fd37a7SXin LI       /* We can't easily determine whether it is a stack overflow; so
17818fd37a7SXin LI 	 assume that the rest of our program is perfect (!) and that
17918fd37a7SXin LI 	 this segmentation violation is a stack overflow.  */
18018fd37a7SXin LI       signo = 0;
18118fd37a7SXin LI #  else
18218fd37a7SXin LI       /* If the faulting address is within the stack, or within one
18318fd37a7SXin LI 	 page of the stack end, assume that it is a stack
18418fd37a7SXin LI 	 overflow.  */
18518fd37a7SXin LI       ucontext_t const *user_context = context;
18618fd37a7SXin LI       char const *stack_base = user_context->uc_stack.ss_sp;
18718fd37a7SXin LI       size_t stack_size = user_context->uc_stack.ss_size;
18818fd37a7SXin LI       char const *faulting_address = info->si_addr;
18918fd37a7SXin LI       size_t s = faulting_address - stack_base;
19018fd37a7SXin LI       size_t page_size = sysconf (_SC_PAGESIZE);
19118fd37a7SXin LI       if (find_stack_direction (0) < 0)
19218fd37a7SXin LI 	s += page_size;
19318fd37a7SXin LI       if (s < stack_size + page_size)
19418fd37a7SXin LI 	signo = 0;
19518fd37a7SXin LI 
19618fd37a7SXin LI #   if DEBUG
19718fd37a7SXin LI       {
19818fd37a7SXin LI 	char buf[1024];
19918fd37a7SXin LI 	sprintf (buf,
20018fd37a7SXin LI 		 "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
20118fd37a7SXin LI 		 faulting_address, stack_base, (unsigned long) stack_size,
20218fd37a7SXin LI 		 (unsigned long) page_size, signo);
20318fd37a7SXin LI 	write (STDERR_FILENO, buf, strlen (buf));
20418fd37a7SXin LI       }
20518fd37a7SXin LI #   endif
20618fd37a7SXin LI #  endif
20718fd37a7SXin LI     }
20818fd37a7SXin LI 
20918fd37a7SXin LI   die (signo);
21018fd37a7SXin LI }
21118fd37a7SXin LI # endif
21218fd37a7SXin LI 
21318fd37a7SXin LI static void
null_action(int signo)21418fd37a7SXin LI null_action (int signo __attribute__ ((unused)))
21518fd37a7SXin LI {
21618fd37a7SXin LI }
21718fd37a7SXin LI 
21818fd37a7SXin LI /* Set up ACTION so that it is invoked on C stack overflow.  Return -1
21918fd37a7SXin LI    (setting errno) if this cannot be done.
22018fd37a7SXin LI 
22118fd37a7SXin LI    When ACTION is called, it is passed an argument equal to SIGSEGV
22218fd37a7SXin LI    for a segmentation violation that does not appear related to stack
22318fd37a7SXin LI    overflow, and is passed zero otherwise.  On many platforms it is
22418fd37a7SXin LI    hard to tell; when in doubt, zero is passed.
22518fd37a7SXin LI 
22618fd37a7SXin LI    A null ACTION acts like an action that does nothing.
22718fd37a7SXin LI 
22818fd37a7SXin LI    ACTION must be async-signal-safe.  ACTION together with its callees
22918fd37a7SXin LI    must not require more than SIGSTKSZ bytes of stack space.  */
23018fd37a7SXin LI 
23118fd37a7SXin LI int
c_stack_action(void (* action)(int))23218fd37a7SXin LI c_stack_action (void (*action) (int))
23318fd37a7SXin LI {
23418fd37a7SXin LI   int r;
23518fd37a7SXin LI   stack_t st;
23618fd37a7SXin LI   st.ss_flags = 0;
23718fd37a7SXin LI   st.ss_sp = alternate_signal_stack.buffer;
23818fd37a7SXin LI   st.ss_size = sizeof alternate_signal_stack.buffer;
23918fd37a7SXin LI   r = sigaltstack (&st, 0);
24018fd37a7SXin LI   if (r != 0)
24118fd37a7SXin LI     return r;
24218fd37a7SXin LI 
24318fd37a7SXin LI   segv_action = action ? action : null_action;
24418fd37a7SXin LI   program_error_message = _("program error");
24518fd37a7SXin LI   stack_overflow_message = _("stack overflow");
24618fd37a7SXin LI 
24718fd37a7SXin LI   {
24818fd37a7SXin LI # if SIGACTION_WORKS
24918fd37a7SXin LI     struct sigaction act;
25018fd37a7SXin LI     sigemptyset (&act.sa_mask);
25118fd37a7SXin LI 
25218fd37a7SXin LI     /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
25318fd37a7SXin LI        this is not true on Solaris 8 at least.  It doesn't hurt to use
25418fd37a7SXin LI        SA_NODEFER here, so leave it in.  */
25518fd37a7SXin LI     act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
25618fd37a7SXin LI 
25718fd37a7SXin LI     act.sa_sigaction = segv_handler;
25818fd37a7SXin LI 
25918fd37a7SXin LI     return sigaction (SIGSEGV, &act, 0);
26018fd37a7SXin LI # else
26118fd37a7SXin LI     return signal (SIGSEGV, die) == SIG_ERR ? -1 : 0;
26218fd37a7SXin LI # endif
26318fd37a7SXin LI   }
26418fd37a7SXin LI }
26518fd37a7SXin LI 
26618fd37a7SXin LI #else /* ! (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK) */
26718fd37a7SXin LI 
26818fd37a7SXin LI int
c_stack_action(void (* action)(int))26918fd37a7SXin LI c_stack_action (void (*action) (int)  __attribute__ ((unused)))
27018fd37a7SXin LI {
27118fd37a7SXin LI   errno = ENOTSUP;
27218fd37a7SXin LI   return -1;
27318fd37a7SXin LI }
27418fd37a7SXin LI 
27518fd37a7SXin LI #endif
27618fd37a7SXin LI 
27718fd37a7SXin LI 
27818fd37a7SXin LI 
27918fd37a7SXin LI #if DEBUG
28018fd37a7SXin LI 
28118fd37a7SXin LI int volatile exit_failure;
28218fd37a7SXin LI 
28318fd37a7SXin LI static long
recurse(char * p)28418fd37a7SXin LI recurse (char *p)
28518fd37a7SXin LI {
28618fd37a7SXin LI   char array[500];
28718fd37a7SXin LI   array[0] = 1;
28818fd37a7SXin LI   return *p + recurse (array);
28918fd37a7SXin LI }
29018fd37a7SXin LI 
29118fd37a7SXin LI char *program_name;
29218fd37a7SXin LI 
29318fd37a7SXin LI int
main(int argc,char ** argv)29418fd37a7SXin LI main (int argc __attribute__ ((unused)), char **argv)
29518fd37a7SXin LI {
29618fd37a7SXin LI   program_name = argv[0];
29718fd37a7SXin LI   fprintf (stderr,
29818fd37a7SXin LI 	   "The last output line should contain \"stack overflow\".\n");
29918fd37a7SXin LI   if (c_stack_action (0) == 0)
30018fd37a7SXin LI     return recurse ("\1");
30118fd37a7SXin LI   perror ("c_stack_action");
30218fd37a7SXin LI   return 1;
30318fd37a7SXin LI }
30418fd37a7SXin LI 
30518fd37a7SXin LI #endif /* DEBUG */
30618fd37a7SXin LI 
30718fd37a7SXin LI /*
30818fd37a7SXin LI Local Variables:
30918fd37a7SXin LI compile-command: "gcc -DDEBUG -DHAVE_CONFIG_H -I.. -g -O -Wall -W c-stack.c"
31018fd37a7SXin LI End:
31118fd37a7SXin LI */
312