home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / m4-1.4-src.tgz / tar.out / fsf / m4 / src / stackovf.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  12KB  |  393 lines

  1. /* Detect stack overflow (when getrlimit and sigaction or sigvec are available)
  2.    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
  3.    Jim Avera <jima@netcom.com>, October 1993.
  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
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. /* Compiled only when USE_STACKOVF is defined, which itself requires
  21.    getrlimit with the RLIMIT_STACK option, and support for alternate
  22.    signal stacks using either SVR4 or BSD interfaces.
  23.    
  24.    This should compile on ANY system which supports either sigaltstack()
  25.    or sigstack(), with or without <siginfo.h> or another way to determine
  26.    the fault address.
  27.    
  28.    There is no completely portable way to determine if a SIGSEGV signal
  29.    indicates a stack overflow.  The fault address can be used to infer
  30.    this.  However, the fault address is passed to the signal handler in
  31.    different ways on various systems.  One of three methods are used to
  32.    determine the fault address:
  33.    
  34.       1. The siginfo parameter (with siginfo.h, i.e., SVR4).
  35.    
  36.       2. 4th "addr" parameter (assumed if struct sigcontext is defined,
  37.      i.e., SunOS 4.x/BSD).
  38.    
  39.       3. None (if no method is available).  This case just prints a
  40.       message before aborting with a core dump.  That way the user at
  41.       least knows that it *might* be a recursion problem.
  42.    
  43.    Jim Avera <jima@netcom.com> writes, on Tue, 5 Oct 93 19:27 PDT:
  44.    
  45.       "I got interested finding out how a program could catch and
  46.       diagnose its own stack overflow, and ended up modifying m4 to do
  47.       this.  Now it prints a nice error message and exits.
  48.    
  49.       How it works: SIGSEGV is caught using a separate signal stack.  The
  50.       signal handler declares a stack overflow if the fault address is
  51.       near the end of the stack region, or if the maximum VM address
  52.       space limit has been reached.  Otherwise, it returns to re-execute
  53.       the instruction with SIG_DFL set, so that any real bugs cause a
  54.       core dump as usual."
  55.    
  56.    Jim Avera <jima@netcom.com> writes, on Fri, 24 Jun 94 12:14 PDT:
  57.    
  58.       "The stack-overflow detection code would still be needed to avoid a
  59.       SIGSEGV abort if swap space was exhausted at the moment the stack
  60.       tried to grow.  This is probably unlikely to occur with the
  61.       explicit nesting limit option of GNU m4."
  62.    
  63.    Jim Avera <jima@netcom.com> writes, on Wed, 6 Jul 1994 14:41 PDT:
  64.    
  65.       "When a stack overflow occurs, a SIGSEGV signal is sent, which by
  66.       default aborts the process with a core dump.
  67.    
  68.       The code in stackovf.c catches SIGSEGV using a separate signal
  69.       stack.  The signal handler determines whether or not the SIGSEGV
  70.       arose from a stack overflow.  If it is a stack overflow, an
  71.       external function is called (which, in m4, prints a message an
  72.       exits).  Otherwise the SIGSEGV represents an m4 bug, and the signal
  73.       is re-raised with SIG_DFL set, which results in an abort and core
  74.       dump in the usual way.  It seems important (to me) that internal m4
  75.       bugs not be reported as user recursion errors, or vice-versa."  */
  76.  
  77. #define DEBUG_STACKOVF
  78.  
  79. #include "m4.h"            /* stdlib.h, xmalloc() */
  80.  
  81. #include <assert.h>
  82. #include <sys/time.h>
  83. #include <sys/resource.h>
  84. #include <signal.h>
  85.  
  86. #if HAVE_SIGINFO_H
  87. # include <siginfo.h>
  88. #endif
  89.  
  90. #ifndef SIGSTKSZ
  91. # define SIGSTKSZ 8192
  92. #endif
  93.  
  94. /* If the trap address is within STACKOVF_DETECT bytes of the calculated
  95.    stack limit, we diagnose a stack overflow.  This must be large enough
  96.    to cover errors in our estimatation of the limit address, and to
  97.    account for the maximum size of local variables (the amount the
  98.    trapping reference might exceed the stack limit).  Also, some machines
  99.    may report an arbitrary address within the same page frame.
  100.    If the value is too large, we might call some other SIGSEGV a stack 
  101.    overflow, masking a bug.  */
  102.  
  103. #ifndef STACKOVF_DETECT
  104. # define STACKOVF_DETECT 16384
  105. #endif
  106.  
  107. /* Giving a hand to ansi2knr...  */
  108. typedef void (*handler_t) _((void));
  109.  
  110. static const char *stackbot;
  111. static const char *stackend;
  112. static const char *arg0;
  113. static handler_t stackovf_handler;
  114.  
  115. /* The following OS-independent procedure is called from the SIGSEGV
  116.    signal handler.  The signal handler obtains information about the trap
  117.    in an OS-dependent manner, and passes a parameter with the meanings as
  118.    explained below.
  119.    
  120.    If the OS explicitly identifies a stack overflow trap, either pass
  121.    PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not
  122.    (id est, it is a random bounds violation).  Otherwise, if the fault
  123.    address is available, pass the fault address.  Otherwise (if no
  124.    information is available), pass NULL.
  125.    
  126.    Not given an explicit indication, we compare the fault address with
  127.    the estimated stack limit, and test to see if overall VM space is
  128.    exhausted.
  129.    
  130.    If a stack overflow is identified, then the external *stackovf_handler
  131.    function is called, which should print an error message and exit.  If
  132.    it is NOT a stack overflow, then we silently abort with a core dump by
  133.    returning to re-raise the SIGSEGV with SIG_DFL set.  If indeterminate,
  134.    then we do not call *stackovf_handler, but instead print an ambiguous
  135.    message and abort with a core dump.  This only occurs on systems which
  136.    provide no information, but is better than nothing.  */
  137.  
  138. #define PARAM_STACKOVF ((const char *) 1)
  139. #define PARAM_NOSTACKOVF ((const char *) 2)
  140.  
  141. static void
  142. process_sigsegv (int signo, const char *p)
  143. {
  144.   long diff;
  145.   diff = (p - stackend);
  146.  
  147. #ifdef DEBUG_STKOVF
  148.   {
  149.     char buf[140];
  150.  
  151.     sprintf (buf, "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n",
  152.          (long) p, (long) stackend, (long) diff, (long) stackbot);
  153.     write (2, buf, strlen (buf));
  154.   }
  155. #endif
  156.  
  157.   if (p != PARAM_NOSTACKOVF)
  158.     {
  159.       if ((long) sbrk (8192) == (long) -1)
  160.     {
  161.  
  162.       /* sbrk failed.  Assume the RLIMIT_VMEM prevents expansion even
  163.          if the stack limit has not been reached.  */
  164.  
  165.       write (2, "VMEM limit exceeded?\n", 21);
  166.       p = PARAM_STACKOVF;
  167.     }
  168.       if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
  169.     {
  170.  
  171.       /* The fault address is "sufficiently close" to the stack lim.  */
  172.  
  173.       p = PARAM_STACKOVF;
  174.     }
  175.       if (p == PARAM_STACKOVF)
  176.     {
  177.  
  178.       /* We have determined that this is indeed a stack overflow.  */
  179.  
  180.       (*stackovf_handler) ();    /* should call exit() */
  181.     }
  182.     }
  183.   if (p == NULL)
  184.     {
  185.       const char *cp;
  186.  
  187.       cp = "\
  188. Memory bounds violation detected (SIGSEGV).  Either a stack overflow\n\
  189. occurred, or there is a bug in ";
  190.       write (2, cp, strlen (cp));
  191.       write (2, arg0, strlen (arg0));
  192.       cp = ".  Check for possible infinite recursion.\n";
  193.       write (2, cp, strlen (cp));
  194.     }
  195.  
  196.   /* Return to re-execute the instruction which caused the trap with
  197.      SIGSEGV set to SIG_DFL.  An abort with core dump should occur.  */
  198.  
  199.   signal (signo, SIG_DFL);
  200. }
  201.  
  202. #if HAVE_SIGINFO_H
  203.  
  204. /* SVR4.  */
  205.  
  206. static void
  207. sigsegv_handler (int signo, siginfo_t * ip)
  208. {
  209.   process_sigsegv
  210.     (signo, (ip != (siginfo_t *) 0
  211.          && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
  212. }
  213.  
  214. #else /* not HAVE_SIGINFO_H */
  215. #if HAVE_SIGCONTEXT
  216.  
  217. /* SunOS 4.x (and BSD?).  (not tested) */
  218.  
  219. static void
  220. sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
  221. {
  222.   process_sigsegv (signo, addr);
  223. }
  224.  
  225. #else /* not HAVE_SIGCONTEXT */
  226.  
  227. /* OS provides no information.  */
  228.  
  229. static void
  230. sigsegv_handler (int signo)
  231. {
  232.   process_sigsegv (signo, NULL);
  233. }
  234.  
  235. #endif /* not HAVE_SIGCONTEXT */
  236. #endif /* not HAVE_SIGINFO */
  237.  
  238. /* Arrange to trap a stack-overflow and call a specified handler.  The
  239.    call is on a dedicated signal stack.
  240.  
  241.    argv and envp are as passed to main().
  242.  
  243.    If a stack overflow is not detected, then the SIGSEGV is re-raised
  244.    with action set to SIG_DFL, causing an abort and coredump in the usual
  245.    way.
  246.  
  247.    Detection of a stack overflow depends on the trap address being near
  248.    the stack limit address.  The stack limit can not be directly
  249.    determined in a portable way, but we make an estimate based on the
  250.    address of the argv and environment vectors, their contents, and the
  251.    maximum stack size obtained using getrlimit.  */
  252.  
  253. void
  254. setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
  255. {
  256.   struct rlimit rl;
  257.   rlim_t stack_len;
  258.   int grows_upward;
  259.   register char *const *v;
  260.   register char *p;
  261. #if HAVE_SIGACTION && defined(SA_ONSTACK)
  262.   struct sigaction act;
  263. #else
  264.   struct sigvec vec;
  265. #endif
  266.  
  267.   grows_upward = ((char *) argv < (char *) &stack_len);
  268.   arg0 = argv[0];
  269.   stackovf_handler = handler;
  270.  
  271.   /* Calculate the approximate expected addr for a stack-ovf trap.  */
  272.  
  273.   if (getrlimit (RLIMIT_STACK, &rl) < 0)
  274.     error (1, errno, "getrlimit");
  275.   stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max);
  276.   stackbot = (char *) argv;
  277.   grows_upward = ((char *) &stack_len > stackbot);
  278.   if (grows_upward)
  279.     {
  280.  
  281.       /* Grows toward increasing addresses.  */
  282.  
  283.       for (v = argv; (p = (char *) *v) != (char *) 0; v++)
  284.     {
  285.       if (p < stackbot)
  286.         stackbot = p;
  287.     }
  288.       if ((char *) envp < stackbot)
  289.     stackbot = (char *) envp;
  290.       for (v = envp; (p = (char *) *v) != (char *) 0; v++)
  291.     {
  292.       if (p < stackbot)
  293.         stackbot = p;
  294.     }
  295.       stackend = stackbot + stack_len;
  296.     }
  297.   else
  298.     {
  299.  
  300.       /* The stack grows "downward" (toward decreasing addresses).  */
  301.  
  302.       for (v = argv; (p = (char *) *v) != (char *) 0; v++)
  303.     {
  304.       if (p > stackbot)
  305.         stackbot = p;
  306.     }
  307.       if ((char *) envp > stackbot)
  308.     stackbot = (char *) envp;
  309.       for (v = envp; (p = (char *) *v) != (char *) 0; v++)
  310.     {
  311.       if (p > stackbot)
  312.         stackbot = p;
  313.     }
  314.       stackend = stackbot - stack_len;
  315.     }
  316.  
  317.   /* Allocate a separate signal-handler stack.  */
  318.  
  319. #if HAVE_SIGALTSTACK && (defined(HAVE_SIGINFO_H) || !HAVE_SIGSTACK)
  320.  
  321.   /* Use sigaltstack only if siginfo is available, unless there is no
  322.      choice.  */
  323.  
  324.   {
  325.     stack_t ss;
  326.  
  327.     ss.ss_size = SIGSTKSZ;
  328.     ss.ss_sp = xmalloc ((unsigned) ss.ss_size);
  329.     ss.ss_flags = 0;
  330.     if (sigaltstack (&ss, (stack_t *) 0) < 0)
  331.       error (1, errno, "sigaltstack");
  332.   }
  333.  
  334. #else /* not HAVE_SIGALTSTACK || not HAVE_SIGINFO_H && HAVE_SIGSTACK */
  335. #if HAVE_SIGSTACK
  336.  
  337.   {
  338.     struct sigstack ss;
  339.     char *stackbuf = xmalloc (2 * SIGSTKSZ);
  340.  
  341.     ss.ss_sp = stackbuf + SIGSTKSZ;
  342.     ss.ss_onstack = 0;
  343.     if (sigstack (&ss, NULL) < 0)
  344.       error (1, errno, "sigstack");
  345.   }
  346.  
  347. #else /* not HAVE_SIGSTACK */
  348.  
  349. Error - Do not know how to set up stack-ovf trap handler...
  350.  
  351. #endif /* not HAVE_SIGSTACK */
  352. #endif /* not HAVE_SIGALTSTACK || not HAVE_SIGINFO_H && HAVE_SIGSTACK */
  353.  
  354.   /* Arm the SIGSEGV signal handler.  */
  355.  
  356. #if HAVE_SIGACTION && defined(SA_ONSTACK)
  357.  
  358.   sigaction (SIGSEGV, NULL, &act);
  359.   act.sa_handler = (RETSIGTYPE (*) _((int))) sigsegv_handler;
  360.   sigemptyset (&act.sa_mask);
  361.   act.sa_flags = (SA_ONSTACK 
  362. #ifdef SA_RESETHAND
  363.           | SA_RESETHAND
  364. #endif
  365. #ifdef SA_SIGINFO
  366.           | SA_SIGINFO 
  367. #endif
  368.           );
  369.   if (sigaction (SIGSEGV, &act, NULL) < 0)
  370.     error (1, errno, "sigaction");
  371.  
  372. #else /* not HAVE_SIGACTION */
  373. #if HAVE_SIGVEC && defined(SV_ONSTACK)
  374.  
  375.   vec.sv_handler = (RETSIGTYPE (*)_ ((int))) sigsegv_handler;
  376.   vec.sv_mask = 0;
  377.   vec.sv_flags = (SV_ONSTACK
  378. #ifdef SV_RESETHAND
  379.           | SV_RESETHAND
  380. #endif
  381.              );
  382.   if (sigvec (SIGSEGV, &vec, NULL) < 0)
  383.     error (1, errno, "sigvec");
  384.  
  385. #else /* not HAVE_SIGVEC && defined(SV_ONSTACK) */
  386.  
  387. Error - Do not know how to catch signals on an alternate stack...
  388.  
  389. #endif /* HAVE_SIGVEC && defined(SV_ONSTACK) */
  390. #endif /* HAVE_SIGALTSTACK && defined(SA_ONSTACK) */
  391.  
  392. }
  393.