source: vendor/m4/1.4.8/src/stackovf.c

Last change on this file was 3090, checked in by bird, 18 years ago

m4 1.4.8

File size: 12.5 KB
Line 
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.
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 of the License, or
8 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301 USA
19 */
20
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.
24
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.
28
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:
34
35 1. The siginfo parameter (with siginfo.h, i.e., SVR4).
36
37 2. 4th "addr" parameter (assumed if struct sigcontext is defined,
38 i.e., SunOS 4.x/BSD).
39
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.
43
44 Jim Avera <jima@netcom.com> writes, on Tue, 5 Oct 93 19:27 PDT:
45
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.
49
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."
56
57 Jim Avera <jima@netcom.com> writes, on Fri, 24 Jun 94 12:14 PDT:
58
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."
63
64 Jim Avera <jima@netcom.com> writes, on Wed, 6 Jul 1994 14:41 PDT:
65
66 "When a stack overflow occurs, a SIGSEGV signal is sent, which by
67 default aborts the process with a core dump.
68
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." */
77
78#include "m4.h" /* stdlib.h, xmalloc() */
79
80#include <assert.h>
81#include <sys/time.h>
82#include <sys/resource.h>
83#include <signal.h>
84
85#if HAVE_SIGINFO_H
86# include <siginfo.h>
87#endif
88
89#ifndef SA_RESETHAND
90# define SA_RESETHAND 0
91#endif
92#ifndef SA_SIGINFO
93# define SA_SIGINFO 0
94#endif
95
96#ifndef SIGSTKSZ
97# define SIGSTKSZ 8192
98#endif
99
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. */
108
109#ifndef STACKOVF_DETECT
110# define STACKOVF_DETECT 16384
111#endif
112
113typedef void (*handler_t) (void);
114
115static const char *stackbot;
116static const char *stackend;
117static const char *arg0;
118static handler_t stackovf_handler;
119
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.
124
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.
130
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.
134
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. */
142
143#define PARAM_STACKOVF ((const char *) (1 + STACKOVF_DETECT))
144#define PARAM_NOSTACKOVF ((const char *) (2 + STACKOVF_DETECT))
145
146static void
147process_sigsegv (int signo, const char *p)
148{
149 long diff;
150 diff = (p - stackend);
151
152#ifdef DEBUG_STKOVF
153 {
154 char buf[140];
155
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));
159 }
160#endif
161
162 if (p != PARAM_NOSTACKOVF)
163 {
164 if ((long) sbrk (8192) == (long) -1)
165 {
166
167 /* sbrk failed. Assume the RLIMIT_VMEM prevents expansion even
168 if the stack limit has not been reached. */
169
170 write (2, "VMEM limit exceeded?\n", 21);
171 p = PARAM_STACKOVF;
172 }
173 if (diff >= -STACKOVF_DETECT && diff <= STACKOVF_DETECT)
174 {
175
176 /* The fault address is "sufficiently close" to the stack lim. */
177
178 p = PARAM_STACKOVF;
179 }
180 if (p == PARAM_STACKOVF)
181 {
182
183 /* We have determined that this is indeed a stack overflow. */
184
185 (*stackovf_handler) (); /* should call exit() */
186 }
187 }
188 if (p == NULL)
189 {
190 const char *cp;
191
192 cp = "\
193Memory bounds violation detected (SIGSEGV). Either a stack overflow\n\
194occurred, 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));
199 }
200
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. */
203
204 signal (signo, SIG_DFL);
205}
206
207#if HAVE_STRUCT_SIGACTION_SA_SIGACTION
208
209/* POSIX. */
210
211static void
212sigsegv_handler (int signo, siginfo_t *ip, void *context)
213{
214 process_sigsegv
215 (signo, (ip != NULL
216 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
217}
218
219#elif HAVE_SIGINFO_T
220
221/* SVR4. */
222
223static void
224sigsegv_handler (int signo, siginfo_t *ip)
225{
226 process_sigsegv
227 (signo, (ip != NULL
228 && ip->si_signo == SIGSEGV ? (char *) ip->si_addr : NULL));
229}
230
231#elif HAVE_SIGCONTEXT
232
233/* SunOS 4.x (and BSD?). (not tested) */
234
235static void
236sigsegv_handler (int signo, int code, struct sigcontext *scp, char *addr)
237{
238 process_sigsegv (signo, addr);
239}
240
241#else /* not HAVE_SIGCONTEXT */
242
243/* OS provides no information. */
244
245static void
246sigsegv_handler (int signo)
247{
248 process_sigsegv (signo, NULL);
249}
250
251#endif /* not HAVE_SIGCONTEXT */
252
253/* Arrange to trap a stack-overflow and call a specified handler. The
254 call is on a dedicated signal stack.
255
256 argv and envp are as passed to main().
257
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.
261
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. */
267
268void
269setup_stackovf_trap (char *const *argv, char *const *envp, handler_t handler)
270{
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
281
282Error - Do not know how to set up stack-ovf trap handler...
283
284#endif
285
286 arg0 = argv[0];
287 stackovf_handler = handler;
288
289 /* Calculate the approximate expected addr for a stack-ovf trap. */
290
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)
297 {
298
299 /* Grows toward increasing addresses. */
300
301 for (v = argv; (p = (char *) *v) != NULL; v++)
302 {
303 if (p < stackbot)
304 stackbot = p;
305 }
306 if ((char *) envp < stackbot)
307 stackbot = (char *) envp;
308 for (v = envp; (p = (char *) *v) != NULL; v++)
309 {
310 if (p < stackbot)
311 stackbot = p;
312 }
313 stackend = stackbot + stack_len;
314 }
315 else
316 {
317
318 /* The stack grows "downward" (toward decreasing addresses). */
319
320 for (v = argv; (p = (char *) *v) != NULL; v++)
321 {
322 if (p > stackbot)
323 stackbot = p;
324 }
325 if ((char *) envp > stackbot)
326 stackbot = (char *) envp;
327 for (v = envp; (p = (char *) *v) != NULL; v++)
328 {
329 if (p > stackbot)
330 stackbot = p;
331 }
332 stackend = stackbot - stack_len;
333 }
334
335 /* Allocate a separate signal-handler stack. */
336
337#if HAVE_SIGALTSTACK && (HAVE_SIGINFO_T || ! HAVE_SIGSTACK)
338
339 /* Use sigaltstack only if siginfo_t is available, unless there is no
340 choice. */
341
342 {
343 stack_t ss;
344
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)
349 {
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");
359 }
360 }
361
362#elif HAVE_SIGSTACK
363
364 {
365 struct sigstack ss;
366 char *stackbuf = xmalloc (2 * SIGSTKSZ);
367
368 ss.ss_sp = stackbuf + SIGSTKSZ;
369 ss.ss_onstack = 0;
370 if (sigstack (&ss, NULL) < 0)
371 {
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");
381 }
382 }
383
384#else /* not HAVE_SIGSTACK */
385
386Error - Do not know how to set up stack-ovf trap handler...
387
388#endif /* not HAVE_SIGSTACK */
389
390 /* Arm the SIGSEGV signal handler. */
391
392#if HAVE_SIGACTION && defined SA_ONSTACK
393
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");
404
405#else /* ! HAVE_SIGACTION */
406
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");
412
413#endif /* ! HAVE_SIGACTION */
414
415}
Note: See TracBrowser for help on using the repository browser.