m4-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Changes to m4/src/stackovf.c [branch-1_4]


From: Gary V . Vaughan
Subject: Changes to m4/src/stackovf.c [branch-1_4]
Date: Sun, 01 May 2005 07:54:19 -0400

Index: m4/src/stackovf.c
diff -u /dev/null m4/src/stackovf.c:1.1.1.1.2.1
--- /dev/null   Sun May  1 11:54:19 2005
+++ m4/src/stackovf.c   Sun May  1 11:54:12 2005
@@ -0,0 +1,393 @@
+/* Detect stack overflow (when getrlimit and sigaction or sigvec are available)
+   Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+   Jim Avera <address@hidden>, October 1993.
+
+   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 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301  USA
+ */
+
+/* Compiled only when USE_STACKOVF is defined, which itself requires
+   getrlimit with the RLIMIT_STACK option, and support for alternate
+   signal stacks using either SVR4 or BSD interfaces.
+
+   This should compile on ANY system which supports either sigaltstack()
+   or sigstack(), with or without <siginfo.h> or another way to determine
+   the fault address.
+
+   There is no completely portable way to determine if a SIGSEGV signal
+   indicates a stack overflow.  The fault address can be used to infer
+   this.  However, the fault address is passed to the signal handler in
+   different ways on various systems.  One of three methods are used to
+   determine the fault address:
+
+      1. The siginfo parameter (with siginfo.h, i.e., SVR4).
+
+      2. 4th "addr" parameter (assumed if struct sigcontext is defined,
+        i.e., SunOS 4.x/BSD).
+
+      3. None (if no method is available).  This case just prints a
+      message before aborting with a core dump.  That way the user at
+      least knows that it *might* be a recursion problem.
+
+   Jim Avera <address@hidden> writes, on Tue, 5 Oct 93 19:27 PDT:
+
+      "I got interested finding out how a program could catch and
+      diagnose its own stack overflow, and ended up modifying m4 to do
+      this.  Now it prints a nice error message and exits.
+
+      How it works: SIGSEGV is caught using a separate signal stack.  The
+      signal handler declares a stack overflow if the fault address is
+      near the end of the stack region, or if the maximum VM address
+      space limit has been reached.  Otherwise, it returns to re-execute
+      the instruction with SIG_DFL set, so that any real bugs cause a
+      core dump as usual."
+
+   Jim Avera <address@hidden> writes, on Fri, 24 Jun 94 12:14 PDT:
+
+      "The stack-overflow detection code would still be needed to avoid a
+      SIGSEGV abort if swap space was exhausted at the moment the stack
+      tried to grow.  This is probably unlikely to occur with the
+      explicit nesting limit option of GNU m4."
+
+   Jim Avera <address@hidden> writes, on Wed, 6 Jul 1994 14:41 PDT:
+
+      "When a stack overflow occurs, a SIGSEGV signal is sent, which by
+      default aborts the process with a core dump.
+
+      The code in stackovf.c catches SIGSEGV using a separate signal
+      stack.  The signal handler determines whether or not the SIGSEGV
+      arose from a stack overflow.  If it is a stack overflow, an
+      external function is called (which, in m4, prints a message an
+      exits).  Otherwise the SIGSEGV represents an m4 bug, and the signal
+      is re-raised with SIG_DFL set, which results in an abort and core
+      dump in the usual way.  It seems important (to me) that internal m4
+      bugs not be reported as user recursion errors, or vice-versa."  */
+
+#define DEBUG_STACKOVF
+
+#include "m4.h"                        /* stdlib.h, xmalloc() */
+
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+
+#if HAVE_SIGINFO_H
+# include <siginfo.h>
+#endif
+
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 8192
+#endif
+
+/* If the trap address is within STACKOVF_DETECT bytes of the calculated
+   stack limit, we diagnose a stack overflow.  This must be large enough
+   to cover errors in our estimatation of the limit address, and to
+   account for the maximum size of local variables (the amount the
+   trapping reference might exceed the stack limit).  Also, some machines
+   may report an arbitrary address within the same page frame.
+   If the value is too large, we might call some other SIGSEGV a stack
+   overflow, masking a bug.  */
+
+#ifndef STACKOVF_DETECT
+# define STACKOVF_DETECT 16384
+#endif
+
+/* Giving a hand to ansi2knr...  */
+typedef void (*handler_t) _((void));
+
+static const char *stackbot;
+static const char *stackend;
+static const char *arg0;
+static handler_t stackovf_handler;
+
+/* The following OS-independent procedure is called from the SIGSEGV
+   signal handler.  The signal handler obtains information about the trap
+   in an OS-dependent manner, and passes a parameter with the meanings as
+   explained below.
+
+   If the OS explicitly identifies a stack overflow trap, either pass
+   PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not
+   (id est, it is a random bounds violation).  Otherwise, if the fault
+   address is available, pass the fault address.  Otherwise (if no
+   information is available), pass NULL.
+
+   Not given an explicit indication, we compare the fault address with
+   the estimated stack limit, and test to see if overall VM space is
+   exhausted.
+
+   If a stack overflow is identified, then the external *stackovf_handler
+   function is called, which should print an error message and exit.  If
+   it is NOT a stack overflow, then we silently abort with a core dump by
+   returning to re-raise the SIGSEGV with SIG_DFL set.  If indeterminate,
+   then we do not call *stackovf_handler, but instead print an ambiguous
+   message and abort with a core dump.  This only occurs on systems which
+   provide no information, but is better than nothing.  */
+
+#define PARAM_STACKOVF ((const char *) 1)
+#define PARAM_NOSTACKOVF ((const char *) 2)
+
+static void
+process_sigsegv (int signo, const char *p)
+{
+  long diff;
+  diff = (p - stackend);
+
+#ifdef DEBUG_STKOVF
+  {
+    char buf[140];
+
+    sprintf (buf, "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n",
+            (long) p, (long) stackend, (long) diff, (long) stackbot);
+    write (2, buf, strlen (buf));
+  }
+#endif
+
+  if (p != PARAM_NOSTACKOVF)
+    {
+      if ((long) sbrk (8192) == (long) -1)
+       {
+
+         /* sbrk failed.  Assume the RLIMIT_VMEM prevents expansion even
+            if the stack limit has not been reached.  */
+
+         write (2, "VMEM limit exceeded?\n", 21);
+         p = PARAM_STACKOVF;
+       }
+      if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
+       {
+
+         /* The fault address is "sufficiently close" to the stack lim.  */
+
+         p = PARAM_STACKOVF;
+       }
+      if (p == PARAM_STACKOVF)
+       {
+
+         /* We have determined that this is indeed a stack overflow.  */
+
+         (*stackovf_handler) ();       /* should call exit() */
+       }
+    }
+  if (p == NULL)
+    {
+      const char *cp;
+
+      cp = "\
+Memory bounds violation detected (SIGSEGV).  Either a stack overflow\n\
+occurred, or there is a bug in ";
+      write (2, cp, strlen (cp));
+      write (2, arg0, strlen (arg0));
+      cp = ".  Check for possible infinite recursion.\n";
+      write (2, cp, strlen (cp));
+    }
+
+  /* Return to re-execute the instruction which caused the trap with
+     SIGSEGV set to SIG_DFL.  An abort with core dump should occur.  */
+
+  signal (signo, SIG_DFL);
+}
+
+#if HAVE_SIGINFO_H
+
+/* SVR4.  */
+
+static void
+sigsegv_handler (int signo, siginfo_t * ip)
+{
+  process_sigsegv
+    (signo, (ip != (siginfo_t *) 0
+            && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
+}
+
+#else /* not HAVE_SIGINFO_H */
+#if HAVE_SIGCONTEXT
+
+/* SunOS 4.x (and BSD?).  (not tested) */
+
+static void
+sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
+{
+  process_sigsegv (signo, addr);
+}
+
+#else /* not HAVE_SIGCONTEXT */
+
+/* OS provides no information.  */
+
+static void
+sigsegv_handler (int signo)
+{
+  process_sigsegv (signo, NULL);
+}
+
+#endif /* not HAVE_SIGCONTEXT */
+#endif /* not HAVE_SIGINFO */
+
+/* Arrange to trap a stack-overflow and call a specified handler.  The
+   call is on a dedicated signal stack.
+
+   argv and envp are as passed to main().
+
+   If a stack overflow is not detected, then the SIGSEGV is re-raised
+   with action set to SIG_DFL, causing an abort and coredump in the usual
+   way.
+
+   Detection of a stack overflow depends on the trap address being near
+   the stack limit address.  The stack limit can not be directly
+   determined in a portable way, but we make an estimate based on the
+   address of the argv and environment vectors, their contents, and the
+   maximum stack size obtained using getrlimit.  */
+
+void
+setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
+{
+  struct rlimit rl;
+  rlim_t stack_len;
+  int grows_upward;
+  register char *const *v;
+  register char *p;
+#if HAVE_SIGACTION && defined(SA_ONSTACK)
+  struct sigaction act;
+#else
+  struct sigvec vec;
+#endif
+
+  grows_upward = ((char *) argv < (char *) &stack_len);
+  arg0 = argv[0];
+  stackovf_handler = handler;
+
+  /* Calculate the approximate expected addr for a stack-ovf trap.  */
+
+  if (getrlimit (RLIMIT_STACK, &rl) < 0)
+    error (1, errno, "getrlimit");
+  stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max);
+  stackbot = (char *) argv;
+  grows_upward = ((char *) &stack_len > stackbot);
+  if (grows_upward)
+    {
+
+      /* Grows toward increasing addresses.  */
+
+      for (v = argv; (p = (char *) *v) != (char *) 0; v++)
+       {
+         if (p < stackbot)
+           stackbot = p;
+       }
+      if ((char *) envp < stackbot)
+       stackbot = (char *) envp;
+      for (v = envp; (p = (char *) *v) != (char *) 0; v++)
+       {
+         if (p < stackbot)
+           stackbot = p;
+       }
+      stackend = stackbot + stack_len;
+    }
+  else
+    {
+
+      /* The stack grows "downward" (toward decreasing addresses).  */
+
+      for (v = argv; (p = (char *) *v) != (char *) 0; v++)
+       {
+         if (p > stackbot)
+           stackbot = p;
+       }
+      if ((char *) envp > stackbot)
+       stackbot = (char *) envp;
+      for (v = envp; (p = (char *) *v) != (char *) 0; v++)
+       {
+         if (p > stackbot)
+           stackbot = p;
+       }
+      stackend = stackbot - stack_len;
+    }
+
+  /* Allocate a separate signal-handler stack.  */
+
+#if HAVE_SIGALTSTACK && (defined(HAVE_SIGINFO_H) || !HAVE_SIGSTACK)
+
+  /* Use sigaltstack only if siginfo is available, unless there is no
+     choice.  */
+
+  {
+    stack_t ss;
+
+    ss.ss_size = SIGSTKSZ;
+    ss.ss_sp = xmalloc ((unsigned) ss.ss_size);
+    ss.ss_flags = 0;
+    if (sigaltstack (&ss, (stack_t *) 0) < 0)
+      error (1, errno, "sigaltstack");
+  }
+
+#else /* not HAVE_SIGALTSTACK || not HAVE_SIGINFO_H && HAVE_SIGSTACK */
+#if HAVE_SIGSTACK
+
+  {
+    struct sigstack ss;
+    char *stackbuf = xmalloc (2 * SIGSTKSZ);
+
+    ss.ss_sp = stackbuf + SIGSTKSZ;
+    ss.ss_onstack = 0;
+    if (sigstack (&ss, NULL) < 0)
+      error (1, errno, "sigstack");
+  }
+
+#else /* not HAVE_SIGSTACK */
+
+Error - Do not know how to set up stack-ovf trap handler...
+
+#endif /* not HAVE_SIGSTACK */
+#endif /* not HAVE_SIGALTSTACK || not HAVE_SIGINFO_H && HAVE_SIGSTACK */
+
+  /* Arm the SIGSEGV signal handler.  */
+
+#if HAVE_SIGACTION && defined(SA_ONSTACK)
+
+  sigaction (SIGSEGV, NULL, &act);
+  act.sa_handler = (RETSIGTYPE (*) _((int))) sigsegv_handler;
+  sigemptyset (&act.sa_mask);
+  act.sa_flags = (SA_ONSTACK
+#ifdef SA_RESETHAND
+                 | SA_RESETHAND
+#endif
+#ifdef SA_SIGINFO
+                 | SA_SIGINFO
+#endif
+                 );
+  if (sigaction (SIGSEGV, &act, NULL) < 0)
+    error (1, errno, "sigaction");
+
+#else /* not HAVE_SIGACTION */
+#if HAVE_SIGVEC && defined(SV_ONSTACK)
+
+  vec.sv_handler = (RETSIGTYPE (*)_ ((int))) sigsegv_handler;
+  vec.sv_mask = 0;
+  vec.sv_flags = (SV_ONSTACK
+#ifdef SV_RESETHAND
+                 | SV_RESETHAND
+#endif
+                );
+  if (sigvec (SIGSEGV, &vec, NULL) < 0)
+    error (1, errno, "sigvec");
+
+#else /* not HAVE_SIGVEC && defined(SV_ONSTACK) */
+
+Error - Do not know how to catch signals on an alternate stack...
+
+#endif /* HAVE_SIGVEC && defined(SV_ONSTACK) */
+#endif /* HAVE_SIGALTSTACK && defined(SA_ONSTACK) */
+
+}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]