1
0
mirror of https://git.savannah.gnu.org/git/gnulib.git synced 2025-08-08 17:22:05 +03:00

c-stack: improve checking if !libsigsegv

If SIGINFO_WORKS, do not treat a null pointer dereference as if it
were a stack overflow.  Use uintptr_t and INT_ADD_WRAPV to avoid
unlikely pointer overflow.  Also, fix some obsolete code and typos.
I found these problems while looking into this bug report:
https://lists.gnu.org/r/grep-devel/2020-09/msg00053.html
* lib/c-stack.c: Include c-stack.h first, to test interface.
Include inttypes.h for UINTPTR_MAX, stdbool.h, stddef.h for
max_align_t, intprops.h for INT_ADD_WRAPV.
(USE_LIBSIGSEGV): New macro; use it to simplify later code.
(SIGSTKSZ): Simplify setup.  Work around libsigsegv bug only
for libsigsegv 2.8 and earlier since the bug should be fixed
after that.
(alternate_signal_stack): Use max_align_t instead of doing it by hand.
(segv_handler, overflow_handler, segv_handler) [DEBUG]:
Assume sprintf returns byte count; this assumption is safe now.
(page_size): New static volatile variable, since sysconf isn’t
documented to be async-signal-safe on Solaris.  This variable is
present and used if (!USE_LIBSIGSEGV && HAVE_SIGALTSTACK &&
HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING &&
SIGINFO_WORKS).
(segv_handler): Use it if present.  Never report null pointer
dereference as a stack overflow.  Check for (unlikely) unsigned
and/or pointer overflow.
* m4/c-stack.m4 (AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC):
Rename cache variables to gl_cv_sys_stack_overflow_works
and gl_cv_sys_xsi_stack_overflow_heuristic.
All uses changed.
(gl_PREREQ_C_STACK): Do not require AC_FUNC_ALLOCA, since
c-stack no longer uses STACK_DIRECTION.
Do not check for unistd.h, since we depend on unistd.
Fix shell typo ‘$"ac_cv_sys_xsi_stack_overflow_heuristic"’.
* modules/c-stack (Depends-on): Sort.  Add intprops, inttypes,
stdbool, stddef.
This commit is contained in:
Paul Eggert
2020-09-20 11:48:17 -07:00
parent 17a6bcd48f
commit 8ba9126d00
4 changed files with 139 additions and 78 deletions

View File

@@ -1,3 +1,40 @@
2020-09-20 Paul Eggert <eggert@cs.ucla.edu>
c-stack: improve checking if !libsigsegv
If SIGINFO_WORKS, do not treat a null pointer dereference as if it
were a stack overflow. Use uintptr_t and INT_ADD_WRAPV to avoid
unlikely pointer overflow. Also, fix some obsolete code and typos.
I found these problems while looking into this bug report:
https://lists.gnu.org/r/grep-devel/2020-09/msg00053.html
* lib/c-stack.c: Include c-stack.h first, to test interface.
Include inttypes.h for UINTPTR_MAX, stdbool.h, stddef.h for
max_align_t, intprops.h for INT_ADD_WRAPV.
(USE_LIBSIGSEGV): New macro; use it to simplify later code.
(SIGSTKSZ): Simplify setup. Work around libsigsegv bug only
for libsigsegv 2.8 and earlier since the bug should be fixed
after that.
(alternate_signal_stack): Use max_align_t instead of doing it by hand.
(segv_handler, overflow_handler, segv_handler) [DEBUG]:
Assume sprintf returns byte count; this assumption is safe now.
(page_size): New static volatile variable, since sysconf isnt
documented to be async-signal-safe on Solaris. This variable is
present and used if (!USE_LIBSIGSEGV && HAVE_SIGALTSTACK &&
HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING &&
SIGINFO_WORKS).
(segv_handler): Use it if present. Never report null pointer
dereference as a stack overflow. Check for (unlikely) unsigned
and/or pointer overflow.
* m4/c-stack.m4 (AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC):
Rename cache variables to gl_cv_sys_stack_overflow_works
and gl_cv_sys_xsi_stack_overflow_heuristic.
All uses changed.
(gl_PREREQ_C_STACK): Do not require AC_FUNC_ALLOCA, since
c-stack no longer uses STACK_DIRECTION.
Do not check for unistd.h, since we depend on unistd.
Fix shell typo $"ac_cv_sys_xsi_stack_overflow_heuristic".
* modules/c-stack (Depends-on): Sort. Add intprops, inttypes,
stdbool, stddef.
2020-09-20 Bruno Haible <bruno@clisp.org> 2020-09-20 Bruno Haible <bruno@clisp.org>
Revert now-unnecessary override of config.guess on Alpine Linux 3.10. Revert now-unnecessary override of config.guess on Alpine Linux 3.10.

View File

@@ -35,30 +35,25 @@
#include <config.h> #include <config.h>
#include "c-stack.h"
#include "gettext.h" #include "gettext.h"
#define _(msgid) gettext (msgid) #define _(msgid) gettext (msgid)
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <signal.h> #include <signal.h>
#if ! HAVE_STACK_T && ! defined stack_t #if ! HAVE_STACK_T && ! defined stack_t
typedef struct sigaltstack stack_t; typedef struct sigaltstack stack_t;
#endif #endif
#ifndef SIGSTKSZ
# define SIGSTKSZ 16384
#elif HAVE_LIBSIGSEGV && SIGSTKSZ < 16384
/* libsigsegv 2.6 through 2.8 have a bug where some architectures use
more than the Linux default of an 8k alternate stack when deciding
if a fault was caused by stack overflow. */
# undef SIGSTKSZ
# define SIGSTKSZ 16384
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
/* Posix 2001 declares ucontext_t in <ucontext.h>, Posix 200x in /* Pre-2008 POSIX declared ucontext_t in ucontext.h instead of signal.h. */
<signal.h>. */
#if HAVE_UCONTEXT_H #if HAVE_UCONTEXT_H
# include <ucontext.h> # include <ucontext.h>
#endif #endif
@@ -69,13 +64,26 @@ typedef struct sigaltstack stack_t;
# include <stdio.h> # include <stdio.h>
#endif #endif
#if HAVE_LIBSIGSEGV /* 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 <sigsegv.h> # include <sigsegv.h>
/* libsigsegv 2.6 through 2.8 have a bug where some architectures use
more than the Linux default of an 8k alternate stack when deciding
if a fault was caused by stack overflow. */
# if LIBSIGSEGV_VERSION <= 0x0208 && SIGSTKSZ < 16384
# undef SIGSTKSZ
# endif
#endif
#ifndef SIGSTKSZ
# define SIGSTKSZ 16384
#endif #endif
#include "c-stack.h"
#include "exitfail.h" #include "exitfail.h"
#include "ignore-value.h" #include "ignore-value.h"
#include "intprops.h"
#include "getprogname.h" #include "getprogname.h"
#if defined SA_ONSTACK && defined SA_SIGINFO #if defined SA_ONSTACK && defined SA_SIGINFO
@@ -97,7 +105,7 @@ static _GL_ASYNC_SAFE void (* volatile segv_action) (int);
static char const * volatile program_error_message; static char const * volatile program_error_message;
static char const * volatile stack_overflow_message; static char const * volatile stack_overflow_message;
#if ((HAVE_LIBSIGSEGV && ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC) \ #if (USE_LIBSIGSEGV \
|| (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \ || (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \
&& HAVE_STACK_OVERFLOW_HANDLING)) && HAVE_STACK_OVERFLOW_HANDLING))
@@ -111,12 +119,12 @@ static _GL_ASYNC_SAFE _Noreturn void
die (int signo) die (int signo)
{ {
char const *message; char const *message;
#if !SIGINFO_WORKS && !HAVE_LIBSIGSEGV # if !SIGINFO_WORKS && !USE_LIBSIGSEGV
/* We can't easily determine whether it is a stack overflow; so /* We can't easily determine whether it is a stack overflow; so
assume that the rest of our program is perfect (!) and that assume that the rest of our program is perfect (!) and that
this segmentation violation is a stack overflow. */ this segmentation violation is a stack overflow. */
signo = 0; signo = 0;
#endif /* !SIGINFO_WORKS && !HAVE_LIBSIGSEGV */ # endif
segv_action (signo); segv_action (signo);
message = signo ? program_error_message : stack_overflow_message; message = signo ? program_error_message : stack_overflow_message;
ignore_value (write (STDERR_FILENO, progname, strlen (progname))); ignore_value (write (STDERR_FILENO, progname, strlen (progname)));
@@ -128,22 +136,12 @@ die (int signo)
raise (signo); raise (signo);
abort (); abort ();
} }
#endif
#if (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \
&& HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV
/* Storage for the alternate signal stack. */ /* Storage for the alternate signal stack. */
static union static union
{ {
char buffer[SIGSTKSZ]; char buffer[SIGSTKSZ];
max_align_t align;
/* These other members are for proper alignment. There's no
standard way to guarantee stack alignment, but this seems enough
in practice. */
long double ld;
long l;
void *p;
} alternate_signal_stack; } alternate_signal_stack;
static _GL_ASYNC_SAFE void static _GL_ASYNC_SAFE void
@@ -153,10 +151,7 @@ null_action (int signo _GL_UNUSED)
#endif /* SIGALTSTACK || LIBSIGSEGV */ #endif /* SIGALTSTACK || LIBSIGSEGV */
/* Only use libsigsegv if we need it; platforms like Solaris can #if USE_LIBSIGSEGV
detect stack overflow without the overhead of an external
library. */
#if HAVE_LIBSIGSEGV && ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
/* Nonzero if general segv handler could not be installed. */ /* Nonzero if general segv handler could not be installed. */
static volatile int segv_handler_missing; static volatile int segv_handler_missing;
@@ -171,8 +166,8 @@ segv_handler (void *address _GL_UNUSED, int serious)
{ {
char buf[1024]; char buf[1024];
int saved_errno = errno; int saved_errno = errno;
sprintf (buf, "segv_handler serious=%d\n", serious); ignore_value (write (STDERR_FILENO, buf,
ignore_value (write (STDERR_FILENO, buf, strlen (buf))); sprintf (buf, "segv_handler serious=%d\n", serious)));
errno = saved_errno; errno = saved_errno;
} }
# endif # endif
@@ -193,9 +188,10 @@ overflow_handler (int emergency, stackoverflow_context_t context _GL_UNUSED)
# if DEBUG # if DEBUG
{ {
char buf[1024]; char buf[1024];
sprintf (buf, "overflow_handler emergency=%d segv_handler_missing=%d\n", ignore_value (write (STDERR_FILENO, buf,
emergency, segv_handler_missing); sprintf (buf, ("overflow_handler emergency=%d"
ignore_value (write (STDERR_FILENO, buf, strlen (buf))); " segv_handler_missing=%d\n"),
emergency, segv_handler_missing)));
} }
# endif # endif
@@ -228,6 +224,8 @@ c_stack_action (_GL_ASYNC_SAFE void (*action) (int))
# if SIGINFO_WORKS # if SIGINFO_WORKS
static size_t volatile page_size;
/* Handle a segmentation violation and exit. This function is /* Handle a segmentation violation and exit. This function is
async-signal-safe. */ async-signal-safe. */
@@ -235,8 +233,17 @@ static _GL_ASYNC_SAFE _Noreturn void
segv_handler (int signo, siginfo_t *info, void *context _GL_UNUSED) segv_handler (int signo, siginfo_t *info, void *context _GL_UNUSED)
{ {
/* Clear SIGNO if it seems to have been a stack overflow. */ /* Clear SIGNO if it seems to have been a stack overflow. */
# if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
/* We can't easily determine whether it is a stack overflow; so /* 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 assume that the rest of our program is perfect (!) and that
this segmentation violation is a stack overflow. this segmentation violation is a stack overflow.
@@ -246,33 +253,44 @@ segv_handler (int signo, siginfo_t *info, void *context _GL_UNUSED)
Solaris populates uc_stack with the details of the Solaris populates uc_stack with the details of the
interrupted stack, while Linux populates it with the details interrupted stack, while Linux populates it with the details
of the current stack. */ of the current stack. */
signo = 0; if (!cannot_be_stack_overflow)
# else
if (0 < info->si_code)
{ {
/* If the faulting address is within the stack, or within one /* If the faulting address is within the stack, or within one
page of the stack, assume that it is a stack overflow. */ 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; ucontext_t const *user_context = context;
char const *stack_base = user_context->uc_stack.ss_sp; stack_base = user_context->uc_stack.ss_sp;
size_t stack_size = user_context->uc_stack.ss_size; stack_size = user_context->uc_stack.ss_size;
char const *faulting_address = info->si_addr; # endif
size_t page_size = sysconf (_SC_PAGESIZE); uintptr_t base = (uintptr_t) stack_base,
size_t s = faulting_address - stack_base + page_size; lo = (INT_SUBTRACT_WRAPV (base, page_size, &lo) || lo < page_size
if (s < stack_size + 2 * 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; signo = 0;
# if DEBUG # if DEBUG
{ {
char buf[1024]; char buf[1024];
sprintf (buf, ignore_value (write (STDERR_FILENO, buf,
"segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n", sprintf (buf,
faulting_address, stack_base, (unsigned long) stack_size, ("segv_handler code=%d fault=%p base=%p"
(unsigned long) page_size, signo); " size=0x%zx page=0x%zx signo=%d\n"),
ignore_value (write (STDERR_FILENO, buf, strlen (buf))); info->si_code, info->si_addr, stack_base,
stack_size, page_size, signo)));
} }
# endif
}
# endif # endif
}
die (signo); die (signo);
} }
@@ -303,6 +321,10 @@ c_stack_action (_GL_ASYNC_SAFE void (*action) (int))
stack_overflow_message = _("stack overflow"); stack_overflow_message = _("stack overflow");
progname = getprogname (); progname = getprogname ();
# if SIGINFO_WORKS
page_size = sysconf (_SC_PAGESIZE);
# endif
sigemptyset (&act.sa_mask); sigemptyset (&act.sa_mask);
# if SIGINFO_WORKS # if SIGINFO_WORKS
@@ -323,8 +345,9 @@ c_stack_action (_GL_ASYNC_SAFE void (*action) (int))
return sigaction (SIGSEGV, &act, NULL); return sigaction (SIGSEGV, &act, NULL);
} }
#else /* ! ((HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK #else /* ! (USE_LIBSIGSEGV
&& HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV) */ || (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
&& HAVE_STACK_OVERFLOW_HANDLING)) */
int int
c_stack_action (_GL_ASYNC_SAFE void (*action) (int) _GL_UNUSED) c_stack_action (_GL_ASYNC_SAFE void (*action) (int) _GL_UNUSED)

View File

@@ -7,7 +7,7 @@
# Written by Paul Eggert. # Written by Paul Eggert.
# serial 17 # serial 18
AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC], AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC],
[ [
@@ -34,7 +34,7 @@ AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC],
[Define to 1 if an invalid memory address access may yield a SIGBUS.]) [Define to 1 if an invalid memory address access may yield a SIGBUS.])
AC_CACHE_CHECK([for working C stack overflow detection], AC_CACHE_CHECK([for working C stack overflow detection],
[ac_cv_sys_stack_overflow_works], [gl_cv_sys_stack_overflow_works],
[AC_RUN_IFELSE([AC_LANG_SOURCE( [AC_RUN_IFELSE([AC_LANG_SOURCE(
[[ [[
#include <unistd.h> #include <unistd.h>
@@ -121,17 +121,17 @@ AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC],
return recurse (0); return recurse (0);
} }
]])], ]])],
[ac_cv_sys_stack_overflow_works=yes], [gl_cv_sys_stack_overflow_works=yes],
[ac_cv_sys_stack_overflow_works=no], [gl_cv_sys_stack_overflow_works=no],
[case "$host_os" in [case "$host_os" in
# Guess no on native Windows. # Guess no on native Windows.
mingw*) ac_cv_sys_stack_overflow_works="guessing no" ;; mingw*) gl_cv_sys_stack_overflow_works="guessing no" ;;
*) ac_cv_sys_stack_overflow_works=cross-compiling ;; *) gl_cv_sys_stack_overflow_works=cross-compiling ;;
esac esac
]) ])
]) ])
if test "$ac_cv_sys_stack_overflow_works" = yes; then if test "$gl_cv_sys_stack_overflow_works" = yes; then
AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], [1], AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], [1],
[Define to 1 if extending the stack slightly past the limit causes [Define to 1 if extending the stack slightly past the limit causes
a SIGSEGV which can be handled on an alternate stack established a SIGSEGV which can be handled on an alternate stack established
@@ -200,14 +200,14 @@ int main ()
fi fi
AC_CACHE_CHECK([for precise C stack overflow detection], AC_CACHE_CHECK([for precise C stack overflow detection],
[ac_cv_sys_xsi_stack_overflow_heuristic], [gl_cv_sys_xsi_stack_overflow_heuristic],
[dnl On Linux/sparc64 (both in 32-bit and 64-bit mode), it would be wrong [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 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 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. dnl an 'ucontext_t *'. It would lead to a failure of test-c-stack2.sh.
case "${host_os}--${host_cpu}" in case "${host_os}--${host_cpu}" in
linux*--sparc*) linux*--sparc*)
ac_cv_sys_xsi_stack_overflow_heuristic=no gl_cv_sys_xsi_stack_overflow_heuristic=no
;; ;;
*) *)
AC_RUN_IFELSE( AC_RUN_IFELSE(
@@ -329,14 +329,14 @@ int main ()
return recurse (0); return recurse (0);
} }
]])], ]])],
[ac_cv_sys_xsi_stack_overflow_heuristic=yes], [gl_cv_sys_xsi_stack_overflow_heuristic=yes],
[ac_cv_sys_xsi_stack_overflow_heuristic=no], [gl_cv_sys_xsi_stack_overflow_heuristic=no],
[ac_cv_sys_xsi_stack_overflow_heuristic=cross-compiling]) [gl_cv_sys_xsi_stack_overflow_heuristic=cross-compiling])
;; ;;
esac esac
]) ])
if test $ac_cv_sys_xsi_stack_overflow_heuristic = yes; then if test "$gl_cv_sys_xsi_stack_overflow_heuristic" = yes; then
AC_DEFINE([HAVE_XSI_STACK_OVERFLOW_HEURISTIC], [1], AC_DEFINE([HAVE_XSI_STACK_OVERFLOW_HEURISTIC], [1],
[Define to 1 if extending the stack slightly past the limit causes [Define to 1 if extending the stack slightly past the limit causes
a SIGSEGV, and an alternate stack can be established with sigaltstack, a SIGSEGV, and an alternate stack can be established with sigaltstack,
@@ -353,19 +353,16 @@ AC_DEFUN([gl_PREREQ_C_STACK],
[AC_REQUIRE([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC]) [AC_REQUIRE([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC])
AC_REQUIRE([gl_LIBSIGSEGV]) AC_REQUIRE([gl_LIBSIGSEGV])
# for STACK_DIRECTION
AC_REQUIRE([AC_FUNC_ALLOCA])
AC_CHECK_FUNCS_ONCE([sigaltstack]) AC_CHECK_FUNCS_ONCE([sigaltstack])
AC_CHECK_DECLS([sigaltstack], , , [[#include <signal.h>]]) AC_CHECK_DECLS([sigaltstack], , , [[#include <signal.h>]])
AC_CHECK_HEADERS_ONCE([unistd.h ucontext.h]) AC_CHECK_HEADERS_ONCE([ucontext.h])
AC_CHECK_TYPES([stack_t], , , [#include <signal.h>]) AC_CHECK_TYPES([stack_t], , , [#include <signal.h>])
dnl c-stack does not need -lsigsegv if the system has XSI heuristics. dnl c-stack does not need -lsigsegv if the system has XSI heuristics.
if test "$gl_cv_lib_sigsegv" = yes \ if test "$gl_cv_lib_sigsegv" = yes \
&& test $"ac_cv_sys_xsi_stack_overflow_heuristic" != yes ; then && test "$gl_cv_sys_xsi_stack_overflow_heuristic" != yes; then
AC_SUBST([LIBCSTACK], [$LIBSIGSEGV]) AC_SUBST([LIBCSTACK], [$LIBSIGSEGV])
AC_SUBST([LTLIBCSTACK], [$LTLIBSIGSEGV]) AC_SUBST([LTLIBCSTACK], [$LTLIBSIGSEGV])
fi fi

View File

@@ -7,15 +7,19 @@ lib/c-stack.c
m4/c-stack.m4 m4/c-stack.m4
Depends-on: Depends-on:
gettext-h
errno errno
exitfail exitfail
getprogname
gettext-h
ignore-value ignore-value
unistd intprops
inttypes
libsigsegv
raise raise
sigaction sigaction
libsigsegv stdbool
getprogname stddef
unistd
configure.ac: configure.ac:
gl_C_STACK gl_C_STACK