diff --git a/gnulib-tests/Makefile.in b/gnulib-tests/Makefile.in index aa979f4..194cc7d 100644 --- a/gnulib-tests/Makefile.in +++ b/gnulib-tests/Makefile.in @@ -3100,7 +3100,7 @@ test_argmatch_LDADD = $(LDADD) @LIBINTL@ $(LIB_MBRTOWC) test_bind_LDADD = $(LDADD) @LIBSOCKET@ $(INET_PTON_LIB) test_btowc_LDADD = $(LDADD) $(LIB_SETLOCALE) test_c_ctype_LDADD = $(LDADD) $(LIB_SETLOCALE) -test_c_stack_LDADD = $(LDADD) $(LIBCSTACK) @LIBINTL@ +test_c_stack_LDADD = $(LDADD) -lsigsegv @LIBINTL@ test_c_strcasecmp_LDADD = $(LDADD) $(LIB_SETLOCALE) test_c_strncasecmp_LDADD = $(LDADD) $(LIB_SETLOCALE) test_connect_LDADD = $(LDADD) @LIBSOCKET@ $(INET_PTON_LIB) diff --git a/gnulib-tests/gnulib.mk b/gnulib-tests/gnulib.mk index b1dc6f9..ba0594e 100644 --- a/gnulib-tests/gnulib.mk +++ b/gnulib-tests/gnulib.mk @@ -209,7 +209,7 @@ EXTRA_DIST += test-c-ctype.c macros.h TESTS += test-c-stack.sh test-c-stack2.sh TESTS_ENVIRONMENT += LIBSIGSEGV='@LIBSIGSEGV@' check_PROGRAMS += test-c-stack -test_c_stack_LDADD = $(LDADD) $(LIBCSTACK) @LIBINTL@ +test_c_stack_LDADD = $(LDADD) -lsigsegv @LIBINTL@ MOSTLYCLEANFILES += t-c-stack.tmp t-c-stack2.tmp EXTRA_DIST += test-c-stack.c test-c-stack.sh test-c-stack2.sh macros.h diff --git a/lib/c-stack.c b/lib/c-stack.c index 3aea16a..b0e5cb1 100644 --- a/lib/c-stack.c +++ b/lib/c-stack.c @@ -1,6 +1,6 @@ /* Stack overflow handling. - Copyright (C) 2002, 2004, 2006, 2008-2020 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, 2008-2021 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 @@ -37,54 +37,28 @@ #include "c-stack.h" -#include "gettext.h" -#define _(msgid) gettext (msgid) - #include #include - #include -#if ! HAVE_STACK_T && ! defined stack_t -typedef struct sigaltstack stack_t; -#endif - -#include #include #include #include - -/* Pre-2008 POSIX declared ucontext_t in ucontext.h instead of signal.h. */ -#if HAVE_UCONTEXT_H -# include -#endif - #include #if DEBUG # include #endif -/* Use libsigsegv only if needed; kernels like Solaris can detect - stack overflow without the overhead of an external library. */ -#define USE_LIBSIGSEGV (!HAVE_XSI_STACK_OVERFLOW_HEURISTIC && HAVE_LIBSIGSEGV) - -#if USE_LIBSIGSEGV -# include -#endif +#include #include "exitfail.h" -#include "ignore-value.h" -#include "intprops.h" #include "getprogname.h" +#include "ignore-value.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 +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#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 @@ -106,9 +80,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. */ @@ -118,12 +89,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; @@ -164,12 +129,8 @@ 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 __GNUC_PREREQ (4, 6) +# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) # pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" # endif @@ -240,132 +201,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 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; - -# 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) diff --git a/m4/c-stack.m4 b/m4/c-stack.m4 index 85107f4..7cccb06 100644 --- a/m4/c-stack.m4 +++ b/m4/c-stack.m4 @@ -1,376 +1,21 @@ # Check prerequisites for compiling lib/c-stack.c. -# Copyright (C) 2002-2004, 2008-2020 Free Software Foundation, Inc. +# Copyright (C) 2002-2004, 2008-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Written by Paul Eggert. -# serial 20 - -AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC], - [ - AC_REQUIRE([AC_CANONICAL_HOST]) - AC_CHECK_FUNCS_ONCE([setrlimit]) - AC_CHECK_HEADERS_ONCE([ucontext.h]) - - dnl List of signals that are sent when an invalid virtual memory address - dnl is accessed, or when the stack overflows. - dnl Either { SIGSEGV } or { SIGSEGV, SIGBUS }. - case "$host_os" in - sunos4* | freebsd* | dragonfly* | openbsd* | mirbsd* | netbsd* | kfreebsd* | knetbsd*) # BSD systems - FAULT_YIELDS_SIGBUS=1 ;; - hpux*) # HP-UX - FAULT_YIELDS_SIGBUS=1 ;; - macos* | darwin*) # Mac OS X - FAULT_YIELDS_SIGBUS=1 ;; - gnu*) # Hurd - FAULT_YIELDS_SIGBUS=1 ;; - *) - FAULT_YIELDS_SIGBUS=0 ;; - esac - AC_DEFINE_UNQUOTED([FAULT_YIELDS_SIGBUS], [$FAULT_YIELDS_SIGBUS], - [Define to 1 if an invalid memory address access may yield a SIGBUS.]) - - AC_CACHE_CHECK([for working C stack overflow detection], - [gl_cv_sys_stack_overflow_works], - [AC_RUN_IFELSE([AC_LANG_SOURCE( - [[ - #include - #include - #if HAVE_SETRLIMIT - # include - # include - # include - #endif - #ifndef SIGSTKSZ - # define SIGSTKSZ 16384 - #endif - - static union - { - char buffer[2 * SIGSTKSZ]; - long double ld; - long u; - void *p; - } alternate_signal_stack; - - static void - segv_handler (int signo) - { - _exit (0); - } - - static int - c_stack_action () - { - stack_t st; - struct sigaction act; - int r; - - st.ss_flags = 0; - /* Use the midpoint to avoid Irix sigaltstack bug. */ - st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ; - st.ss_size = SIGSTKSZ; - r = sigaltstack (&st, 0); - if (r != 0) - return 1; - - sigemptyset (&act.sa_mask); - act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; - act.sa_handler = segv_handler; - #if FAULT_YIELDS_SIGBUS - if (sigaction (SIGBUS, &act, 0) < 0) - return 2; - #endif - if (sigaction (SIGSEGV, &act, 0) < 0) - return 3; - return 0; - } - static volatile int * - recurse_1 (volatile int n, volatile int *p) - { - if (n >= 0) - *recurse_1 (n + 1, p) += n; - return p; - } - static int - recurse (volatile int n) - { - int sum = 0; - return *recurse_1 (n, &sum); - } - int - main () - { - int result; - #if HAVE_SETRLIMIT && defined RLIMIT_STACK - /* Before starting the endless recursion, try to be friendly - to the user's machine. On some Linux 2.2.x systems, there - is no stack limit for user processes at all. We don't want - to kill such systems. */ - struct rlimit rl; - rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */ - setrlimit (RLIMIT_STACK, &rl); - #endif - - result = c_stack_action (); - if (result != 0) - return result; - return recurse (0); - } - ]])], - [gl_cv_sys_stack_overflow_works=yes], - [gl_cv_sys_stack_overflow_works=no], - [case "$host_os" in - # Guess no on native Windows. - mingw*) gl_cv_sys_stack_overflow_works="guessing no" ;; - *) gl_cv_sys_stack_overflow_works=cross-compiling ;; - esac - ]) - ]) - - if test "$gl_cv_sys_stack_overflow_works" = yes; then - AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], [1], - [Define to 1 if extending the stack slightly past the limit causes - a SIGSEGV which can be handled on an alternate stack established - with sigaltstack.]) - - dnl The ss_sp field of a stack_t is, according to POSIX, the lowest address - dnl of the memory block designated as an alternate stack. But IRIX 5.3 - dnl interprets it as the highest address! - AC_CACHE_CHECK([for correct stack_t interpretation], - [gl_cv_sigaltstack_low_base], [ - AC_RUN_IFELSE([ - AC_LANG_SOURCE([[ -#include -#include -#if HAVE_SYS_SIGNAL_H -# include -#endif -#ifndef SIGSTKSZ -# define SIGSTKSZ 16384 -#endif -volatile char *stack_lower_bound; -volatile char *stack_upper_bound; -static void check_stack_location (volatile char *addr) -{ - if (addr >= stack_lower_bound && addr <= stack_upper_bound) - exit (0); - else - exit (1); -} -static void stackoverflow_handler (int sig) -{ - char dummy; - check_stack_location (&dummy); -} -int main () -{ - char mystack[2 * SIGSTKSZ]; - stack_t altstack; - struct sigaction action; - /* Install the alternate stack. */ - altstack.ss_sp = mystack + SIGSTKSZ; - altstack.ss_size = SIGSTKSZ; - stack_lower_bound = (char *) altstack.ss_sp; - stack_upper_bound = (char *) altstack.ss_sp + altstack.ss_size - 1; - altstack.ss_flags = 0; /* no SS_DISABLE */ - if (sigaltstack (&altstack, NULL) < 0) - exit (2); - /* Install the SIGSEGV handler. */ - sigemptyset (&action.sa_mask); - action.sa_handler = &stackoverflow_handler; - action.sa_flags = SA_ONSTACK; - if (sigaction (SIGSEGV, &action, (struct sigaction *) NULL) < 0) - exit(3); - /* Provoke a SIGSEGV. */ - raise (SIGSEGV); - exit (4); -}]])], - [gl_cv_sigaltstack_low_base=yes], - [gl_cv_sigaltstack_low_base=no], - [gl_cv_sigaltstack_low_base=cross-compiling])]) - if test "$gl_cv_sigaltstack_low_base" = no; then - AC_DEFINE([SIGALTSTACK_SS_REVERSED], [1], - [Define if sigaltstack() interprets the stack_t.ss_sp field - incorrectly, as the highest address of the alternate stack range - rather than as the lowest address.]) - fi - - AC_CACHE_CHECK([for precise C stack overflow detection], - [gl_cv_sys_xsi_stack_overflow_heuristic], - [dnl On Linux/sparc64 (both in 32-bit and 64-bit mode), it would be wrong - dnl to set HAVE_XSI_STACK_OVERFLOW_HEURISTIC to 1, because the third - dnl argument passed to the segv_handler is a 'struct sigcontext *', not - dnl an 'ucontext_t *'. It would lead to a failure of test-c-stack2.sh. - case "${host_os}--${host_cpu}" in - linux*--sparc*) - gl_cv_sys_xsi_stack_overflow_heuristic=no - ;; - *) - AC_RUN_IFELSE( - [AC_LANG_SOURCE( - [[ - #include - #include - #if HAVE_UCONTEXT_H - # include - #endif - #if HAVE_SETRLIMIT - # include - # include - # include - #endif - #ifndef SIGSTKSZ - # define SIGSTKSZ 16384 - #endif - - static union - { - char buffer[2 * SIGSTKSZ]; - long double ld; - long u; - void *p; - } alternate_signal_stack; - - #if STACK_DIRECTION - # define find_stack_direction(ptr) STACK_DIRECTION - #else - static int - find_stack_direction (char const *addr) - { - char dummy; - return (! addr ? find_stack_direction (&dummy) - : addr < &dummy ? 1 : -1); - } - #endif - - static void - segv_handler (int signo, siginfo_t *info, void *context) - { - if (0 < info->si_code) - { - /* For XSI heuristics to work, we need uc_stack to - describe the interrupted stack (as on Solaris), and - not the currently executing stack (as on Linux). */ - ucontext_t const *user_context = context; - char const *stack_min = user_context->uc_stack.ss_sp; - size_t stack_size = user_context->uc_stack.ss_size; - char const *faulting_address = info->si_addr; - size_t s = faulting_address - stack_min; - size_t page_size = sysconf (_SC_PAGESIZE); - if (find_stack_direction (0) < 0) - s += page_size; - if (s < stack_size + page_size) - _exit (0); - _exit (4); - } - _exit (5); - } - - static int - c_stack_action () - { - stack_t st; - struct sigaction act; - int r; - - st.ss_flags = 0; - /* Use the midpoint to avoid Irix sigaltstack bug. */ - st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ; - st.ss_size = SIGSTKSZ; - r = sigaltstack (&st, 0); - if (r != 0) - return 1; - - sigemptyset (&act.sa_mask); - act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO; - act.sa_sigaction = segv_handler; - #if FAULT_YIELDS_SIGBUS - if (sigaction (SIGBUS, &act, 0) < 0) - return 2; - #endif - if (sigaction (SIGSEGV, &act, 0) < 0) - return 3; - return 0; - } - static volatile int * - recurse_1 (volatile int n, volatile int *p) - { - if (n >= 0) - *recurse_1 (n + 1, p) += n; - return p; - } - static int - recurse (volatile int n) - { - int sum = 0; - return *recurse_1 (n, &sum); - } - int - main () - { - int result; - #if HAVE_SETRLIMIT && defined RLIMIT_STACK - /* Before starting the endless recursion, try to be friendly - to the user's machine. On some Linux 2.2.x systems, there - is no stack limit for user processes at all. We don't want - to kill such systems. */ - struct rlimit rl; - rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */ - setrlimit (RLIMIT_STACK, &rl); - #endif - - result = c_stack_action (); - if (result != 0) - return result; - return recurse (0); - } - ]])], - [gl_cv_sys_xsi_stack_overflow_heuristic=yes], - [gl_cv_sys_xsi_stack_overflow_heuristic=no], - [gl_cv_sys_xsi_stack_overflow_heuristic=cross-compiling]) - ;; - esac - ]) - - if test "$gl_cv_sys_xsi_stack_overflow_heuristic" = yes; then - AC_DEFINE([HAVE_XSI_STACK_OVERFLOW_HEURISTIC], [1], - [Define to 1 if extending the stack slightly past the limit causes - a SIGSEGV, and an alternate stack can be established with sigaltstack, - and the signal handler is passed a context that specifies the - run time stack. This behavior is defined by POSIX 1003.1-2001 - with the X/Open System Interface (XSI) option - and is a standardized way to implement a SEGV-based stack - overflow detection heuristic.]) - fi - fi]) - - -AC_DEFUN([gl_PREREQ_C_STACK], - [AC_REQUIRE([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC]) - - AC_CHECK_FUNCS_ONCE([sigaltstack]) - AC_CHECK_DECLS([sigaltstack], , , [[#include ]]) - - AC_CHECK_HEADERS_ONCE([ucontext.h]) - - AC_CHECK_TYPES([stack_t], , , [[#include ]]) - - dnl c-stack does not need -lsigsegv if the system has XSI heuristics. - if test "$gl_cv_sys_xsi_stack_overflow_heuristic" != yes; then - gl_LIBSIGSEGV - if test "$gl_cv_lib_sigsegv" = yes; then - AC_SUBST([LIBCSTACK], [$LIBSIGSEGV]) - AC_SUBST([LTLIBCSTACK], [$LTLIBSIGSEGV]) - fi - fi -]) +# serial 23 AC_DEFUN([gl_C_STACK], [ - dnl Prerequisites of lib/c-stack.c. - gl_PREREQ_C_STACK + dnl 'c-stack' needs -lsigsegv if and only if the 'sigsegv' module needs it. + if test "$with_libsigsegv" = yes; then + if test "$gl_cv_lib_sigsegv" = yes; then + AC_SUBST([LIBCSTACK], [$LIBSIGSEGV]) + AC_SUBST([LTLIBCSTACK], [$LTLIBSIGSEGV]) + fi + fi ]) diff --git a/src/Makefile.am b/src/Makefile.am index 1aebcf7..055ee17 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,7 +45,7 @@ LDADD = \ ../lib/libgreputils.a $(LIBINTL) ../lib/libgreputils.a $(LIBICONV) \ $(LIBTHREAD) -grep_LDADD = $(LDADD) $(PCRE_LIBS) $(LIBCSTACK) +grep_LDADD = $(LDADD) $(PCRE_LIBS) -lsigsegv localedir = $(datadir)/locale AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib diff --git a/src/Makefile.in b/src/Makefile.in index 20c43b5..a276db8 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1541,7 +1541,7 @@ LDADD = \ ../lib/libgreputils.a $(LIBINTL) ../lib/libgreputils.a $(LIBICONV) \ $(LIBTHREAD) -grep_LDADD = $(LDADD) $(PCRE_LIBS) $(LIBCSTACK) +grep_LDADD = $(LDADD) $(PCRE_LIBS) -lsigsegv AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib EXTRA_DIST = egrep.sh CLEANFILES = egrep fgrep *-t