mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-04-28 06:33:36 +00:00
1463 lines
49 KiB
C
1463 lines
49 KiB
C
/* Page fault handling library.
|
|
Copyright (C) 1993-2026 Free Software Foundation, Inc.
|
|
Copyright (C) 2018 Nylon Chen <nylon7@andestech.com>
|
|
|
|
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 2 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 Bruno Haible and Nylon Chen. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include "sigsegv.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h> /* declares perror */
|
|
#include <stdint.h> /* defines uintptr_t */
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#if HAVE_GETRLIMIT
|
|
# include <sys/resource.h> /* declares struct rlimit */
|
|
#endif
|
|
|
|
#ifdef __OpenBSD__
|
|
# include <sys/param.h> /* defines macro OpenBSD */
|
|
#endif
|
|
|
|
|
|
/* Version number. */
|
|
int libsigsegv_version = LIBSIGSEGV_VERSION;
|
|
|
|
|
|
/* ======================= Fault handler information ======================= */
|
|
|
|
/* Define:
|
|
|
|
SIGSEGV_FAULT_HANDLER_ARGLIST
|
|
is the argument list for the actual fault handler.
|
|
|
|
and if available (optional):
|
|
|
|
SIGSEGV_FAULT_ADDRESS
|
|
is a macro for fetching the fault address.
|
|
|
|
SIGSEGV_FAULT_CONTEXT
|
|
is a macro giving a pointer to the entire fault context (i.e.
|
|
the register set etc.).
|
|
|
|
SIGSEGV_FAULT_STACKPOINTER
|
|
is a macro for fetching the stackpointer at the moment the fault
|
|
occurred.
|
|
*/
|
|
|
|
#if defined __linux__ && !defined __ANDROID__ /* Linux */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __alpha__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/alpha/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/alpha/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/alpha/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are actually the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.sc_regs[30]
|
|
|
|
# elif defined __arm64__ /* 64-bit */
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/aarch64/sys/ucontext.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/aarch64/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are actually the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.sp
|
|
|
|
# elif defined __arm__ || defined __armhf__ /* 32-bit */
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/arm/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/arm/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/arm/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are actually the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.arm_sp
|
|
|
|
# elif defined __cris__
|
|
|
|
/* See glibc-ports/sysdeps/unix/sysv/linux/cris/sys/ucontext.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc-ports/sysdeps/unix/sysv/linux/cris/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are actually the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.usp
|
|
|
|
# elif defined __hppa__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/hppa/sys/ucontext.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/hppa/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are actually the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.sc_gr[30]
|
|
|
|
# elif defined __x86_64__ /* 64 bit registers */
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/x86_64/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in
|
|
glibc/sysdeps/unix/sysv/linux/x86/bits/sigcontext.h
|
|
(see also <asm/sigcontext.h>)
|
|
are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_RSP]
|
|
|
|
# elif defined __i386__ /* 32 bit registers */
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/i386/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
|
|
and the 'struct sigcontext_ia32' defined in <asm/sigcontext32.h>
|
|
are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_ESP]
|
|
/* same value as ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_UESP] */
|
|
|
|
# elif defined __ia64__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/ia64/sys/ucontext.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/ia64/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in
|
|
glibc/sysdeps/unix/sysv/linux/ia64/bits/sigcontext.h
|
|
(see also <asm/sigcontext.h>)
|
|
are actually the same. */
|
|
|
|
/* IA-64 has two stack pointers, one that grows down, called $r12, and one
|
|
that grows up, called $bsp/$bspstore. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.sc_gr[12]
|
|
|
|
/* It would be better to access $bspstore instead of $bsp but I don't know
|
|
where to find it in 'struct sigcontext'. Anyway, it doesn't matter
|
|
because $bsp and $bspstore never differ by more than ca. 1 KB. */
|
|
# define SIGSEGV_FAULT_BSP_POINTER ((ucontext_t *) ucp)->uc_mcontext.sc_ar_bsp
|
|
|
|
# elif defined __loongarch__
|
|
|
|
/* See <sys/ucontext.h>.
|
|
Note that the 'mcontext_t' defined in <sys/ucontext.h>
|
|
and the 'struct sigcontext' defined in <bits/sigcontext.h>
|
|
(see also <asm/sigcontext.h>) are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.__gregs[3]
|
|
|
|
# elif defined __m68k__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/m68k/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/m68k/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/m68k/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are quite different types. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[R_SP]
|
|
|
|
# elif defined __mips__ || defined __mipsn32__ || defined __mips64__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/mips/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/mips/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in
|
|
glibc/sysdeps/unix/sysv/linux/mips/bits/sigcontext.h
|
|
(see also <asm/sigcontext.h>)
|
|
are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[29]
|
|
|
|
# elif defined __nds32__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/nds32/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/nds32/sigcontextinfo.h.
|
|
Both are found in <https://patches-gcc.linaro.org/cover/4409/> part 08/11
|
|
<https://sourceware.org/ml/libc-alpha/2018-05/msg00125.html>. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.nds32_sp
|
|
|
|
# elif defined __powerpc__ || defined __powerpc64__ || defined __powerpc64_elfv2__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/powerpc/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h,
|
|
the 'struct sigcontext' defined in <asm/sigcontext.h>,
|
|
and the 'struct pt_regs' defined in <asm/ptrace.h>
|
|
are quite different types. */
|
|
|
|
# if defined __powerpc64__ || defined __powerpc64_elfv2__ /* 64-bit */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gp_regs[1]
|
|
# else /* 32-bit */
|
|
# if MUSL_LIBC
|
|
/* musl libc has a different structure of ucontext_t in
|
|
musl/arch/powerpc/bits/signal.h. */
|
|
/* The glibc comments say:
|
|
"Different versions of the kernel have stored the registers on signal
|
|
delivery at different offsets from the ucontext struct. Programs should
|
|
thus use the uc_mcontext.uc_regs pointer to find where the registers are
|
|
actually stored." */
|
|
# if 0
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[1]
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_regs->gregs[1]
|
|
# endif
|
|
# else
|
|
/* Assume the structure of ucontext_t in
|
|
glibc/sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h. */
|
|
/* Because of the union, both definitions should be equivalent. */
|
|
# if 0
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.regs->gpr[1]
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.uc_regs->gregs[1]
|
|
# endif
|
|
# endif
|
|
# endif
|
|
|
|
# elif defined __riscv32__ || __riscv64__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/riscv/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in
|
|
glibc/sysdeps/unix/sysv/linux/riscv/bits/sigcontext.h
|
|
start with the same block of 32 general-purpose registers. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.__gregs[REG_SP]
|
|
|
|
# elif defined __s390__ || defined __s390x__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/s390/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/s390/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/s390/sys/ucontext.h
|
|
and the '_sigregs' type, indirect part of 'struct sigcontext', defined
|
|
in <asm/sigcontext.h>, are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[15]
|
|
|
|
# elif defined __sh__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/sh/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/sh/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/sh/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in <asm/sigcontext.h>
|
|
are effectively the same. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[15]
|
|
|
|
# elif defined __sparc__ || defined __sparc64__
|
|
|
|
/* See glibc/sysdeps/unix/sysv/linux/sparc/sys/ucontext.h
|
|
and the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/sparc/{sparc32,sparc64}/sigcontextinfo.h.
|
|
Note that the 'mcontext_t' defined in
|
|
glibc/sysdeps/unix/sysv/linux/sparc/sys/ucontext.h
|
|
and the 'struct sigcontext' defined in
|
|
glibc/sysdeps/unix/sysv/linux/sparc/bits/sigcontext.h
|
|
(see also <asm/sigcontext.h>)
|
|
are quite different types. */
|
|
|
|
# if defined __sparc64__/* 64-bit */
|
|
/* From linux-4.8.1/arch/sparc/kernel/signal_64.c, function setup_rt_frame, we
|
|
see that ucp is not an 'ucontext_t *' but rather a 'struct sigcontext *'
|
|
that happens to have the same value as sip (which is possible because a
|
|
'struct sigcontext' starts with 128 bytes room for the siginfo_t). */
|
|
# define SIGSEGV_FAULT_STACKPOINTER (((struct sigcontext *) ucp)->sigc_regs.u_regs[14] + 2047)
|
|
# else /* 32-bit */
|
|
/* From linux-4.8.1/arch/sparc/kernel/signal_32.c, function setup_rt_frame,
|
|
and linux-4.8.1/arch/sparc/kernel/signal32.c, function setup_rt_frame32, we
|
|
see that ucp is a 'struct pt_regs *' or 'struct pt_regs32 *', respectively.
|
|
In userland, this is a 'struct sigcontext *'. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((struct sigcontext *) ucp)->si_regs.u_regs[14]
|
|
# endif
|
|
|
|
/* The sip->si_addr field is correct for a normal fault, but unusable in case
|
|
of a stack overflow. What I observe (when running
|
|
tests/test-sigsegv-catch-stackoverflow1, with a printf right at the beginning
|
|
of sigsegv_handler) is that sip->si_addr is near 0:
|
|
- in 64-bit mode: sip->si_addr = 0x000000000000030F, and gdb shows me that
|
|
the fault occurs in an instruction 'stx %o3,[%fp+0x30f]' and %fp is 0.
|
|
In fact, all registers %l0..%l7 and %i0..%i7 are 0.
|
|
- in 32-bit mode: sip->si_addr = 0xFFFFFA64, and gdb shows me that
|
|
the fault occurs in an instruction 'st %g2,[%fp-1436]' and %fp is 0.
|
|
In fact, all registers %l0..%l7 and %i0..%i7 are 0.
|
|
Apparently when the stack overflow occurred, some trap has tried to move the
|
|
contents of the registers %l0..%l7 and %i0..%i7 (a "window" in SPARC
|
|
terminology) to the stack, did not succeed in doing this, replaced all these
|
|
register values with 0, and resumed execution at the fault location. This
|
|
time, due to %fp = 0, a different fault was triggered. Now it is impossible
|
|
to determine the real (previous) fault address because, even if know the
|
|
faulting instruction, the previous register values have been lost. */
|
|
# define BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
|
|
|
|
# else
|
|
|
|
/* When adding support for other CPUs here: */
|
|
|
|
/* For SIGSEGV_FAULT_HANDLER_ARGLIST, see the definition of SIGCONTEXT in
|
|
glibc/sysdeps/unix/sysv/linux/<cpu>/sigcontextinfo.h. */
|
|
|
|
/* For SIGSEGV_FAULT_STACKPOINTER, see the definition of GET_STACK in
|
|
glibc/sysdeps/unix/sysv/linux/<cpu>/sigcontextinfo.h. */
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __ANDROID__ /* Android */
|
|
/* A platform that supports the POSIX:2008 (XPG 7) way, without
|
|
'struct sigcontext' nor 'ucontext_t'. */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *context
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT context
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
#endif
|
|
|
|
#if defined __gnu_hurd__ /* Hurd */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, long code, struct sigcontext *scp
|
|
# define SIGSEGV_FAULT_ADDRESS (unsigned long) code
|
|
# define SIGSEGV_FAULT_CONTEXT scp
|
|
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
/* scp points to a 'struct sigcontext' (defined in
|
|
glibc/sysdeps/mach/hurd/x86_64/bits/sigcontext.h).
|
|
The registers, at the moment the signal occurred, get pushed on the kernel
|
|
stack through gnumach/x86_64/locore.S:alltraps. They are denoted by a
|
|
'struct i386_saved_state' (defined in gnumach/i386/i386/thread.h).
|
|
Upon invocation of the Mach interface function thread_get_state
|
|
<https://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html>
|
|
(= __thread_get_state in glibc), defined in gnumach/kern/thread.c,
|
|
the function thread_getstatus, defined in gnumach/i386/i386/pcb.c, copies the
|
|
register values in a different arrangement into a 'struct i386_thread_state',
|
|
defined in gnumach/i386/include/mach/i386/thread_status.h. (Different
|
|
arrangement: trapno, err get dropped; different order of r8...r15; also rsp
|
|
gets set to 0.)
|
|
This 'struct i386_thread_state' is actually the 'basic' part of a
|
|
'struct machine_thread_all_state', defined in
|
|
glibc/sysdeps/mach/x86/thread_state.h.
|
|
From there, the function _hurd_setup_sighandler, defined in
|
|
glibc/sysdeps/mach/hurd/x86/trampoline.c,
|
|
1. sets rsp to the same value as ursp,
|
|
2. copies the 'struct i386_thread_state' into the appropriate part of a
|
|
'struct sigcontext', defined in
|
|
glibc/sysdeps/mach/hurd/x86_64/bits/sigcontext.h. */
|
|
/* Both sc_rsp and sc_ursp have the same value.
|
|
It appears more reliable to use sc_ursp because sc_rsp is marked as
|
|
"not used". */
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_ursp
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
/* scp points to a 'struct sigcontext' (defined in
|
|
glibc/sysdeps/mach/hurd/i386/bits/sigcontext.h).
|
|
The registers, at the moment the signal occurred, get pushed on the kernel
|
|
stack through gnumach/i386/i386/locore.S:alltraps. They are denoted by a
|
|
'struct i386_saved_state' (defined in gnumach/i386/i386/thread.h).
|
|
Upon invocation of the Mach interface function thread_get_state
|
|
<https://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html>
|
|
(= __thread_get_state in glibc), defined in gnumach/kern/thread.c,
|
|
the function thread_getstatus, defined in gnumach/i386/i386/pcb.c, copies the
|
|
register values in a different arrangement into a 'struct i386_thread_state',
|
|
defined in gnumach/i386/include/mach/i386/thread_status.h. (Different
|
|
arrangement: trapno, err get dropped; also esp gets set to 0.)
|
|
This 'struct i386_thread_state' is actually the 'basic' part of a
|
|
'struct machine_thread_all_state', defined in
|
|
glibc/sysdeps/mach/x86/thread_state.h.
|
|
From there, the function _hurd_setup_sighandler, defined in
|
|
glibc/sysdeps/mach/hurd/x86/trampoline.c,
|
|
1. sets esp to the same value as uesp,
|
|
2. copies the 'struct i386_thread_state' into the appropriate part of a
|
|
'struct sigcontext', defined in
|
|
glibc/sysdeps/mach/hurd/i386/bits/sigcontext.h. */
|
|
/* Both sc_esp and sc_uesp have the same value.
|
|
It appears more reliable to use sc_uesp because sc_esp is marked as
|
|
"not used". */
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_uesp
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ /* GNU/kFreeBSD, FreeBSD */
|
|
|
|
# if defined __arm__ || defined __armhf__ || (defined __arm64__ || defined __aarch64__)
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
|
|
# if defined __arm64__ || defined __aarch64__ /* 64-bit */
|
|
|
|
/* See sys/arm64/include/ucontext.h. */
|
|
|
|
# if defined __CHERI_PURE_CAPABILITY__
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.mc_capregs.cap_sp
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.mc_gpregs.gp_sp
|
|
# endif
|
|
|
|
# elif defined __arm__ || defined __armhf__ /* 32-bit */
|
|
|
|
/* See sys/arm/include/ucontext.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.__gregs[_REG_SP]
|
|
|
|
# endif
|
|
|
|
# else
|
|
|
|
/* On FreeBSD 12, both of these approaches work. On FreeBSD derivatives, the
|
|
first one has more chances to work. */
|
|
# if 1
|
|
/* Use signal handlers without SA_SIGINFO. */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp, void *addr
|
|
# define SIGSEGV_FAULT_ADDRESS addr
|
|
# define SIGSEGV_FAULT_CONTEXT scp
|
|
|
|
/* See sys/x86/include/signal.h. */
|
|
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_rsp
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_esp
|
|
|
|
# endif
|
|
|
|
# else
|
|
/* Use signal handlers with SA_SIGINFO. */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *scp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((struct sigcontext *) scp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
/* See sys/x86/include/signal.h. */
|
|
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((struct sigcontext *) scp)->sc_rsp
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((struct sigcontext *) scp)->sc_esp
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __NetBSD__ /* NetBSD */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
/* _UC_MACHINE_SP is a platform independent macro.
|
|
Defined in <machine/mcontext.h>, see
|
|
https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/$arch/include/mcontext.h
|
|
Supported on alpha, amd64, i386, ia64, m68k, mips, powerpc, sparc since
|
|
NetBSD 2.0.
|
|
On i386, _UC_MACHINE_SP is the same as ->uc_mcontext.__gregs[_REG_UESP],
|
|
and apparently the same value as ->uc_mcontext.__gregs[_REG_ESP]. */
|
|
# ifdef _UC_MACHINE_SP
|
|
# define SIGSEGV_FAULT_STACKPOINTER _UC_MACHINE_SP ((ucontext_t *) ucp)
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __OpenBSD__ /* OpenBSD */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, struct sigcontext *scp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT scp
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __alpha__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/alpha/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs[30]
|
|
|
|
# elif defined __arm__ || defined __armhf__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/arm/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_usr_sp
|
|
|
|
# elif defined __hppa__ || defined __hppa64__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/hppa/include/signal.h
|
|
and
|
|
openbsd-src/sys/arch/hppa64/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs[30]
|
|
|
|
# elif defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/amd64/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_rsp
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/i386/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_esp
|
|
|
|
# elif defined __m68k__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/m68k/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_sp
|
|
|
|
# elif defined __m88k__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/m88k/include/signal.h
|
|
and the definition of 'struct reg' in
|
|
openbsd-src/sys/arch/m88k/include/reg.h. */
|
|
|
|
# if OpenBSD >= 201211 /* OpenBSD version >= 5.2 */
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs[31]
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs.r[31]
|
|
# endif
|
|
|
|
# elif defined __mips__ || defined __mipsn32__ || defined __mips64__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/mips64/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs[29]
|
|
|
|
# elif defined __powerpc64__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/powerpc64/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_sp
|
|
|
|
# elif defined __powerpc__
|
|
|
|
/* See the definition of 'struct sigcontext' and 'struct trapframe' in
|
|
openbsd-src/sys/arch/powerpc/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_frame.fixreg[1]
|
|
|
|
# elif defined __sh__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/sh/include/signal.h
|
|
and the definition of 'struct reg' in
|
|
openbsd-src/sys/arch/sh/include/reg.h. */
|
|
|
|
# if OpenBSD >= 201211 /* OpenBSD version >= 5.2 */
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_reg[20-15]
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_reg.r_r15
|
|
# endif
|
|
|
|
# elif defined __sparc__ || defined __sparc64__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/sparc/include/signal.h
|
|
and
|
|
openbsd-src/sys/arch/sparc64/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_sp
|
|
|
|
# elif defined __vax__
|
|
|
|
/* See the definition of 'struct sigcontext' in
|
|
openbsd-src/sys/arch/vax/include/signal.h. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER scp->sc_sp
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if (defined __APPLE__ && defined __MACH__) /* macOS */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __x86_64__
|
|
|
|
/* See the definitions of
|
|
- 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
|
|
- 'struct __darwin_mcontext64' in <i386/_mcontext.h>, and
|
|
- 'struct __darwin_x86_thread_state64' in <mach/i386/_structs.h>. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->__ss.__rsp
|
|
|
|
# elif defined __i386__
|
|
|
|
/* See the definitions of
|
|
- 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
|
|
- 'struct __darwin_mcontext32' in <i386/_mcontext.h>, and
|
|
- 'struct __darwin_i386_thread_state' in <mach/i386/_structs.h>. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->__ss.__esp
|
|
|
|
# elif defined __arm64__
|
|
|
|
/* See the definitions of
|
|
- 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_types/_ucontext.h>,
|
|
- 'struct __darwin_mcontext64' in <arm/_mcontext.h>, and
|
|
- 'struct __darwin_arm_thread_state64' in <mach/arm/_structs.h>. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->__ss.__sp
|
|
|
|
# elif defined __powerpc__
|
|
|
|
/* See the definitions of
|
|
- 'ucontext_t' and 'struct __darwin_ucontext' in <sys/_structs.h>,
|
|
- 'struct __darwin_mcontext' in <ppc/_structs.h>, and
|
|
- 'struct __darwin_ppc_thread_state' in <mach/ppc/_structs.h>. */
|
|
# if !(defined _STRUCT_MCONTEXT || defined _STRUCT_MCONTEXT32 || defined _STRUCT_MCONTEXT64)
|
|
/* Mac OS X 10.4 and earlier omitted the underscores. */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->ss.r1
|
|
# else
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->__ss.__r1
|
|
# endif
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined _AIX /* AIX */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __powerpc__ || defined __powerpc64__
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.jmp_context.gpr[1]
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __sun /* Solaris */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_RSP]
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[ESP]
|
|
|
|
# elif defined __sparc__ || defined __sparc64__
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gregs[REG_O6]
|
|
|
|
# if SOLARIS11
|
|
|
|
/* On Solaris 11.3/SPARC, both in 32-bit and 64-bit mode, when catching
|
|
stack overflow, the fault address is correct the first time, but is zero
|
|
or near zero the second time.
|
|
'truss tests/test-sigsegv-catch-stackoverflow1' shows it:
|
|
|
|
In 32-bit mode:
|
|
|
|
Incurred fault #6, FLTBOUNDS %pc = 0x000116E8
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0xFFB00000
|
|
Received signal #11, SIGSEGV [caught]
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0xFFB00000
|
|
then
|
|
Incurred fault #6, FLTBOUNDS %pc = 0x000116E8
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0x00000008
|
|
Received signal #11, SIGSEGV [caught]
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0x00000008
|
|
|
|
In 64-bit mode:
|
|
|
|
Incurred fault #6, FLTBOUNDS %pc = 0x100001C58
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0xFFFFFFFF7FF00000
|
|
Received signal #11, SIGSEGV [caught]
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0xFFFFFFFF7FF00000
|
|
then
|
|
Incurred fault #6, FLTBOUNDS %pc = 0x100001C58
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0x00000000
|
|
Received signal #11, SIGSEGV [caught]
|
|
siginfo: SIGSEGV SEGV_MAPERR addr=0x00000000
|
|
*/
|
|
# define BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __CYGWIN__ /* Cygwin */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
/* See the definition of 'ucontext_t' in <sys/ucontext.h> and
|
|
of 'struct __mcontext' in <cygwin/signal.h>. */
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.rsp
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.esp
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __HAIKU__ /* Haiku */
|
|
|
|
# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *ucp
|
|
# define SIGSEGV_FAULT_ADDRESS sip->si_addr
|
|
# define SIGSEGV_FAULT_CONTEXT ((ucontext_t *) ucp)
|
|
# define SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
|
|
# if defined __x86_64__
|
|
/* 64 bit registers */
|
|
|
|
/* See the definition of 'ucontext_t' in <signal.h> and
|
|
of 'struct vregs' in <arch/x86_64/signal.h>. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.rsp
|
|
|
|
# elif defined __i386__
|
|
/* 32 bit registers */
|
|
|
|
/* See the definition of 'ucontext_t' in <signal.h> and
|
|
of 'struct vregs' in <arch/x86/signal.h>. */
|
|
|
|
# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.esp
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
/* ========================================================================== */
|
|
|
|
/* List of signals that are sent when an invalid virtual memory address
|
|
is accessed, or when the stack overflows. */
|
|
#if defined __gnu_hurd__ \
|
|
|| defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
|
|
|| defined __NetBSD__ || defined __OpenBSD__ \
|
|
|| (defined __APPLE__ && defined __MACH__)
|
|
# if defined __CHERI__
|
|
# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \
|
|
{ int var; \
|
|
var = SIGSEGV; { body } \
|
|
var = SIGBUS; { body } \
|
|
var = SIGPROT; { body } \
|
|
}
|
|
# else
|
|
# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \
|
|
{ int var; \
|
|
var = SIGSEGV; { body } \
|
|
var = SIGBUS; { body } \
|
|
}
|
|
# endif
|
|
#else
|
|
# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \
|
|
{ int var; var = SIGSEGV; { body } }
|
|
#endif
|
|
|
|
/* ========================================================================== */
|
|
|
|
/* Determine the virtual memory area of a given address. */
|
|
#include "stackvma.h"
|
|
|
|
/* ========================================================================== */
|
|
|
|
/* On the average Unix platform, we define
|
|
|
|
HAVE_SIGSEGV_RECOVERY
|
|
if there is a fault-*.h include file which defines
|
|
SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS.
|
|
|
|
HAVE_STACK_OVERFLOW_RECOVERY
|
|
if HAVE_SIGALTSTACK is set and
|
|
at least two of the following are true:
|
|
A) There is a fault-*.h include file which defines
|
|
SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS.
|
|
B) There is a fault-*.h include file which defines
|
|
SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_STACKPOINTER.
|
|
C) There is a stackvma-*.c, other than stackvma-none.c, which
|
|
defines sigsegv_get_vma.
|
|
|
|
Why? Obviously, to catch stack overflow, we need an alternate signal
|
|
stack; this requires kernel support. But we also need to distinguish
|
|
(with a reasonable confidence) a stack overflow from a regular SIGSEGV.
|
|
If we have A) and B), we use the
|
|
Heuristic AB: If the fault address is near the stack pointer, it's a
|
|
stack overflow.
|
|
If we have A) and C), we use the
|
|
Heuristic AC: If the fault address is near and beyond the bottom of
|
|
the stack's virtual memory area, it's a stack overflow.
|
|
If we have B) and C), we use the
|
|
Heuristic BC: If the stack pointer is near the bottom of the stack's
|
|
virtual memory area, it's a stack overflow.
|
|
This heuristic comes in two flavours: On OSes which let the stack's
|
|
VMA grow continuously, we determine the bottom by use of getrlimit().
|
|
On OSes which preallocate the stack's VMA with its maximum size
|
|
(like BeOS), we use the stack's VMA directly.
|
|
*/
|
|
|
|
#if HAVE_SIGSEGV_RECOVERY \
|
|
&& !(defined SIGSEGV_FAULT_HANDLER_ARGLIST && defined SIGSEGV_FAULT_ADDRESS)
|
|
# error "You need to define SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAULT_ADDRESS before you can define HAVE_SIGSEGV_RECOVERY."
|
|
#endif
|
|
#if !HAVE_SIGSEGV_RECOVERY \
|
|
&& (defined SIGSEGV_FAULT_HANDLER_ARGLIST && defined SIGSEGV_FAULT_ADDRESS) \
|
|
&& !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__))
|
|
# if __GNUC__ || (__clang_major__ >= 4)
|
|
# warning "You can define HAVE_SIGSEGV_RECOVERY on this platform."
|
|
# else
|
|
# error "You can define HAVE_SIGSEGV_RECOVERY on this platform."
|
|
# endif
|
|
#endif
|
|
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY \
|
|
&& !(defined SIGSEGV_FAULT_ADDRESS + defined SIGSEGV_FAULT_STACKPOINTER + HAVE_STACKVMA >= 2)
|
|
# error "You need to define two of SIGSEGV_FAULT_ADDRESS, SIGSEGV_FAULT_STACKPOINTER, HAVE_STACKVMA, before you can define HAVE_STACK_OVERFLOW_RECOVERY."
|
|
#endif
|
|
#if !HAVE_STACK_OVERFLOW_RECOVERY \
|
|
&& (defined SIGSEGV_FAULT_ADDRESS + defined SIGSEGV_FAULT_STACKPOINTER + HAVE_STACKVMA >= 2) \
|
|
&& !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__)) \
|
|
&& !(defined __NetBSD__ && (defined __sparc__ || defined __sparc64__))
|
|
# if __GNUC__ || (__clang_major__ >= 4)
|
|
# warning "You can define HAVE_STACK_OVERFLOW_RECOVERY on this platform."
|
|
# else
|
|
# error "You can define HAVE_STACK_OVERFLOW_RECOVERY on this platform."
|
|
# endif
|
|
#endif
|
|
|
|
/* ========================================================================== */
|
|
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY
|
|
|
|
/* ======= Leaving a signal handler executing on the alternate stack ======= */
|
|
|
|
/* Platform dependent:
|
|
Leaving a signal handler executing on the alternate stack. */
|
|
static void sigsegv_reset_onstack_flag (void);
|
|
|
|
/* -------------------------- leave-sigaltstack.c -------------------------- */
|
|
|
|
# if defined __gnu_hurd__ \
|
|
|| defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \
|
|
|| defined __NetBSD__ || defined __OpenBSD__
|
|
|
|
static void
|
|
sigsegv_reset_onstack_flag (void)
|
|
{
|
|
stack_t ss;
|
|
|
|
if (sigaltstack (NULL, &ss) >= 0)
|
|
{
|
|
ss.ss_flags &= ~SS_ONSTACK;
|
|
sigaltstack (&ss, NULL);
|
|
}
|
|
}
|
|
|
|
/* --------------------------- leave-setcontext.c --------------------------- */
|
|
|
|
# elif defined __sun /* Solaris */
|
|
|
|
# include <ucontext.h>
|
|
|
|
static void
|
|
sigsegv_reset_onstack_flag (void)
|
|
{
|
|
ucontext_t uc;
|
|
|
|
if (getcontext (&uc) >= 0)
|
|
/* getcontext returns twice. We are interested in the returned context
|
|
only the first time, i.e. when the SS_ONSTACK bit is set. */
|
|
if (uc.uc_stack.ss_flags & SS_ONSTACK)
|
|
{
|
|
uc.uc_stack.ss_flags &= ~SS_ONSTACK;
|
|
/* Note that setcontext() does not refill uc. Therefore if
|
|
setcontext() keeps SS_ONSTACK set in the kernel, either
|
|
setcontext() will return -1 or getcontext() will return a
|
|
second time, with the SS_ONSTACK bit being cleared. */
|
|
setcontext (&uc);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------ leave-nop.c ------------------------------ */
|
|
|
|
# else
|
|
|
|
static void
|
|
sigsegv_reset_onstack_flag (void)
|
|
{
|
|
/* Nothing to do. sigaltstack() simply looks at the stack pointer,
|
|
therefore SS_ONSTACK is not sticky. */
|
|
}
|
|
|
|
# endif
|
|
|
|
/* ========================================================================== */
|
|
|
|
# if HAVE_STACKVMA
|
|
|
|
/* Address of the last byte belonging to the stack vma. */
|
|
static uintptr_t stack_top = 0;
|
|
|
|
/* Needs to be called once only. */
|
|
static void
|
|
remember_stack_top (void *some_variable_on_stack)
|
|
{
|
|
struct vma_struct vma;
|
|
|
|
if (sigsegv_get_vma ((uintptr_t) some_variable_on_stack, &vma) >= 0)
|
|
stack_top = vma.end - 1;
|
|
}
|
|
|
|
# endif /* HAVE_STACKVMA */
|
|
|
|
static stackoverflow_handler_t stk_user_handler = (stackoverflow_handler_t)NULL;
|
|
static uintptr_t stk_extra_stack;
|
|
static size_t stk_extra_stack_size;
|
|
|
|
#endif /* HAVE_STACK_OVERFLOW_RECOVERY */
|
|
|
|
#if HAVE_SIGSEGV_RECOVERY
|
|
|
|
/* User's SIGSEGV handler. */
|
|
static sigsegv_handler_t user_handler = (sigsegv_handler_t)NULL;
|
|
|
|
#endif /* HAVE_SIGSEGV_RECOVERY */
|
|
|
|
|
|
/* Our SIGSEGV handler, with OS dependent argument list. */
|
|
|
|
#if HAVE_SIGSEGV_RECOVERY
|
|
|
|
static void
|
|
sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
|
|
{
|
|
void *address = (void *) (SIGSEGV_FAULT_ADDRESS);
|
|
|
|
# if HAVE_STACK_OVERFLOW_RECOVERY
|
|
# if !(HAVE_STACKVMA || defined SIGSEGV_FAULT_STACKPOINTER)
|
|
#error "Insufficient heuristics for detecting a stack overflow. Either define CFG_STACKVMA and HAVE_STACKVMA correctly, or define SIGSEGV_FAULT_STACKPOINTER correctly, or undefine HAVE_STACK_OVERFLOW_RECOVERY!"
|
|
# endif
|
|
|
|
/* Call user's handler. */
|
|
if (user_handler && (*user_handler) (address, 0))
|
|
{
|
|
/* Handler successful. */
|
|
}
|
|
else
|
|
{
|
|
/* Handler declined responsibility. */
|
|
|
|
/* Did the user install a stack overflow handler? */
|
|
if (stk_user_handler)
|
|
{
|
|
/* See whether it was a stack overflow. If so, longjump away. */
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
uintptr_t old_sp = (uintptr_t) (SIGSEGV_FAULT_STACKPOINTER);
|
|
# ifdef __ia64
|
|
uintptr_t old_bsp = (uintptr_t) (SIGSEGV_FAULT_BSP_POINTER);
|
|
# endif
|
|
# endif
|
|
|
|
# if HAVE_STACKVMA
|
|
/* Were we able to determine the stack top? */
|
|
if (stack_top)
|
|
{
|
|
/* Determine stack bounds. */
|
|
int saved_errno = errno;
|
|
struct vma_struct vma;
|
|
int ret = sigsegv_get_vma (stack_top, &vma);
|
|
errno = saved_errno;
|
|
if (ret >= 0)
|
|
{
|
|
# ifndef BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW
|
|
/* Heuristic AC: If the fault_address is nearer to the stack
|
|
segment's [start,end] than to the previous segment, we
|
|
consider it a stack overflow.
|
|
In the case of IA-64, we know that the previous segment
|
|
is the up-growing bsp segment, and either of the two
|
|
stacks can overflow. */
|
|
uintptr_t addr = (uintptr_t) address;
|
|
|
|
# ifdef __ia64
|
|
if (addr >= vma.prev_end && addr <= vma.end - 1)
|
|
# else
|
|
# if STACK_DIRECTION < 0
|
|
if (addr >= vma.start
|
|
? (addr <= vma.end - 1)
|
|
: vma.is_near_this (addr, &vma))
|
|
# else
|
|
if (addr <= vma.end - 1
|
|
? (addr >= vma.start)
|
|
: vma.is_near_this (addr, &vma))
|
|
# endif
|
|
# endif
|
|
{
|
|
# else /* BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW */
|
|
# if HAVE_GETRLIMIT && defined RLIMIT_STACK
|
|
/* Heuristic BC: If the stack size has reached its maximal size,
|
|
and old_sp is near the low end, we consider it a stack
|
|
overflow. */
|
|
saved_errno = errno;
|
|
struct rlimit rl;
|
|
ret = getrlimit (RLIMIT_STACK, &rl);
|
|
errno = saved_errno;
|
|
if (ret >= 0)
|
|
{
|
|
uintptr_t current_stack_size = vma.end - vma.start;
|
|
uintptr_t max_stack_size = rl.rlim_cur;
|
|
if (current_stack_size <= max_stack_size + 4096
|
|
&& max_stack_size <= current_stack_size + 4096
|
|
# else
|
|
{
|
|
if (1
|
|
# endif
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
/* Heuristic BC: If we know old_sp, and it is neither
|
|
near the low end, nor in the alternate stack, then
|
|
it's probably not a stack overflow. */
|
|
&& ((old_sp >= stk_extra_stack
|
|
&& old_sp <= stk_extra_stack + stk_extra_stack_size)
|
|
# if STACK_DIRECTION < 0
|
|
|| (old_sp <= vma.start + 4096
|
|
&& vma.start <= old_sp + 4096))
|
|
# else
|
|
|| (old_sp <= vma.end + 4096
|
|
&& vma.end <= old_sp + 4096))
|
|
# endif
|
|
# endif
|
|
)
|
|
# endif /* BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW */
|
|
# else /* !HAVE_STACKVMA */
|
|
/* Heuristic AB: If the fault address is near the stack pointer,
|
|
it's a stack overflow. */
|
|
uintptr_t addr = (uintptr_t) address;
|
|
|
|
if ((addr <= old_sp + 4096 && old_sp <= addr + 4096)
|
|
# ifdef __ia64
|
|
|| (addr <= old_bsp + 4096 && old_bsp <= addr + 4096)
|
|
# endif
|
|
)
|
|
{
|
|
{
|
|
{
|
|
# endif /* !HAVE_STACKVMA */
|
|
{
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
int emergency =
|
|
(old_sp >= stk_extra_stack
|
|
&& old_sp <= stk_extra_stack + stk_extra_stack_size);
|
|
stackoverflow_context_t context = (SIGSEGV_FAULT_CONTEXT);
|
|
# else
|
|
int emergency = 0;
|
|
stackoverflow_context_t context = (void *) 0;
|
|
# endif
|
|
/* Call user's handler. */
|
|
(*stk_user_handler) (emergency, context);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# endif /* HAVE_STACK_OVERFLOW_RECOVERY */
|
|
|
|
if (user_handler && (*user_handler) (address, 1))
|
|
{
|
|
/* Handler successful. */
|
|
}
|
|
else
|
|
{
|
|
/* Handler declined responsibility for real. */
|
|
|
|
/* Remove ourselves and dump core. */
|
|
SIGSEGV_FOR_ALL_SIGNALS (signo, signal (signo, SIG_DFL);)
|
|
}
|
|
|
|
# if HAVE_STACK_OVERFLOW_RECOVERY
|
|
}
|
|
# endif /* HAVE_STACK_OVERFLOW_RECOVERY */
|
|
}
|
|
|
|
#elif HAVE_STACK_OVERFLOW_RECOVERY
|
|
|
|
static void
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
sigsegv_handler (SIGSEGV_FAULT_HANDLER_ARGLIST)
|
|
# else
|
|
sigsegv_handler (int sig)
|
|
# endif
|
|
{
|
|
# if !((HAVE_GETRLIMIT && defined RLIMIT_STACK) || defined SIGSEGV_FAULT_STACKPOINTER)
|
|
# error "Insufficient heuristics for detecting a stack overflow. Either define SIGSEGV_FAULT_STACKPOINTER correctly, or undefine HAVE_STACK_OVERFLOW_RECOVERY!"
|
|
# endif
|
|
|
|
/* Did the user install a handler? */
|
|
if (stk_user_handler)
|
|
{
|
|
/* See whether it was a stack overflow. If so, longjump away. */
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
uintptr_t old_sp = (uintptr_t) (SIGSEGV_FAULT_STACKPOINTER);
|
|
# endif
|
|
|
|
/* Were we able to determine the stack top? */
|
|
if (stack_top)
|
|
{
|
|
/* Determine stack bounds. */
|
|
int saved_errno = errno;
|
|
struct vma_struct vma;
|
|
int ret = sigsegv_get_vma (stack_top, &vma);
|
|
errno = saved_errno;
|
|
if (ret >= 0)
|
|
{
|
|
# if HAVE_GETRLIMIT && defined RLIMIT_STACK
|
|
/* Heuristic BC: If the stack size has reached its maximal size,
|
|
and old_sp is near the low end, we consider it a stack
|
|
overflow. */
|
|
saved_errno = errno;
|
|
struct rlimit rl;
|
|
ret = getrlimit (RLIMIT_STACK, &rl);
|
|
errno = saved_errno;
|
|
if (ret >= 0)
|
|
{
|
|
uintptr_t current_stack_size = vma.end - vma.start;
|
|
uintptr_t max_stack_size = rl.rlim_cur;
|
|
if (current_stack_size <= max_stack_size + 4096
|
|
&& max_stack_size <= current_stack_size + 4096
|
|
# else
|
|
{
|
|
if (1
|
|
# endif
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
/* Heuristic BC: If we know old_sp, and it is neither
|
|
near the low end, nor in the alternate stack, then
|
|
it's probably not a stack overflow. */
|
|
&& ((old_sp >= stk_extra_stack
|
|
&& old_sp <= stk_extra_stack + stk_extra_stack_size)
|
|
# if STACK_DIRECTION < 0
|
|
|| (old_sp <= vma.start + 4096
|
|
&& vma.start <= old_sp + 4096))
|
|
# else
|
|
|| (old_sp <= vma.end + 4096
|
|
&& vma.end <= old_sp + 4096))
|
|
# endif
|
|
# endif
|
|
)
|
|
{
|
|
# ifdef SIGSEGV_FAULT_STACKPOINTER
|
|
int emergency =
|
|
(old_sp >= stk_extra_stack
|
|
&& old_sp <= stk_extra_stack + stk_extra_stack_size);
|
|
stackoverflow_context_t context = (SIGSEGV_FAULT_CONTEXT);
|
|
# else
|
|
int emergency = 0;
|
|
stackoverflow_context_t context = (void *) 0;
|
|
# endif
|
|
/* Call user's handler. */
|
|
(*stk_user_handler)(emergency,context);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove ourselves and dump core. */
|
|
SIGSEGV_FOR_ALL_SIGNALS (signo, signal (signo, SIG_DFL);)
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#if HAVE_SIGSEGV_RECOVERY || HAVE_STACK_OVERFLOW_RECOVERY
|
|
|
|
static void
|
|
install_for (int sig)
|
|
{
|
|
struct sigaction action;
|
|
|
|
# ifdef SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
action.sa_sigaction = (void (*) (int, siginfo_t *, void *)) &sigsegv_handler;
|
|
# else
|
|
action.sa_handler = (void (*) (int)) &sigsegv_handler;
|
|
# endif
|
|
/* Block most signals while SIGSEGV is being handled. */
|
|
/* Signals SIGKILL, SIGSTOP cannot be blocked. */
|
|
/* Signals SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU are not blocked because
|
|
dealing with these signals seems dangerous. */
|
|
/* Signals SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTRAP, SIGIOT, SIGEMT, SIGBUS,
|
|
SIGSYS, SIGSTKFLT are not blocked because these are synchronous signals,
|
|
which may require immediate intervention, otherwise the process may
|
|
starve. */
|
|
sigemptyset (&action.sa_mask);
|
|
# ifdef SIGHUP
|
|
sigaddset (&action.sa_mask,SIGHUP);
|
|
# endif
|
|
# ifdef SIGINT
|
|
sigaddset (&action.sa_mask,SIGINT);
|
|
# endif
|
|
# ifdef SIGQUIT
|
|
sigaddset (&action.sa_mask,SIGQUIT);
|
|
# endif
|
|
# ifdef SIGPIPE
|
|
sigaddset (&action.sa_mask,SIGPIPE);
|
|
# endif
|
|
# ifdef SIGALRM
|
|
sigaddset (&action.sa_mask,SIGALRM);
|
|
# endif
|
|
# ifdef SIGTERM
|
|
sigaddset (&action.sa_mask,SIGTERM);
|
|
# endif
|
|
# ifdef SIGUSR1
|
|
sigaddset (&action.sa_mask,SIGUSR1);
|
|
# endif
|
|
# ifdef SIGUSR2
|
|
sigaddset (&action.sa_mask,SIGUSR2);
|
|
# endif
|
|
# ifdef SIGCHLD
|
|
sigaddset (&action.sa_mask,SIGCHLD);
|
|
# endif
|
|
# ifdef SIGCLD
|
|
sigaddset (&action.sa_mask,SIGCLD);
|
|
# endif
|
|
# ifdef SIGURG
|
|
sigaddset (&action.sa_mask,SIGURG);
|
|
# endif
|
|
# ifdef SIGIO
|
|
sigaddset (&action.sa_mask,SIGIO);
|
|
# endif
|
|
# ifdef SIGPOLL
|
|
sigaddset (&action.sa_mask,SIGPOLL);
|
|
# endif
|
|
# ifdef SIGXCPU
|
|
sigaddset (&action.sa_mask,SIGXCPU);
|
|
# endif
|
|
# ifdef SIGXFSZ
|
|
sigaddset (&action.sa_mask,SIGXFSZ);
|
|
# endif
|
|
# ifdef SIGVTALRM
|
|
sigaddset (&action.sa_mask,SIGVTALRM);
|
|
# endif
|
|
# ifdef SIGPROF
|
|
sigaddset (&action.sa_mask,SIGPROF);
|
|
# endif
|
|
# ifdef SIGPWR
|
|
sigaddset (&action.sa_mask,SIGPWR);
|
|
# endif
|
|
# ifdef SIGLOST
|
|
sigaddset (&action.sa_mask,SIGLOST);
|
|
# endif
|
|
# ifdef SIGWINCH
|
|
sigaddset (&action.sa_mask,SIGWINCH);
|
|
# endif
|
|
/* Note that sigaction() implicitly adds sig itself to action.sa_mask. */
|
|
/* Ask the OS to provide a structure siginfo_t to the handler. */
|
|
# ifdef SIGSEGV_FAULT_ADDRESS_FROM_SIGINFO
|
|
action.sa_flags = SA_SIGINFO;
|
|
# else
|
|
action.sa_flags = 0;
|
|
# endif
|
|
# if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGALTSTACK /* not BeOS */
|
|
/* Work around Linux 2.2.5 bug: If SA_ONSTACK is specified but sigaltstack()
|
|
has not been called, the kernel will busy loop, eating CPU time. So
|
|
avoid setting SA_ONSTACK until the user has requested stack overflow
|
|
handling. */
|
|
if (stk_user_handler)
|
|
action.sa_flags |= SA_ONSTACK;
|
|
# endif
|
|
sigaction (sig, &action, (struct sigaction *) NULL);
|
|
}
|
|
|
|
#endif /* HAVE_SIGSEGV_RECOVERY || HAVE_STACK_OVERFLOW_RECOVERY */
|
|
|
|
int
|
|
sigsegv_install_handler (sigsegv_handler_t handler)
|
|
{
|
|
#if HAVE_SIGSEGV_RECOVERY
|
|
user_handler = handler;
|
|
|
|
SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
|
|
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
sigsegv_deinstall_handler (void)
|
|
{
|
|
#if HAVE_SIGSEGV_RECOVERY
|
|
user_handler = (sigsegv_handler_t)NULL;
|
|
|
|
# if HAVE_STACK_OVERFLOW_RECOVERY
|
|
if (!stk_user_handler)
|
|
# endif
|
|
{
|
|
SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int
|
|
sigsegv_leave_handler (void (*continuation) (void*, void*, void*),
|
|
void* cont_arg1, void* cont_arg2, void* cont_arg3)
|
|
{
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY
|
|
/*
|
|
* Reset the system's knowledge that we are executing on the alternate
|
|
* stack. If we didn't do that, siglongjmp would be needed instead of
|
|
* longjmp to leave the signal handler.
|
|
*/
|
|
sigsegv_reset_onstack_flag ();
|
|
#endif
|
|
(*continuation) (cont_arg1, cont_arg2, cont_arg3);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
stackoverflow_install_handler (stackoverflow_handler_t handler,
|
|
void *extra_stack, size_t extra_stack_size)
|
|
{
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY
|
|
# if HAVE_STACKVMA
|
|
if (!stack_top)
|
|
{
|
|
int dummy;
|
|
remember_stack_top (&dummy);
|
|
if (!stack_top)
|
|
return -1;
|
|
}
|
|
# endif
|
|
|
|
stk_user_handler = handler;
|
|
stk_extra_stack = (uintptr_t) extra_stack;
|
|
stk_extra_stack_size = extra_stack_size;
|
|
{
|
|
stack_t ss;
|
|
ss.ss_sp = extra_stack;
|
|
ss.ss_size = extra_stack_size;
|
|
ss.ss_flags = 0; /* no SS_DISABLE */
|
|
if (sigaltstack (&ss, (stack_t*)0) < 0)
|
|
return -1;
|
|
}
|
|
|
|
/* Install the signal handlers with SA_ONSTACK. */
|
|
SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
stackoverflow_deinstall_handler (void)
|
|
{
|
|
#if HAVE_STACK_OVERFLOW_RECOVERY
|
|
stk_user_handler = (stackoverflow_handler_t) NULL;
|
|
|
|
# if HAVE_SIGSEGV_RECOVERY
|
|
if (user_handler)
|
|
{
|
|
/* Reinstall the signal handlers without SA_ONSTACK, to avoid Linux
|
|
bug. */
|
|
SIGSEGV_FOR_ALL_SIGNALS (sig, install_for (sig);)
|
|
}
|
|
else
|
|
# endif
|
|
{
|
|
SIGSEGV_FOR_ALL_SIGNALS (sig, signal (sig, SIG_DFL);)
|
|
}
|
|
|
|
{
|
|
stack_t ss;
|
|
ss.ss_flags = SS_DISABLE;
|
|
if (sigaltstack (&ss, (stack_t *) 0) < 0)
|
|
perror ("gnulib sigsegv (stackoverflow_deinstall_handler)");
|
|
}
|
|
#endif
|
|
}
|