| 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 <[email protected]>, 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 | }
|
|---|