diff options
author | Bruno Haible <bruno@clisp.org> | 2021-05-17 03:36:35 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2021-05-18 19:38:36 +0200 |
commit | 40e416dd1d2197754f43497dd4c0d250fd20da91 (patch) | |
tree | dd5eae1e19c9fb6ba104ba04d55e82ef03bc08f2 /lib/c-stack.c | |
parent | 8e99f24c0931a38880c6ee9b8287c7da80b0036b (diff) | |
download | gnulib-40e416dd1d2197754f43497dd4c0d250fd20da91.tar.gz |
c-stack: Support catching stack overflow on most platforms without libsigsegv.
* lib/c-stack.c: Remove all Solaris-specific code. Include <sigsegv.h>
always.
* m4/c-stack.m4: Remove all Solaris-specific code.
(gl_C_STACK): Test $with_libsigsegv from module 'sigsegv'.
* modules/c-stack (Files): Remove m4/libsigsegv.m4.
(Depends-on): Add sigsegv. Remove havelib, intprops, inttypes, mempcpy,
sigaction, stdbool.
Diffstat (limited to 'lib/c-stack.c')
-rw-r--r-- | lib/c-stack.c | 188 |
1 files changed, 7 insertions, 181 deletions
diff --git a/lib/c-stack.c b/lib/c-stack.c index 3e8cd25651..1965bdd0c5 100644 --- a/lib/c-stack.c +++ b/lib/c-stack.c @@ -39,59 +39,27 @@ #include <errno.h> #include <inttypes.h> - #include <signal.h> -#if ! HAVE_STACK_T && ! defined stack_t -typedef struct sigaltstack stack_t; -#endif - -#include <stdbool.h> #include <stddef.h> #include <stdlib.h> #include <string.h> - -/* Pre-2008 POSIX declared ucontext_t in ucontext.h instead of signal.h. */ -#if HAVE_UCONTEXT_H -# include <ucontext.h> -#endif - #include <unistd.h> #if DEBUG # include <stdio.h> #endif +#include <sigsegv.h> + +#include "exitfail.h" +#include "getprogname.h" #include "idx.h" +#include "ignore-value.h" #include "gettext.h" #define _(msgid) gettext (msgid) -#if HAVE_LIBSIGSEGV -# include <sigsegv.h> -#endif - -/* Use libsigsegv only if needed; kernels like Solaris can detect - stack overflow without the overhead of an external library. - However, BOGUS_SI_ADDR_ON_STACK_OVERFLOW indicates a buggy - Solaris kernel, and a too-small LIBSIGSEGV_VERSION indicates a - libsigsegv so old that it does not work around the bug. */ -#define USE_LIBSIGSEGV (!HAVE_XSI_STACK_OVERFLOW_HEURISTIC && HAVE_LIBSIGSEGV \ - && ! (BOGUS_SI_ADDR_UPON_STACK_OVERFLOW \ - && LIBSIGSEGV_VERSION < 0x020D)) - -#include "exitfail.h" -#include "ignore-value.h" -#include "intprops.h" -#include "getprogname.h" - -#if defined SA_ONSTACK && defined SA_SIGINFO -# define SIGINFO_WORKS 1 -#else -# define SIGINFO_WORKS 0 -# ifndef SA_ONSTACK -# define SA_ONSTACK 0 -# endif -#endif +#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 @@ -113,9 +81,6 @@ static _GL_ASYNC_SAFE void (* volatile segv_action) (int); static char const * volatile program_error_message; static char const * volatile stack_overflow_message; -#if (USE_LIBSIGSEGV \ - || (HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING)) - /* 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. */ @@ -125,12 +90,6 @@ static char const * volatile progname; static _GL_ASYNC_SAFE _Noreturn void die (int signo) { -# if !SIGINFO_WORKS && !USE_LIBSIGSEGV - /* We can't easily determine whether it is a stack overflow; so - assume that the rest of our program is perfect (!) and that - this segmentation violation is a stack overflow. */ - signo = 0; -# endif segv_action (signo); char const *message = signo ? program_error_message : stack_overflow_message; @@ -171,10 +130,6 @@ null_action (int signo _GL_UNUSED) { } -#endif /* SIGALTSTACK || LIBSIGSEGV */ - -#if USE_LIBSIGSEGV - /* Pacify GCC 9.3.1, which otherwise would complain about segv_handler. */ # if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) # pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" @@ -247,136 +202,7 @@ c_stack_action (_GL_ASYNC_SAFE void (*action) (int)) return 0; } -#elif HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING - -# if SIGINFO_WORKS - -static size_t volatile page_size; - -/* Handle a segmentation violation and exit. This function is - async-signal-safe. */ - -static _GL_ASYNC_SAFE _Noreturn void -segv_handler (int signo, siginfo_t *info, void *context _GL_UNUSED) -{ - /* Clear SIGNO if it seems to have been a stack overflow. */ - - /* If si_code is nonpositive, something like raise (SIGSEGV) occurred - so it cannot be a stack overflow. */ - bool cannot_be_stack_overflow = info->si_code <= 0; - - /* An unaligned address cannot be a stack overflow. */ -# if FAULT_YIELDS_SIGBUS - cannot_be_stack_overflow |= signo == SIGBUS && info->si_code == BUS_ADRALN; -# endif - - /* If we can't easily determine that it is not a stack overflow, - assume that the rest of our program is perfect (!) and that - this segmentation violation is a stack overflow. - - Note that although both Linux and Solaris provide - sigaltstack, SA_ONSTACK, and SA_SIGINFO, currently only - Solaris satisfies the XSI heuristic. This is because - Solaris populates uc_stack with the details of the - interrupted stack, while Linux populates it with the details - of the current stack. */ - if (!cannot_be_stack_overflow) - { -# if BOGUS_SI_ADDR_UPON_STACK_OVERFLOW - signo = 0; -# else - /* If the faulting address is within the stack, or within one - page of the stack, assume that it is a stack overflow. */ - uintptr_t faulting_address = (uintptr_t) info->si_addr; - - /* On all platforms we know of, the first page is not in the - stack to catch null pointer dereferening. However, all other - pages might be in the stack. */ - void *stack_base = (void *) (uintptr_t) page_size; - uintptr_t stack_size = 0; stack_size -= page_size; -# if HAVE_XSI_STACK_OVERFLOW_HEURISTIC - /* Tighten the stack bounds via the XSI heuristic. */ - ucontext_t const *user_context = context; - stack_base = user_context->uc_stack.ss_sp; - stack_size = user_context->uc_stack.ss_size; -# endif - uintptr_t base = (uintptr_t) stack_base, - lo = (INT_SUBTRACT_WRAPV (base, page_size, &lo) || lo < page_size - ? page_size : lo), - hi = ((INT_ADD_WRAPV (base, stack_size, &hi) - || INT_ADD_WRAPV (hi, page_size - 1, &hi)) - ? UINTPTR_MAX : hi); - if (lo <= faulting_address && faulting_address <= hi) - signo = 0; -# endif - -# if DEBUG - { - char buf[1024]; - ignore_value (write (STDERR_FILENO, buf, - sprintf (buf, - ("segv_handler code=%d fault=%p base=%p" - " size=0x%zx page=0x%zx signo=%d\n"), - info->si_code, info->si_addr, stack_base, - stack_size, page_size, signo))); - } -# endif - } - - die (signo); -} -# endif - -int -c_stack_action (_GL_ASYNC_SAFE void (*action) (int)) -{ - stack_t st; - st.ss_flags = 0; - st.ss_sp = alternate_signal_stack; - st.ss_size = sizeof alternate_signal_stack; -# if SIGALTSTACK_SS_REVERSED - /* Irix mistakenly treats ss_sp as the upper bound, rather than - lower bound, of the alternate stack. */ - st.ss_size -= sizeof (void *); - char *ss_sp = st.ss_sp; - st.ss_sp = ss_sp + st.ss_size; -# endif - int r = sigaltstack (&st, NULL); - if (r != 0) - return r; - - segv_action = action ? action : null_action; - program_error_message = _("program error"); - stack_overflow_message = _("stack overflow"); - progname = getprogname (); - -# if SIGINFO_WORKS - page_size = sysconf (_SC_PAGESIZE); -# endif - - struct sigaction act; - sigemptyset (&act.sa_mask); - -# if SIGINFO_WORKS - /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but - this is not true on Solaris 8 at least. It doesn't hurt to use - SA_NODEFER here, so leave it in. */ - act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO; - act.sa_sigaction = segv_handler; -# else - act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; - act.sa_handler = die; -# endif - -# if FAULT_YIELDS_SIGBUS - if (sigaction (SIGBUS, &act, NULL) < 0) - return -1; -# endif - return sigaction (SIGSEGV, &act, NULL); -} - -#else /* ! (USE_LIBSIGSEGV - || (HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING)) */ +#else /* !HAVE_STACK_OVERFLOW_RECOVERY */ int c_stack_action (_GL_ASYNC_SAFE void (*action) (int) _GL_UNUSED) |