Cache regex compilation for another autoconf speedup.
[m4/ericb.git] / src / stackovf.c
blob775ba30bbea76734bf407272ffa434734981d9b9
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 sprintf (buf, "process_sigsegv: p=%#lx stackend=%#lx diff=%ld bot=%#lx\n",
158 (long) p, (long) stackend, (long) diff, (long) stackbot);
159 write (2, buf, strlen (buf));
161 #endif
163 if (p != PARAM_NOSTACKOVF)
165 if ((long) sbrk (8192) == (long) -1)
168 /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even
169 if the stack limit has not been reached. */
171 write (2, "VMEM limit exceeded?\n", 21);
172 p = PARAM_STACKOVF;
174 if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
177 /* The fault address is "sufficiently close" to the stack lim. */
179 p = PARAM_STACKOVF;
181 if (p == PARAM_STACKOVF)
184 /* We have determined that this is indeed a stack overflow. */
186 (*stackovf_handler) (); /* should call exit() */
189 if (p == NULL)
191 const char *cp;
193 cp = "\
194 Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\
195 occurred, or there is a bug in ";
196 write (2, cp, strlen (cp));
197 write (2, arg0, strlen (arg0));
198 cp = ". Check for possible infinite recursion.\n";
199 write (2, cp, strlen (cp));
202 /* Return to re-execute the instruction which caused the trap with
203 SIGSEGV set to SIG_DFL. An abort with core dump should occur. */
205 signal (signo, SIG_DFL);
208 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION
210 /* POSIX. */
212 static void
213 sigsegv_handler (int signo, siginfo_t *ip, void *context)
215 process_sigsegv
216 (signo, (ip != NULL
217 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
220 #elif HAVE_SIGINFO_T
222 /* SVR4. */
224 static void
225 sigsegv_handler (int signo, siginfo_t *ip)
227 process_sigsegv
228 (signo, (ip != NULL
229 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
232 #elif HAVE_SIGCONTEXT
234 /* SunOS 4.x (and BSD?). (not tested) */
236 static void
237 sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
239 process_sigsegv (signo, addr);
242 #else /* not HAVE_SIGCONTEXT */
244 /* OS provides no information. */
246 static void
247 sigsegv_handler (int signo)
249 process_sigsegv (signo, NULL);
252 #endif /* not HAVE_SIGCONTEXT */
254 /* Arrange to trap a stack-overflow and call a specified handler. The
255 call is on a dedicated signal stack.
257 argv and envp are as passed to main().
259 If a stack overflow is not detected, then the SIGSEGV is re-raised
260 with action set to SIG_DFL, causing an abort and coredump in the usual
261 way.
263 Detection of a stack overflow depends on the trap address being near
264 the stack limit address. The stack limit can not be directly
265 determined in a portable way, but we make an estimate based on the
266 address of the argv and environment vectors, their contents, and the
267 maximum stack size obtained using getrlimit. */
269 void
270 setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
272 struct rlimit rl;
273 rlim_t stack_len;
274 int grows_upward;
275 register char *const *v;
276 register char *p;
277 #if HAVE_SIGACTION && defined SA_ONSTACK
278 struct sigaction act;
279 #elif HAVE_SIGVEC && defined SV_ONSTACK
280 struct sigvec vec;
281 #else
283 Error - Do not know how to set up stack-ovf trap handler...
285 #endif
287 arg0 = argv[0];
288 stackovf_handler = handler;
290 /* Calculate the approximate expected addr for a stack-ovf trap. */
292 if (getrlimit (RLIMIT_STACK, &rl) < 0)
293 error (EXIT_FAILURE, errno, "getrlimit");
294 stack_len = (rl.rlim_cur < rl.rlim_max ? rl.rlim_cur : rl.rlim_max);
295 stackbot = (char *) argv;
296 grows_upward = ((char *) &stack_len > stackbot);
297 if (grows_upward)
300 /* Grows toward increasing addresses. */
302 for (v = argv; (p = (char *) *v) != NULL; v++)
304 if (p < stackbot)
305 stackbot = p;
307 if ((char *) envp < stackbot)
308 stackbot = (char *) envp;
309 for (v = envp; (p = (char *) *v) != NULL; v++)
311 if (p < stackbot)
312 stackbot = p;
314 stackend = stackbot + stack_len;
316 else
319 /* The stack grows "downward" (toward decreasing addresses). */
321 for (v = argv; (p = (char *) *v) != NULL; v++)
323 if (p > stackbot)
324 stackbot = p;
326 if ((char *) envp > stackbot)
327 stackbot = (char *) envp;
328 for (v = envp; (p = (char *) *v) != NULL; v++)
330 if (p > stackbot)
331 stackbot = p;
333 stackend = stackbot - stack_len;
336 /* Allocate a separate signal-handler stack. */
338 #if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK)
340 /* Use sigaltstack only if siginfo_t is available, unless there is no
341 choice. */
344 stack_t ss;
345 # ifndef HAVE_STACK_T_SS_SP
346 /* This workaround is for BSD/OS 4.0.1:
347 http://lists.gnu.org/archive/html/bug-m4/2006-12/msg00004.html */
348 # define ss_sp ss_base
349 # endif
351 ss.ss_size = SIGSTKSZ;
352 ss.ss_sp = xmalloc ((unsigned) ss.ss_size);
353 ss.ss_flags = 0;
354 if (sigaltstack (&ss, NULL) < 0)
356 /* Oops - sigaltstack exists but doesn't work. We can't
357 install the overflow detector, but should gracefully treat
358 it as though sigaltstack doesn't exist. For example, this
359 happens when compiled with Linux 2.1 headers but run
360 against Linux 2.0 kernel. */
361 free (ss.ss_sp);
362 if (errno == ENOSYS)
363 return;
364 error (EXIT_FAILURE, errno, "sigaltstack");
368 #elif HAVE_SIGSTACK
371 struct sigstack ss;
372 char *stackbuf = xmalloc (2 * SIGSTKSZ);
374 ss.ss_sp = stackbuf + SIGSTKSZ;
375 ss.ss_onstack = 0;
376 if (sigstack (&ss, NULL) < 0)
378 /* Oops - sigstack exists but doesn't work. We can't install
379 the overflow detector, but should gracefully treat it as
380 though sigstack doesn't exist. For example, this happens
381 when compiled with Linux 2.1 headers but run against Linux
382 2.0 kernel. */
383 free (stackbuf);
384 if (errno == ENOSYS)
385 return;
386 error (EXIT_FAILURE, errno, "sigstack");
390 #else /* not HAVE_SIGSTACK */
392 Error - Do not know how to set up stack-ovf trap handler...
394 #endif /* not HAVE_SIGSTACK */
396 /* Arm the SIGSEGV signal handler. */
398 #if HAVE_SIGACTION && defined SA_ONSTACK
400 sigaction (SIGSEGV, NULL, &act);
401 # if HAVE_STRUCT_SIGACTION_SA_SIGACTION
402 act.sa_sigaction = sigsegv_handler;
403 # else /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
404 act.sa_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
405 # endif /* ! HAVE_STRUCT_SIGACTION_SA_SIGACTION */
406 sigemptyset (&act.sa_mask);
407 act.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO);
408 if (sigaction (SIGSEGV, &act, NULL) < 0)
409 error (EXIT_FAILURE, errno, "sigaction");
411 #else /* ! HAVE_SIGACTION */
413 vec.sv_handler = (RETSIGTYPE (*) (int)) sigsegv_handler;
414 vec.sv_mask = 0;
415 vec.sv_flags = (SV_ONSTACK | SV_RESETHAND);
416 if (sigvec (SIGSEGV, &vec, NULL) < 0)
417 error (EXIT_FAILURE, errno, "sigvec");
419 #endif /* ! HAVE_SIGACTION */