mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-06-15 15:25:49 +00:00
87fb310b69
In documentation and comments, be more like POSIX in terminology involving multithreading. Explain the distinction between multithreaded process vs multithreaded program. Change “program” to “process” when the latter wording is more accurate or informative. Simplify the wording for the constraints on processes that use unlocked I/O. Change “multithread-safe” to “thread-safe”. Change “thread-safety” to “thread safety”. However, do not change “multithreaded” to “multi-threaded” even though there are some uses of both spellinga, as there are a whole bunch of uses of “multithreaded”, also in identifier names; perhaps Gnulib should even standardize on “multithreaded” (not “multi-threaded”), contra POSIX.
218 lines
6.8 KiB
C
218 lines
6.8 KiB
C
/* Stack overflow handling.
|
|
|
|
Copyright (C) 2002, 2004, 2006, 2008-2026 Free Software Foundation, Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/* Written by Paul Eggert. */
|
|
|
|
/* NOTES:
|
|
|
|
A program that uses alloca, dynamic arrays, or large local
|
|
variables may extend the stack by more than a page at a time. If
|
|
so, when the stack overflows the operating system may not detect
|
|
the overflow until the program uses the array, and this module may
|
|
incorrectly report a program error instead of a stack overflow.
|
|
|
|
To avoid this problem, allocate only small objects on the stack; a
|
|
program should be OK if it limits single allocations to a page or
|
|
less. Allocate larger arrays in static storage, or on the heap
|
|
(e.g., with malloc). Yes, this is a pain, but we don't know of any
|
|
better solution that is portable.
|
|
|
|
No attempt has been made to deal with multithreaded processes. */
|
|
|
|
#include <config.h>
|
|
|
|
#include "c-stack.h"
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <signal.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#if DEBUG
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#include <sigsegv.h>
|
|
|
|
#include "exitfail.h"
|
|
#include "idx.h"
|
|
#include "ignore-value.h"
|
|
|
|
#include "gettext.h"
|
|
#define _(msgid) dgettext (GNULIB_TEXT_DOMAIN, msgid)
|
|
|
|
/* Here we need the original abort() function. (Printing a stack trace
|
|
from within a signal handler is not going to work in most cases anyway.) */
|
|
#undef abort
|
|
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY
|
|
|
|
/* Storage for the alternate signal stack.
|
|
64 KiB is not too large for Gnulib-using apps, and is large enough
|
|
for all known platforms. Smaller sizes may run into trouble.
|
|
For example, libsigsegv 2.6 through 2.8 have a bug where some
|
|
architectures use more than the Linux default of an 8 KiB alternate
|
|
stack when deciding if a fault was caused by stack overflow. */
|
|
static max_align_t alternate_signal_stack[(64 * 1024
|
|
+ sizeof (max_align_t) - 1)
|
|
/ sizeof (max_align_t)];
|
|
|
|
/* The user-specified action to take when a SEGV-related program error
|
|
or stack overflow occurs. */
|
|
static _GL_ASYNC_SAFE void (* volatile segv_action) (int);
|
|
|
|
/* Translated messages for program errors and stack overflow. Do not
|
|
translate them in the signal handler, since gettext is not
|
|
async-signal-safe. */
|
|
static char const * volatile program_error_message;
|
|
static char const * volatile stack_overflow_message;
|
|
|
|
/* Output an error message, then exit with status EXIT_FAILURE if it
|
|
appears to have been a stack overflow, or with a core dump
|
|
otherwise. This function is async-signal-safe. */
|
|
|
|
static char const * volatile progname;
|
|
|
|
static _GL_ASYNC_SAFE _Noreturn void
|
|
die (int signo)
|
|
{
|
|
segv_action (signo);
|
|
char const *message = signo ? program_error_message : stack_overflow_message;
|
|
|
|
/* If the message is short, write it all at once to avoid
|
|
interleaving with other messages. Avoid writev as it is not
|
|
documented to be async-signal-safe. */
|
|
size_t prognamelen = strlen (progname);
|
|
size_t messagelen = strlen (message);
|
|
static char const separator[] = {':', ' '};
|
|
char buf[sizeof alternate_signal_stack / 16 + sizeof separator];
|
|
idx_t buflen;
|
|
if (prognamelen + messagelen < sizeof buf - sizeof separator)
|
|
{
|
|
char *p = mempcpy (buf, progname, prognamelen);
|
|
p = mempcpy (p, separator, sizeof separator);
|
|
p = mempcpy (p, message, messagelen);
|
|
*p++ = '\n';
|
|
buflen = p - buf;
|
|
}
|
|
else
|
|
{
|
|
ignore_value (write (STDERR_FILENO, progname, prognamelen));
|
|
ignore_value (write (STDERR_FILENO, separator, sizeof separator));
|
|
ignore_value (write (STDERR_FILENO, message, messagelen));
|
|
buf[0] = '\n';
|
|
buflen = 1;
|
|
}
|
|
ignore_value (write (STDERR_FILENO, buf, buflen));
|
|
|
|
if (! signo)
|
|
_exit (exit_failure);
|
|
raise (signo);
|
|
abort ();
|
|
}
|
|
|
|
static _GL_ASYNC_SAFE void
|
|
null_action (_GL_UNUSED int signo)
|
|
{
|
|
}
|
|
|
|
/* Pacify GCC 9.3.1, which otherwise would complain about segv_handler. */
|
|
# if _GL_GNUC_PREREQ (4, 6)
|
|
# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
|
|
# endif
|
|
|
|
/* Nonzero if general segv handler could not be installed. */
|
|
static volatile int segv_handler_missing;
|
|
|
|
/* Handle a segmentation violation and exit if it cannot be stack
|
|
overflow. This function is async-signal-safe. */
|
|
|
|
static _GL_ASYNC_SAFE int
|
|
segv_handler (_GL_UNUSED void *address, int serious)
|
|
{
|
|
# if DEBUG
|
|
{
|
|
char buf[1024];
|
|
int saved_errno = errno;
|
|
ignore_value (write (STDERR_FILENO, buf,
|
|
sprintf (buf, "segv_handler serious=%d\n", serious)));
|
|
errno = saved_errno;
|
|
}
|
|
# endif
|
|
|
|
/* If this fault is not serious, return 0 to let the stack overflow
|
|
handler take a shot at it. */
|
|
if (!serious)
|
|
return 0;
|
|
die (SIGSEGV);
|
|
}
|
|
|
|
/* Handle a segmentation violation that is likely to be a stack
|
|
overflow and exit. This function is async-signal-safe. */
|
|
|
|
static _GL_ASYNC_SAFE _Noreturn void
|
|
overflow_handler (int emergency, _GL_UNUSED stackoverflow_context_t context)
|
|
{
|
|
# if DEBUG
|
|
{
|
|
char buf[1024];
|
|
ignore_value (write (STDERR_FILENO, buf,
|
|
sprintf (buf, ("overflow_handler emergency=%d"
|
|
" segv_handler_missing=%d\n"),
|
|
emergency, segv_handler_missing)));
|
|
}
|
|
# endif
|
|
|
|
die ((!emergency || segv_handler_missing) ? 0 : SIGSEGV);
|
|
}
|
|
|
|
int
|
|
c_stack_action (_GL_ASYNC_SAFE void (*action) (int))
|
|
{
|
|
segv_action = action ? action : null_action;
|
|
program_error_message = _("program error");
|
|
stack_overflow_message = _("stack overflow");
|
|
progname = getprogname ();
|
|
|
|
/* Always install the overflow handler. */
|
|
if (stackoverflow_install_handler (overflow_handler,
|
|
alternate_signal_stack,
|
|
sizeof alternate_signal_stack))
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
/* Try installing a general handler; if it fails, then treat all
|
|
segv as stack overflow. */
|
|
segv_handler_missing = sigsegv_install_handler (segv_handler);
|
|
return 0;
|
|
}
|
|
|
|
#else /* !HAVE_STACK_OVERFLOW_RECOVERY */
|
|
|
|
int
|
|
c_stack_action (_GL_ASYNC_SAFE void (*action) (_GL_UNUSED int) )
|
|
{
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
#endif
|