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 |
|
---|
113 | typedef void (*handler_t) (void);
|
---|
114 |
|
---|
115 | static const char *stackbot;
|
---|
116 | static const char *stackend;
|
---|
117 | static const char *arg0;
|
---|
118 | static 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 |
|
---|
146 | static void
|
---|
147 | process_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 = "\
|
---|
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));
|
---|
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 |
|
---|
211 | static void
|
---|
212 | sigsegv_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 |
|
---|
223 | static void
|
---|
224 | sigsegv_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 |
|
---|
235 | static void
|
---|
236 | sigsegv_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 |
|
---|
245 | static void
|
---|
246 | sigsegv_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 |
|
---|
268 | void
|
---|
269 | setup_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 |
|
---|
282 | Error - 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 |
|
---|
386 | Error - 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 | }
|
---|