Fix texinfo grammar.
[m4/ericb.git] / src / stackovf.c
blob3877cd9d7d3430947272d48e5a7c75082e2b6e07
1 /* Detect stack overflow (when getrlimit and sigaction or sigvec are available)
3 Copyright (C) 1993, 1994, 2006, 2007 Free Software Foundation, Inc.
4 Jim Avera <jima@netcom.com>, October 1993.
6 This file is part of GNU M4.
8 GNU M4 is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 GNU M4 is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 /* Compiled only when USE_STACKOVF is defined, which itself requires
23 getrlimit with the RLIMIT_STACK option, and support for alternate
24 signal stacks using either SVR4 or BSD interfaces.
26 This should compile on ANY system which supports either sigaltstack()
27 or sigstack(), with or without <siginfo.h> or another way to determine
28 the fault address.
30 There is no completely portable way to determine if a SIGSEGV signal
31 indicates a stack overflow. The fault address can be used to infer
32 this. However, the fault address is passed to the signal handler in
33 different ways on various systems. One of three methods are used to
34 determine the fault address:
36 1. The siginfo parameter (with siginfo.h, i.e., SVR4).
38 2. 4th "addr" parameter (assumed if struct sigcontext is defined,
39 i.e., SunOS 4.x/BSD).
41 3. None (if no method is available). This case just prints a
42 message before aborting with a core dump. That way the user at
43 least knows that it *might* be a recursion problem.
45 Jim Avera <jima@netcom.com> writes, on Tue, 5 Oct 93 19:27 PDT:
47 "I got interested finding out how a program could catch and
48 diagnose its own stack overflow, and ended up modifying m4 to do
49 this. Now it prints a nice error message and exits.
51 How it works: SIGSEGV is caught using a separate signal stack. The
52 signal handler declares a stack overflow if the fault address is
53 near the end of the stack region, or if the maximum VM address
54 space limit has been reached. Otherwise, it returns to re-execute
55 the instruction with SIG_DFL set, so that any real bugs cause a
56 core dump as usual."
58 Jim Avera <jima@netcom.com> writes, on Fri, 24 Jun 94 12:14 PDT:
60 "The stack-overflow detection code would still be needed to avoid a
61 SIGSEGV abort if swap space was exhausted at the moment the stack
62 tried to grow. This is probably unlikely to occur with the
63 explicit nesting limit option of GNU m4."
65 Jim Avera <jima@netcom.com> writes, on Wed, 6 Jul 1994 14:41 PDT:
67 "When a stack overflow occurs, a SIGSEGV signal is sent, which by
68 default aborts the process with a core dump.
70 The code in stackovf.c catches SIGSEGV using a separate signal
71 stack. The signal handler determines whether or not the SIGSEGV
72 arose from a stack overflow. If it is a stack overflow, an
73 external function is called (which, in m4, prints a message an
74 exits). Otherwise the SIGSEGV represents an m4 bug, and the signal
75 is re-raised with SIG_DFL set, which results in an abort and core
76 dump in the usual way. It seems important (to me) that internal m4
77 bugs not be reported as user recursion errors, or vice-versa." */
79 #include "m4.h" /* stdlib.h, xmalloc() */
81 #include <assert.h>
82 #include <sys/time.h>
83 #include <sys/resource.h>
84 #include <signal.h>
86 #if HAVE_SIGINFO_H
87 # include <siginfo.h>
88 #endif
90 #ifndef SA_RESETHAND
91 # define SA_RESETHAND 0
92 #endif
93 #ifndef SA_SIGINFO
94 # define SA_SIGINFO 0
95 #endif
97 #ifndef SIGSTKSZ
98 # define SIGSTKSZ 8192
99 #endif
101 /* If the trap address is within STACKOVF_DETECT bytes of the calculated
102 stack limit, we diagnose a stack overflow. This must be large enough
103 to cover errors in our estimatation of the limit address, and to
104 account for the maximum size of local variables (the amount the
105 trapping reference might exceed the stack limit). Also, some machines
106 may report an arbitrary address within the same page frame.
107 If the value is too large, we might call some other SIGSEGV a stack
108 overflow, masking a bug. */
110 #ifndef STACKOVF_DETECT
111 # define STACKOVF_DETECT 16384
112 #endif
114 typedef void (*handler_t) (void);
116 static const char *stackbot;
117 static const char *stackend;
118 static const char *arg0;
119 static handler_t stackovf_handler;
121 /* The following OS-independent procedure is called from the SIGSEGV
122 signal handler. The signal handler obtains information about the trap
123 in an OS-dependent manner, and passes a parameter with the meanings as
124 explained below.
126 If the OS explicitly identifies a stack overflow trap, either pass
127 PARAM_STACKOVF if a stack overflow, or pass PARAM_NOSTACKOVF if not
128 (id est, it is a random bounds violation). Otherwise, if the fault
129 address is available, pass the fault address. Otherwise (if no
130 information is available), pass NULL.
132 Not given an explicit indication, we compare the fault address with
133 the estimated stack limit, and test to see if overall VM space is
134 exhausted.
136 If a stack overflow is identified, then the external *stackovf_handler
137 function is called, which should print an error message and exit. If
138 it is NOT a stack overflow, then we silently abort with a core dump by
139 returning to re-raise the SIGSEGV with SIG_DFL set. If indeterminate,
140 then we do not call *stackovf_handler, but instead print an ambiguous
141 message and abort with a core dump. This only occurs on systems which
142 provide no information, but is better than nothing. */
144 #define PARAM_STACKOVF ((const char *) (1 + STACKOVF_DETECT))
145 #define PARAM_NOSTACKOVF ((const char *) (2 + STACKOVF_DETECT))
147 static void
148 process_sigsegv (int signo, const char *p)
150 long diff;
151 diff = (p - stackend);
153 #ifdef DEBUG_STKOVF
155 char buf[140];
157 snprintf (buf, sizeof buf,
158 "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n",
159 (long) p, (long) stackend, (long) diff, (long) stackbot);
160 write (2, buf, strlen (buf));
162 #endif
164 if (p != PARAM_NOSTACKOVF)
166 if ((long) sbrk (8192) == (long) -1)
169 /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even
170 if the stack limit has not been reached. */
172 write (2, "VMEM limit exceeded?\n", 21);
173 p = PARAM_STACKOVF;
175 if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
178 /* The fault address is "sufficiently close" to the stack lim. */
180 p = PARAM_STACKOVF;
182 if (p == PARAM_STACKOVF)
185 /* We have determined that this is indeed a stack overflow. */
187 (*stackovf_handler) (); /* should call exit() */
190 if (p == NULL)
192 const char *cp;
194 cp = "\
195 Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\
196 occurred, or there is a bug in ";
197 write (2, cp, strlen (cp));
198 write (2, arg0, strlen (arg0));
199 cp = ". Check for possible infinite recursion.\n";
200 write (2, cp, strlen (cp));
203 /* Return to re-execute the instruction which caused the trap with
204 SIGSEGV set to SIG_DFL. An abort with core dump should occur. */
206 signal (signo, SIG_DFL);
209 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION
211 /* POSIX. */
213 static void
214 sigsegv_handler (int signo, siginfo_t *ip, void *context)
216 process_sigsegv
217 (signo, (ip != NULL
218 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
221 #elif HAVE_SIGINFO_T
223 /* SVR4. */
225 static void
226 sigsegv_handler (int signo, siginfo_t *ip)
228 process_sigsegv
229 (signo, (ip != NULL
230 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
233 #elif HAVE_SIGCONTEXT
235 /* SunOS 4.x (and BSD?). (not tested) */
237 static void
238 sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
240 process_sigsegv (signo, addr);
243 #else /* not HAVE_SIGCONTEXT */
245 /* OS provides no information. */
247 static void
248 sigsegv_handler (int signo)
250 process_sigsegv (signo, NULL);
253 #endif /* not HAVE_SIGCONTEXT */
255 /* Arrange to trap a stack-overflow and call a specified handler. The
256 call is on a dedicated signal stack.
258 argv and envp are as passed to main().
260 If a stack overflow is not detected, then the SIGSEGV is re-raised
261 with action set to SIG_DFL, causing an abort and coredump in the usual
262 way.
264 Detection of a stack overflow depends on the trap address being near
265 the stack limit address. The stack limit can not be directly
266 determined in a portable way, but we make an estimate based on the
267 address of the argv and environment vectors, their contents, and the
268 maximum stack size obtained using getrlimit. */
270 void
271 setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
273 struct rlimit rl;
274 rlim_t stack_len;
275 int grows_upward;
276 register char *const *v;
277 register char *p;
278 #if HAVE_SIGACTION && defined SA_ONSTACK
279 struct sigaction act;
280 #elif HAVE_SIGVEC && defined SV_ONSTACK
281 struct sigvec vec;
282 #else
284 Error - Do not know how to set up stack-ovf trap handler...
286 #endif
288 arg0 = argv[0];
289 stackovf_handler = handler;
291 /* Calculate the approximate expected addr for a stack-ovf trap. */
293 if (getrlimit (RLIMIT_STACK, &rl) < 0)
294 error (EXIT_FAILURE, errno, "getrlimit");
295 stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max);
296 stackbot = (char *) argv;
297 grows_upward = ((char *) &stack_len > stackbot);
298 if (grows_upward)
301 /* Grows toward increasing addresses. */
303 for (v = argv; (p = (char *) *v) != NULL; v++)
305 if (p < stackbot)
306 stackbot = p;
308 if ((char *) envp < stackbot)
309 stackbot = (char *) envp;
310 for (v = envp; (p = (char *) *v) != NULL; v++)
312 if (p < stackbot)
313 stackbot = p;
315 stackend = stackbot + stack_len;
317 else
320 /* The stack grows "downward" (toward decreasing addresses). */
322 for (v = argv; (p = (char *) *v) != NULL; v++)
324 if (p > stackbot)
325 stackbot = p;
327 if ((char *) envp > stackbot)
328 stackbot = (char *) envp;
329 for (v = envp; (p = (char *) *v) != NULL; v++)
331 if (p > stackbot)
332 stackbot = p;
334 stackend = stackbot - stack_len;
337 /* Allocate a separate signal-handler stack. */
339 #if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK)
341 /* Use sigaltstack only if siginfo_t is available, unless there is no
342 choice. */
345 stack_t ss;
346 # ifndef HAVE_STACK_T_SS_SP
347 /* This workaround is for BSD/OS 4.0.1:
348 http://lists.gnu.org/archive/html/bug-m4/2006-12/msg00004.html */
349 # define ss_sp ss_base
350 # endif
352 ss.ss_size = SIGSTKSZ;
353 ss.ss_sp = xmalloc ((unsigned) ss.ss_size);
354 ss.ss_flags = 0;
355 if (sigaltstack (&ss, NULL) < 0)
357 /* Oops - sigaltstack exists but doesn't work. We can't
358 install the overflow detector, but should gracefully treat
359 it as though sigaltstack doesn't exist. For example, this
360 happens when compiled with Linux 2.1 headers but run
361 against Linux 2.0 kernel. */
362 free (ss.ss_sp);
363 if (errno == ENOSYS)
364 return;
365 error (EXIT_FAILURE, errno, "sigaltstack");
369 #elif HAVE_SIGSTACK
372 struct sigstack ss;
373 char *stackbuf = xmalloc (2 * SIGSTKSZ);
375 ss.ss_sp = stackbuf + SIGSTKSZ;
376 ss.ss_onstack = 0;
377 if (sigstack (&ss, NULL) < 0)
379 /* Oops - sigstack exists but doesn't work. We can't install
380 the overflow detector, but should gracefully treat it as
381 though sigstack doesn't exist. For example, this happens
382 when compiled with Linux 2.1 headers but run against Linux
383 2.0 kernel. */
384 free (stackbuf);
385 if (errno == ENOSYS)
386 return;
387 error (EXIT_FAILURE, errno, "sigstack");
391 #else /* not HAVE_SIGSTACK */
393 Error - Do not know how to set up stack-ovf trap handler...
395 #endif /* not HAVE_SIGSTACK */
397 /* Arm the SIGSEGV signal handler. */
399 #if HAVE_SIGACTION && defined SA_ONSTACK
401 sigaction (SIGSEGV, NULL, &act);
402 # if HAVE_STRUCT_SIGACTION_SA_SIGACTION
403 act.sa_sigaction = sigsegv_handler;
404 # else /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
405 act.sa_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
406 # endif /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
407 sigemptyset (&act.sa_mask);
408 act.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO);
409 if (sigaction (SIGSEGV, &act, NULL) < 0)
410 error (EXIT_FAILURE, errno, "sigaction");
412 #else /* ! HAVE_SIGACTION */
414 vec.sv_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
415 vec.sv_mask = 0;
416 vec.sv_flags = (SV_ONSTACK | SV_RESETHAND);
417 if (sigvec (SIGSEGV, &vec, NULL) < 0)
418 error (EXIT_FAILURE, errno, "sigvec");
420 #endif /* ! HAVE_SIGACTION */