Release 1.4.6:
[m4/ericb.git] / src / stackovf.c
blob2aae5076435e8a337f1bb2bfd2cba4f87d4953c1
1 /* Detect stack overflow (when getrlimit and sigaction or sigvec are available)
2 Copyright (C) 1993, 1994, 2006 Free Software Foundation, Inc.
3 Jim Avera <jima@netcom.com>, October 1993.
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 of the License, or
8 (at your option) any later version.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301 USA
21 /* Compiled only when USE_STACKOVF is defined, which itself requires
22 getrlimit with the RLIMIT_STACK option, and support for alternate
23 signal stacks using either SVR4 or BSD interfaces.
25 This should compile on ANY system which supports either sigaltstack()
26 or sigstack(), with or without <siginfo.h> or another way to determine
27 the fault address.
29 There is no completely portable way to determine if a SIGSEGV signal
30 indicates a stack overflow. The fault address can be used to infer
31 this. However, the fault address is passed to the signal handler in
32 different ways on various systems. One of three methods are used to
33 determine the fault address:
35 1. The siginfo parameter (with siginfo.h, i.e., SVR4).
37 2. 4th "addr" parameter (assumed if struct sigcontext is defined,
38 i.e., SunOS 4.x/BSD).
40 3. None (if no method is available). This case just prints a
41 message before aborting with a core dump. That way the user at
42 least knows that it *might* be a recursion problem.
44 Jim Avera <jima@netcom.com> writes, on Tue, 5 Oct 93 19:27 PDT:
46 "I got interested finding out how a program could catch and
47 diagnose its own stack overflow, and ended up modifying m4 to do
48 this. Now it prints a nice error message and exits.
50 How it works: SIGSEGV is caught using a separate signal stack. The
51 signal handler declares a stack overflow if the fault address is
52 near the end of the stack region, or if the maximum VM address
53 space limit has been reached. Otherwise, it returns to re-execute
54 the instruction with SIG_DFL set, so that any real bugs cause a
55 core dump as usual."
57 Jim Avera <jima@netcom.com> writes, on Fri, 24 Jun 94 12:14 PDT:
59 "The stack-overflow detection code would still be needed to avoid a
60 SIGSEGV abort if swap space was exhausted at the moment the stack
61 tried to grow. This is probably unlikely to occur with the
62 explicit nesting limit option of GNU m4."
64 Jim Avera <jima@netcom.com> writes, on Wed, 6 Jul 1994 14:41 PDT:
66 "When a stack overflow occurs, a SIGSEGV signal is sent, which by
67 default aborts the process with a core dump.
69 The code in stackovf.c catches SIGSEGV using a separate signal
70 stack. The signal handler determines whether or not the SIGSEGV
71 arose from a stack overflow. If it is a stack overflow, an
72 external function is called (which, in m4, prints a message an
73 exits). Otherwise the SIGSEGV represents an m4 bug, and the signal
74 is re-raised with SIG_DFL set, which results in an abort and core
75 dump in the usual way. It seems important (to me) that internal m4
76 bugs not be reported as user recursion errors, or vice-versa." */
78 #include "m4.h" /* stdlib.h, xmalloc() */
80 #include <assert.h>
81 #include <sys/time.h>
82 #include <sys/resource.h>
83 #include <signal.h>
85 #if HAVE_SIGINFO_H
86 # include <siginfo.h>
87 #endif
89 #ifndef SA_RESETHAND
90 # define SA_RESETHAND 0
91 #endif
92 #ifndef SA_SIGINFO
93 # define SA_SIGINFO 0
94 #endif
96 #ifndef SIGSTKSZ
97 # define SIGSTKSZ 8192
98 #endif
100 /* If the trap address is within STACKOVF_DETECT bytes of the calculated
101 stack limit, we diagnose a stack overflow. This must be large enough
102 to cover errors in our estimatation of the limit address, and to
103 account for the maximum size of local variables (the amount the
104 trapping reference might exceed the stack limit). Also, some machines
105 may report an arbitrary address within the same page frame.
106 If the value is too large, we might call some other SIGSEGV a stack
107 overflow, masking a bug. */
109 #ifndef STACKOVF_DETECT
110 # define STACKOVF_DETECT 16384
111 #endif
113 typedef void (*handler_t) (void);
115 static const char *stackbot;
116 static const char *stackend;
117 static const char *arg0;
118 static handler_t stackovf_handler;
120 /* The following OS-independent procedure is called from the SIGSEGV
121 signal handler. The signal handler obtains information about the trap
122 in an OS-dependent manner, and passes a parameter with the meanings as
123 explained below.
125 If the OS explicitly identifies a stack overflow trap, either pass
126 PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not
127 (id est, it is a random bounds violation). Otherwise, if the fault
128 address is available, pass the fault address. Otherwise (if no
129 information is available), pass NULL.
131 Not given an explicit indication, we compare the fault address with
132 the estimated stack limit, and test to see if overall VM space is
133 exhausted.
135 If a stack overflow is identified, then the external *stackovf_handler
136 function is called, which should print an error message and exit. If
137 it is NOT a stack overflow, then we silently abort with a core dump by
138 returning to re-raise the SIGSEGV with SIG_DFL set. If indeterminate,
139 then we do not call *stackovf_handler, but instead print an ambiguous
140 message and abort with a core dump. This only occurs on systems which
141 provide no information, but is better than nothing. */
143 #define PARAM_STACKOVF ((const char *) (1 + STACKOVF_DETECT))
144 #define PARAM_NOSTACKOVF ((const char *) (2 + STACKOVF_DETECT))
146 static void
147 process_sigsegv (int signo, const char *p)
149 long diff;
150 diff = (p - stackend);
152 #ifdef DEBUG_STKOVF
154 char buf[140];
156 sprintf (buf, "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n",
157 (long) p, (long) stackend, (long) diff, (long) stackbot);
158 write (2, buf, strlen (buf));
160 #endif
162 if (p != PARAM_NOSTACKOVF)
164 if ((long) sbrk (8192) == (long) -1)
167 /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even
168 if the stack limit has not been reached. */
170 write (2, "VMEM limit exceeded?\n", 21);
171 p = PARAM_STACKOVF;
173 if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
176 /* The fault address is "sufficiently close" to the stack lim. */
178 p = PARAM_STACKOVF;
180 if (p == PARAM_STACKOVF)
183 /* We have determined that this is indeed a stack overflow. */
185 (*stackovf_handler) (); /* should call exit() */
188 if (p == NULL)
190 const char *cp;
192 cp = "\
193 Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\
194 occurred, or there is a bug in ";
195 write (2, cp, strlen (cp));
196 write (2, arg0, strlen (arg0));
197 cp = ". Check for possible infinite recursion.\n";
198 write (2, cp, strlen (cp));
201 /* Return to re-execute the instruction which caused the trap with
202 SIGSEGV set to SIG_DFL. An abort with core dump should occur. */
204 signal (signo, SIG_DFL);
207 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION
209 /* POSIX. */
211 static void
212 sigsegv_handler (int signo, siginfo_t *ip, void *context)
214 process_sigsegv
215 (signo, (ip != NULL
216 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
219 #elif HAVE_SIGINFO_T
221 /* SVR4. */
223 static void
224 sigsegv_handler (int signo, siginfo_t *ip)
226 process_sigsegv
227 (signo, (ip != NULL
228 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
231 #elif HAVE_SIGCONTEXT
233 /* SunOS 4.x (and BSD?). (not tested) */
235 static void
236 sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
238 process_sigsegv (signo, addr);
241 #else /* not HAVE_SIGCONTEXT */
243 /* OS provides no information. */
245 static void
246 sigsegv_handler (int signo)
248 process_sigsegv (signo, NULL);
251 #endif /* not HAVE_SIGCONTEXT */
253 /* Arrange to trap a stack-overflow and call a specified handler. The
254 call is on a dedicated signal stack.
256 argv and envp are as passed to main().
258 If a stack overflow is not detected, then the SIGSEGV is re-raised
259 with action set to SIG_DFL, causing an abort and coredump in the usual
260 way.
262 Detection of a stack overflow depends on the trap address being near
263 the stack limit address. The stack limit can not be directly
264 determined in a portable way, but we make an estimate based on the
265 address of the argv and environment vectors, their contents, and the
266 maximum stack size obtained using getrlimit. */
268 void
269 setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
271 struct rlimit rl;
272 rlim_t stack_len;
273 int grows_upward;
274 register char *const *v;
275 register char *p;
276 #if HAVE_SIGACTION && defined SA_ONSTACK
277 struct sigaction act;
278 #elif HAVE_SIGVEC && defined SV_ONSTACK
279 struct sigvec vec;
280 #else
282 Error - Do not know how to set up stack-ovf trap handler...
284 #endif
286 arg0 = argv[0];
287 stackovf_handler = handler;
289 /* Calculate the approximate expected addr for a stack-ovf trap. */
291 if (getrlimit (RLIMIT_STACK, &rl) < 0)
292 error (EXIT_FAILURE, errno, "getrlimit");
293 stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max);
294 stackbot = (char *) argv;
295 grows_upward = ((char *) &stack_len > stackbot);
296 if (grows_upward)
299 /* Grows toward increasing addresses. */
301 for (v = argv; (p = (char *) *v) != NULL; v++)
303 if (p < stackbot)
304 stackbot = p;
306 if ((char *) envp < stackbot)
307 stackbot = (char *) envp;
308 for (v = envp; (p = (char *) *v) != NULL; v++)
310 if (p < stackbot)
311 stackbot = p;
313 stackend = stackbot + stack_len;
315 else
318 /* The stack grows "downward" (toward decreasing addresses). */
320 for (v = argv; (p = (char *) *v) != NULL; v++)
322 if (p > stackbot)
323 stackbot = p;
325 if ((char *) envp > stackbot)
326 stackbot = (char *) envp;
327 for (v = envp; (p = (char *) *v) != NULL; v++)
329 if (p > stackbot)
330 stackbot = p;
332 stackend = stackbot - stack_len;
335 /* Allocate a separate signal-handler stack. */
337 #if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK)
339 /* Use sigaltstack only if siginfo_t is available, unless there is no
340 choice. */
343 stack_t ss;
345 ss.ss_size = SIGSTKSZ;
346 ss.ss_sp = xmalloc ((unsigned) ss.ss_size);
347 ss.ss_flags = 0;
348 if (sigaltstack (&ss, NULL) < 0)
350 /* Oops - sigaltstack exists but doesn't work. We can't
351 install the overflow detector, but should gracefully treat
352 it as though sigaltstack doesn't exist. For example, this
353 happens when compiled with Linux 2.1 headers but run
354 against Linux 2.0 kernel. */
355 free (ss.ss_sp);
356 if (errno == ENOSYS)
357 return;
358 error (EXIT_FAILURE, errno, "sigaltstack");
362 #elif HAVE_SIGSTACK
365 struct sigstack ss;
366 char *stackbuf = xmalloc (2 * SIGSTKSZ);
368 ss.ss_sp = stackbuf + SIGSTKSZ;
369 ss.ss_onstack = 0;
370 if (sigstack (&ss, NULL) < 0)
372 /* Oops - sigstack exists but doesn't work. We can't install
373 the overflow detector, but should gracefully treat it as
374 though sigstack doesn't exist. For example, this happens
375 when compiled with Linux 2.1 headers but run against Linux
376 2.0 kernel. */
377 free (stackbuf);
378 if (errno == ENOSYS)
379 return;
380 error (EXIT_FAILURE, errno, "sigstack");
384 #else /* not HAVE_SIGSTACK */
386 Error - Do not know how to set up stack-ovf trap handler...
388 #endif /* not HAVE_SIGSTACK */
390 /* Arm the SIGSEGV signal handler. */
392 #if HAVE_SIGACTION && defined SA_ONSTACK
394 sigaction (SIGSEGV, NULL, &act);
395 # if HAVE_STRUCT_SIGACTION_SA_SIGACTION
396 act.sa_sigaction = sigsegv_handler;
397 # else /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
398 act.sa_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
399 # endif /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
400 sigemptyset (&act.sa_mask);
401 act.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO);
402 if (sigaction (SIGSEGV, &act, NULL) < 0)
403 error (EXIT_FAILURE, errno, "sigaction");
405 #else /* ! HAVE_SIGACTION */
407 vec.sv_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
408 vec.sv_mask = 0;
409 vec.sv_flags = (SV_ONSTACK | SV_RESETHAND);
410 if (sigvec (SIGSEGV, &vec, NULL) < 0)
411 error (EXIT_FAILURE, errno, "sigvec");
413 #endif /* ! HAVE_SIGACTION */