From e54aa6196947ed22ff66bcd714e4fc7bd0c5c3b4 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 16 May 2021 15:20:12 +0200 Subject: [PATCH 1/2] sigsegv: New module. * lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications. * lib/sigsegv.c: Likewise. * lib/stackvma.h: Likewise. * lib/stackvma.c: Likewise. * m4/sigaltstack.m4: Likewise. * m4/stack-direction.m4: Likewise. * modules/sigsegv: New file. --- ChangeLog | 11 + lib/sigsegv.c | 1372 ++++++++++++++++++++++++++++++++ lib/sigsegv.in.h | 233 ++++++ lib/stackvma.c | 2064 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/stackvma.h | 60 ++ m4/sigaltstack.m4 | 198 +++++ m4/stack-direction.m4 | 104 +++ modules/sigsegv | 101 +++ 8 files changed, 4143 insertions(+) create mode 100644 lib/sigsegv.c create mode 100644 lib/sigsegv.in.h create mode 100644 lib/stackvma.c create mode 100644 lib/stackvma.h create mode 100644 m4/sigaltstack.m4 create mode 100644 m4/stack-direction.m4 create mode 100644 modules/sigsegv diff --git a/ChangeLog b/ChangeLog index 30663cb..c939655 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2021-05-16 Bruno Haible + + sigsegv: New module. + * lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications. + * lib/sigsegv.c: Likewise. + * lib/stackvma.h: Likewise. + * lib/stackvma.c: Likewise. + * m4/sigaltstack.m4: Likewise. + * m4/stack-direction.m4: Likewise. + * modules/sigsegv: New file. + 2021-05-15 Pádraig Brady realloc-gnu: avoid glibc MALLOC_CHECK_ issue diff --git a/lib/sigsegv.c b/lib/sigsegv.c new file mode 100644 index 0000000..312f132 --- /dev/null +++ b/lib/sigsegv.c @@ -0,0 +1,1372 @@ +/* Page fault handling library. + Copyright (C) 1993-2021 Bruno Haible + Copyright (C) 2018 Nylon Chen + + 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 . */ + +#include + +/* Specification. */ +#include "sigsegv.h" + +#include +#include /* declares perror */ +#include /* defines uintptr_t */ +#include +#include +#if HAVE_GETRLIMIT +# include /* declares struct rlimit */ +#endif + +#ifdef __OpenBSD__ +# include /* 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 + 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 + 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 + 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 + 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 + 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 ) + 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 + 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 ) + 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 __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 + 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 ) + 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 part 08/11 + . */ + +# 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 , + and the 'struct pt_regs' defined in + 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 */ +/* both 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 + +# 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 , 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 + 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 ) + 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//sigcontextinfo.h. */ + +/* For SIGSEGV_FAULT_STACKPOINTER, see the definition of GET_STACK in + glibc/sysdeps/unix/sysv/linux//sigcontextinfo.h. */ + +# endif + +#endif + +#if defined __GNU__ /* Hurd */ + +# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +# define SIGSEGV_FAULT_ADDRESS (unsigned long) code +# define SIGSEGV_FAULT_CONTEXT scp + +# if defined __i386__ + +/* scp points to a 'struct sigcontext' (defined in + glibc/sysdeps/mach/hurd/i386/bits/sigcontext.h). + The registers of this struct get pushed on the stack through + gnumach/i386/i386/locore.S:trapall. */ +/* Both sc_esp and sc_uesp appear to have the same value. + It appears more reliable to use sc_uesp because it is labelled as + "old esp, if trapped from user". */ +# 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__ + +# 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__ /* 64-bit */ + +/* See sys/arm64/include/ucontext.h. */ + +# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.mc_gpregs.gp_sp + +# 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 , see + http://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 __powerpc__ || defined __powerpc64__ + +/* 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 , + - 'struct __darwin_mcontext64' in , and + - 'struct __darwin_x86_thread_state64' in . */ +# 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 , + - 'struct __darwin_mcontext32' in , and + - 'struct __darwin_i386_thread_state' in . */ +# 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 , + - 'struct __darwin_mcontext64' in , and + - 'struct __darwin_arm_thread_state64' in . */ +# 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 , + - 'struct __darwin_mcontext' in , and + - 'struct __darwin_ppc_thread_state' in . */ +# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext->__ss.__r1 + +# 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 __sgi /* IRIX */ + +# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +# define SIGSEGV_FAULT_ADDRESS (unsigned long) scp->sc_badvaddr +# define SIGSEGV_FAULT_CONTEXT scp + +# if defined __mips__ || defined __mipsn32__ || defined __mips64__ +# define SIGSEGV_FAULT_STACKPOINTER scp->sc_regs[29] +# 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 and + of 'struct __mcontext' in . */ +# 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 and + of 'struct vregs' in . */ + +# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.rsp + +# elif defined __i386__ +/* 32 bit registers */ + +/* See the definition of 'ucontext_t' in and + of 'struct vregs' in . */ + +# 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__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__) +# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \ + { int var; var = SIGSEGV; { body } var = SIGBUS; { body } } +#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__ \ + || 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 __sgi || defined __sun /* IRIX, Solaris */ + +# include + +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; + struct vma_struct vma; + int ret; + + saved_errno = errno; + 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. */ + struct rlimit rl; + + saved_errno = errno; + 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 (sig, signal (sig, 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; + struct vma_struct vma; + int ret; + + saved_errno = errno; + 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. */ + struct rlimit rl; + + saved_errno = errno; + 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 (sig, signal (sig, 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 = &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; +# if SIGALTSTACK_SS_REVERSED + ss.ss_sp = (char *) extra_stack + extra_stack_size - sizeof (void *); + ss.ss_size = extra_stack_size - sizeof (void *); +# else + ss.ss_sp = extra_stack; + ss.ss_size = extra_stack_size; +# endif + 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 +} diff --git a/lib/sigsegv.in.h b/lib/sigsegv.in.h new file mode 100644 index 0000000..1c87acc --- /dev/null +++ b/lib/sigsegv.in.h @@ -0,0 +1,233 @@ +/* Page fault handling library. + Copyright (C) 1998-2021 Bruno Haible + + 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 . */ + +#ifndef _SIGSEGV_H +#define _SIGSEGV_H + +/* Get size_t. */ +#include + +/* Define the fault context structure. */ +#if defined __linux__ || defined __ANDROID__ \ + || (defined __FreeBSD__ && (defined __arm__ || defined __armhf__ || defined __arm64__)) \ + || defined __NetBSD__ \ + || defined _AIX || defined __sun \ + || defined __CYGWIN__ +/* Linux, FreeBSD, NetBSD, AIX, Solaris, Cygwin */ +# include +#elif (defined __APPLE__ && defined __MACH__) +/* macOS */ +# include +#elif defined __HAIKU__ +/* Haiku */ +# include +#endif + +/* Correct the value of SIGSTKSZ on some systems. + AIX 64-bit: original value 4096 is too small. + HP-UX: original value 8192 is too small. + Solaris 11/x86_64: original value 8192 is too small. */ +#if defined _AIX && defined _ARCH_PPC64 +# include +# undef SIGSTKSZ +# define SIGSTKSZ 8192 +#endif +#if defined __hpux || (defined __sun && (defined __x86_64__ || defined __amd64__)) +# include +# undef SIGSTKSZ +# define SIGSTKSZ 16384 +#endif + +/* HAVE_SIGSEGV_RECOVERY + is defined if the system supports catching SIGSEGV. */ +#if defined __linux__ || defined __ANDROID__ || defined __GNU__ \ + || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) || defined __DragonFly__ \ + || defined __NetBSD__ \ + || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__) \ + || defined _AIX || defined __sgi || defined __sun \ + || defined __CYGWIN__ || defined __HAIKU__ +/* Linux, Hurd, GNU/kFreeBSD, FreeBSD, NetBSD, OpenBSD, macOS, AIX, IRIX, Solaris, Cygwin, Haiku */ +# define HAVE_SIGSEGV_RECOVERY 1 +#endif + +/* HAVE_STACK_OVERFLOW_RECOVERY + is defined if stack overflow can be caught. */ +#if defined __linux__ || defined __ANDROID__ || defined __GNU__ \ + || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) || defined __DragonFly__ \ + || (defined __NetBSD__ && !(defined __sparc__ || defined __sparc64__)) \ + || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__) \ + || defined _AIX || defined __sgi || defined __sun \ + || defined __CYGWIN__ || defined __HAIKU__ +/* Linux, Hurd, GNU/kFreeBSD, FreeBSD, NetBSD, OpenBSD, macOS, AIX, IRIX, Solaris, Cygwin, Haiku */ +# define HAVE_STACK_OVERFLOW_RECOVERY 1 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBSIGSEGV_VERSION 0x020D /* version number: (major<<8) + minor */ +extern int libsigsegv_version; /* Likewise */ + +/* -------------------------------------------------------------------------- */ + +#if 1 /* really only HAVE_SIGSEGV_RECOVERY */ + +/* + * The mask of bits that are set to zero in a fault address that gets passed + * to a global SIGSEGV handler. + * On some platforms, the precise fault address is not known, only the memory + * page into which the fault address falls. This is apparently allowed by POSIX: + * + * says: "For some implementations, the value of si_addr may be inaccurate." + * In this case, the returned fault address is rounded down to a multiple of + * getpagesize() = sysconf(_SC_PAGESIZE). + * On such platforms, we define SIGSEGV_FAULT_ADDRESS_ALIGNMENT to be an upper + * bound for getpagesize() (and, like getpagesize(), also a power of 2). + * On the platforms where the returned fault address is the precise one, we + * define SIGSEGV_FAULT_ADDRESS_ALIGNMENT to 1. + */ +# if defined __NetBSD__ && (defined __sparc__ || defined __sparc64__) + /* getpagesize () is 0x1000 or 0x2000, depending on hardware. */ +# define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 0x2000UL +# elif defined __linux__ && (defined __s390__ || defined __s390x__) + /* getpagesize () is 0x1000. */ +# define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 0x1000UL +# else +# define SIGSEGV_FAULT_ADDRESS_ALIGNMENT 1UL +# endif + +/* + * The type of a global SIGSEGV handler. + * The fault address, with the bits (SIGSEGV_FAULT_ADDRESS_ALIGNMENT - 1) + * cleared, is passed as argument. + * The access type (read access or write access) is not passed; your handler + * has to know itself how to distinguish these two cases. + * The second argument is 0, meaning it could also be a stack overflow, or 1, + * meaning the handler should seriously try to fix the fault. + * The return value should be nonzero if the handler has done its job + * and no other handler should be called, or 0 if the handler declines + * responsibility for the given address. + * + * The handler is run at a moment when nothing about the global state of the + * program is known. Therefore it cannot use facilities that manipulate global + * variables or locks. In particular, it cannot use malloc(); use mmap() + * instead. It cannot use fopen(); use open() instead. Etc. All global + * variables that are accessed by the handler should be marked 'volatile'. + */ +typedef int (*sigsegv_handler_t) (void* fault_address, int serious); + +/* + * Installs a global SIGSEGV handler. + * This should be called once only, and it ignores any previously installed + * SIGSEGV handler. + * Returns 0 on success, or -1 if the system doesn't support catching SIGSEGV. + */ +extern int sigsegv_install_handler (sigsegv_handler_t handler); + +/* + * Deinstalls the global SIGSEGV handler. + * This goes back to the state where no SIGSEGV handler is installed. + */ +extern void sigsegv_deinstall_handler (void); + +/* + * Prepares leaving a SIGSEGV handler (through longjmp or similar means). + * Control is transferred by calling CONTINUATION with CONT_ARG1, CONT_ARG2, + * CONT_ARG3 as arguments. + * CONTINUATION must not return. + * The sigsegv_leave_handler function may return if called from a SIGSEGV + * handler; its return value should be used as the handler's return value. + * The sigsegv_leave_handler function does not return if called from a + * stack overflow handler. + */ +extern int sigsegv_leave_handler (void (*continuation) (void*, void*, void*), void* cont_arg1, void* cont_arg2, void* cont_arg3); + +#endif /* HAVE_SIGSEGV_RECOVERY */ + +#if 1 /* really only HAVE_STACK_OVERFLOW_RECOVERY */ + +/* + * The type of a context passed to a stack overflow handler. + * This type is system dependent; on some platforms it is an 'ucontext_t *', + * on some platforms it is a 'struct sigcontext *', on others merely an + * opaque 'void *'. + */ +# if defined __linux__ || defined __ANDROID__ \ + || (defined __FreeBSD__ && (defined __arm__ || defined __armhf__ || defined __arm64__)) \ + || defined __NetBSD__ \ + || (defined __APPLE__ && defined __MACH__) \ + || defined _AIX || defined __sun \ + || defined __CYGWIN__ || defined __HAIKU__ +typedef ucontext_t *stackoverflow_context_t; +# elif defined __GNU__ \ + || defined __FreeBSD_kernel__ || (defined __FreeBSD__ && !(defined __sparc__ || defined __sparc64__)) \ + || defined __OpenBSD__ || defined __sgi +typedef struct sigcontext *stackoverflow_context_t; +# else +typedef void *stackoverflow_context_t; +# endif + +/* + * The type of a stack overflow handler. + * Such a handler should perform a longjmp call in order to reduce the amount + * of stack needed. It must not return. + * The emergency argument is 0 when the stack could be repared, or 1 if the + * application should better save its state and exit now. + * + * The handler is run at a moment when nothing about the global state of the + * program is known. Therefore it cannot use facilities that manipulate global + * variables or locks. In particular, it cannot use malloc(); use mmap() + * instead. It cannot use fopen(); use open() instead. Etc. All global + * variables that are accessed by the handler should be marked 'volatile'. + */ +typedef void (*stackoverflow_handler_t) (int emergency, stackoverflow_context_t scp); + +/* + * Installs a stack overflow handler. + * The extra_stack argument is a pointer to a pre-allocated area used as a + * stack for executing the handler. It typically comes from a static variable + * or from heap-allocated memoty; placing it on the main stack may fail on + * some operating systems. + * Its size, passed in extra_stack_size, should be sufficiently large. The + * following code determines an appropriate size: + * #include + * #ifndef SIGSTKSZ / * glibc defines SIGSTKSZ for this purpose * / + * # define SIGSTKSZ 16384 / * on most platforms, 16 KB are sufficient * / + * #endif + * Returns 0 on success, or -1 if the system doesn't support catching stack + * overflow. + */ +extern int stackoverflow_install_handler (stackoverflow_handler_t handler, + void* extra_stack, size_t extra_stack_size); + +/* + * Deinstalls the stack overflow handler. + */ +extern void stackoverflow_deinstall_handler (void); + +#endif /* HAVE_STACK_OVERFLOW_RECOVERY */ + +/* -------------------------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SIGSEGV_H */ diff --git a/lib/stackvma.c b/lib/stackvma.c new file mode 100644 index 0000000..faa9923 --- /dev/null +++ b/lib/stackvma.c @@ -0,0 +1,2064 @@ +/* Determine the virtual memory area of a given address. + Copyright (C) 2002-2021 Bruno Haible + Copyright (C) 2003-2006 Paolo Bonzini + + 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 . */ + +#include + +/* Specification. */ +#include "stackvma.h" + +#include +#include + +/* =========================== stackvma-simple.c =========================== */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ \ + || (defined __APPLE__ && defined __MACH__) \ + || defined __sgi || defined __sun \ + || defined __CYGWIN__ || defined __HAIKU__ + +/* This file contains the proximity test function for the simple cases, where + the OS has an API for enumerating the mapped ranges of virtual memory. */ + +# if STACK_DIRECTION < 0 + +/* Info about the gap between this VMA and the previous one. + addr must be < vma->start. */ +static int +simple_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + return (vma->start - addr <= (vma->start - vma->prev_end) / 2); +} + +# endif +# if STACK_DIRECTION > 0 + +/* Info about the gap between this VMA and the next one. + addr must be > vma->end - 1. */ +static int +simple_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + return (addr - vma->end < (vma->next_start - vma->end) / 2); +} + +# endif + +#endif + +/* =========================== stackvma-rofile.c =========================== */ +/* Buffered read-only streams. */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ \ + || defined __CYGWIN__ + +# include /* errno, EINTR */ +# include /* open, O_RDONLY */ +# include /* size_t */ +# include /* getpagesize, lseek, read, close */ +# include +# include /* mmap, munmap */ + +# if defined __linux__ || defined __ANDROID__ +# include /* PATH_MAX */ +# endif + +/* Buffered read-only streams. + We cannot use here, because fopen() calls malloc(), and a malloc() + call may have been interrupted. + Also, we cannot use multiple read() calls, because if the buffer size is + smaller than the file's contents: + - On NetBSD, the second read() call would return 0, thus making the file + appear truncated. + - On DragonFly BSD, the first read() call would fail with errno = EFBIG. + - On all platforms, if some other thread is doing memory allocations or + deallocations between two read() calls, there is a high risk that the + result of these two read() calls don't fit together, and as a + consequence we will parse gargage and either omit some VMAs or return + VMAs with nonsensical addresses. + So use mmap(), and ignore the resulting VMA. + The stack-allocated buffer cannot be too large, because this can be called + when we are in the context of an alternate stack of just SIGSTKSZ bytes. */ + +# if defined __linux__ || defined __ANDROID__ + /* On Linux, if the file does not entirely fit into the buffer, the read() + function stops before the line that would come out truncated. The + maximum size of such a line is 73 + PATH_MAX bytes. To be sure that we + have read everything, we must verify that at least that many bytes are + left when read() returned. */ +# define MIN_LEFTOVER (73 + PATH_MAX) +# else +# define MIN_LEFTOVER 1 +# endif + +# if MIN_LEFTOVER < 1024 +# define STACK_ALLOCATED_BUFFER_SIZE 1024 +# else + /* There is no point in using a stack-allocated buffer if it is too small + anyway. */ +# define STACK_ALLOCATED_BUFFER_SIZE 1 +# endif + +struct rofile + { + size_t position; + size_t filled; + int eof_seen; + /* These fields deal with allocation of the buffer. */ + char *buffer; + char *auxmap; + size_t auxmap_length; + uintptr_t auxmap_start; + uintptr_t auxmap_end; + char stack_allocated_buffer[STACK_ALLOCATED_BUFFER_SIZE]; + }; + +/* Open a read-only file stream. */ +static int +rof_open (struct rofile *rof, const char *filename) +{ + int fd; + uintptr_t pagesize; + size_t size; + + fd = open (filename, O_RDONLY); + if (fd < 0) + return -1; + rof->position = 0; + rof->eof_seen = 0; + /* Try the static buffer first. */ + pagesize = 0; + rof->buffer = rof->stack_allocated_buffer; + size = sizeof (rof->stack_allocated_buffer); + rof->auxmap = NULL; + rof->auxmap_start = 0; + rof->auxmap_end = 0; + for (;;) + { + /* Attempt to read the contents in a single system call. */ + if (size > MIN_LEFTOVER) + { + int n = read (fd, rof->buffer, size); + if (n < 0 && errno == EINTR) + goto retry; +# if defined __DragonFly__ + if (!(n < 0 && errno == EFBIG)) +# endif + { + if (n <= 0) + /* Empty file. */ + goto fail1; + if (n + MIN_LEFTOVER <= size) + { + /* The buffer was sufficiently large. */ + rof->filled = n; +# if defined __linux__ || defined __ANDROID__ + /* On Linux, the read() call may stop even if the buffer was + large enough. We need the equivalent of full_read(). */ + for (;;) + { + n = read (fd, rof->buffer + rof->filled, size - rof->filled); + if (n < 0 && errno == EINTR) + goto retry; + if (n < 0) + /* Some error. */ + goto fail1; + if (n + MIN_LEFTOVER > size - rof->filled) + /* Allocate a larger buffer. */ + break; + if (n == 0) + { + /* Reached the end of file. */ + close (fd); + return 0; + } + rof->filled += n; + } +# else + close (fd); + return 0; +# endif + } + } + } + /* Allocate a larger buffer. */ + if (pagesize == 0) + { + pagesize = getpagesize (); + size = pagesize; + while (size <= MIN_LEFTOVER) + size = 2 * size; + } + else + { + size = 2 * size; + if (size == 0) + /* Wraparound. */ + goto fail1; + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); + } + rof->auxmap = (void *) mmap ((void *) 0, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (rof->auxmap == (void *) -1) + { + close (fd); + return -1; + } + rof->auxmap_length = size; + rof->auxmap_start = (uintptr_t) rof->auxmap; + rof->auxmap_end = rof->auxmap_start + size; + rof->buffer = (char *) rof->auxmap; + retry: + /* Restart. */ + if (lseek (fd, 0, SEEK_SET) < 0) + { + close (fd); + fd = open (filename, O_RDONLY); + if (fd < 0) + goto fail2; + } + } + fail1: + close (fd); + fail2: + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); + return -1; +} + +/* Return the next byte from a read-only file stream without consuming it, + or -1 at EOF. */ +static int +rof_peekchar (struct rofile *rof) +{ + if (rof->position == rof->filled) + { + rof->eof_seen = 1; + return -1; + } + return (unsigned char) rof->buffer[rof->position]; +} + +/* Return the next byte from a read-only file stream, or -1 at EOF. */ +static int +rof_getchar (struct rofile *rof) +{ + int c = rof_peekchar (rof); + if (c >= 0) + rof->position++; + return c; +} + +/* Parse an unsigned hexadecimal number from a read-only file stream. */ +static int +rof_scanf_lx (struct rofile *rof, uintptr_t *valuep) +{ + uintptr_t value = 0; + unsigned int numdigits = 0; + for (;;) + { + int c = rof_peekchar (rof); + if (c >= '0' && c <= '9') + value = (value << 4) + (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (value << 4) + (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + value = (value << 4) + (c - 'a' + 10); + else + break; + rof_getchar (rof); + numdigits++; + } + if (numdigits == 0) + return -1; + *valuep = value; + return 0; +} + +/* Close a read-only file stream. */ +static void +rof_close (struct rofile *rof) +{ + if (rof->auxmap != NULL) + munmap (rof->auxmap, rof->auxmap_length); +} + +#endif + +/* ========================== stackvma-vma-iter.c ========================== */ +/* Iterate through the virtual memory areas of the current process, + by reading from the /proc file system. */ + +/* This code is a simplied copy (no handling of protection flags) of the + code in gnulib's lib/vma-iter.c. */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ \ + || defined __CYGWIN__ + +/* Forward declarations. */ +struct callback_locals; +static int callback (struct callback_locals *locals, uintptr_t start, uintptr_t end); + +# if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __CYGWIN__ +/* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc + file system. */ + +static int +vma_iterate_proc (struct callback_locals *locals) +{ + struct rofile rof; + + /* Open the current process' maps file. It describes one VMA per line. */ + if (rof_open (&rof, "/proc/self/maps") >= 0) + { + uintptr_t auxmap_start = rof.auxmap_start; + uintptr_t auxmap_end = rof.auxmap_end; + + for (;;) + { + uintptr_t start, end; + int c; + + /* Parse one line. First start and end. */ + if (!(rof_scanf_lx (&rof, &start) >= 0 + && rof_getchar (&rof) == '-' + && rof_scanf_lx (&rof, &end) >= 0)) + break; + while (c = rof_getchar (&rof), c != -1 && c != '\n') + ; + + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (locals, start, auxmap_start)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (locals, auxmap_end, end)) + break; + } + else + { + if (callback (locals, start, end)) + break; + } + } + rof_close (&rof); + return 0; + } + + return -1; +} + +# elif defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ + +static int +vma_iterate_proc (struct callback_locals *locals) +{ + struct rofile rof; + + /* Open the current process' maps file. It describes one VMA per line. + On FreeBSD: + Cf. + On NetBSD, there are two such files: + - /proc/curproc/map in near-FreeBSD syntax, + - /proc/curproc/maps in Linux syntax. + Cf. */ + if (rof_open (&rof, "/proc/curproc/map") >= 0) + { + uintptr_t auxmap_start = rof.auxmap_start; + uintptr_t auxmap_end = rof.auxmap_end; + + for (;;) + { + uintptr_t start, end; + int c; + + /* Parse one line. First start. */ + if (!(rof_getchar (&rof) == '0' + && rof_getchar (&rof) == 'x' + && rof_scanf_lx (&rof, &start) >= 0)) + break; + while (c = rof_peekchar (&rof), c == ' ' || c == '\t') + rof_getchar (&rof); + /* Then end. */ + if (!(rof_getchar (&rof) == '0' + && rof_getchar (&rof) == 'x' + && rof_scanf_lx (&rof, &end) >= 0)) + break; + while (c = rof_getchar (&rof), c != -1 && c != '\n') + ; + + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (locals, start, auxmap_start)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (locals, auxmap_end, end)) + break; + } + else + { + if (callback (locals, start, end)) + break; + } + } + rof_close (&rof); + return 0; + } + + return -1; +} + +# endif + +# if (defined __FreeBSD_kernel__ || defined __FreeBSD__) && defined KERN_PROC_VMMAP /* FreeBSD >= 7.1 */ + +# include /* struct kinfo_vmentry */ +# include /* sysctl */ + +static int +vma_iterate_bsd (struct callback_locals *locals) +{ + /* Documentation: https://www.freebsd.org/cgi/man.cgi?sysctl(3) */ + int info_path[] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid () }; + size_t len; + size_t pagesize; + size_t memneed; + void *auxmap; + unsigned long auxmap_start; + unsigned long auxmap_end; + char *mem; + char *p; + char *p_end; + + len = 0; + if (sysctl (info_path, 4, NULL, &len, NULL, 0) < 0) + return -1; + /* Allow for small variations over time. In a multithreaded program + new VMAs can be allocated at any moment. */ + len = 2 * len + 200; + /* Allocate memneed bytes of memory. + We cannot use alloca here, because not much stack space is guaranteed. + We also cannot use malloc here, because a malloc() call may call mmap() + and thus pre-allocate available memory. + So use mmap(), and ignore the resulting VMA. */ + pagesize = getpagesize (); + memneed = len; + memneed = ((memneed - 1) / pagesize + 1) * pagesize; + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (auxmap == (void *) -1) + return -1; + auxmap_start = (unsigned long) auxmap; + auxmap_end = auxmap_start + memneed; + mem = (char *) auxmap; + if (sysctl (info_path, 4, mem, &len, NULL, 0) < 0) + { + munmap (auxmap, memneed); + return -1; + } + p = mem; + p_end = mem + len; + while (p < p_end) + { + struct kinfo_vmentry *kve = (struct kinfo_vmentry *) p; + unsigned long start = kve->kve_start; + unsigned long end = kve->kve_end; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (locals, start, auxmap_start)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (locals, auxmap_end, end)) + break; + } + else + { + if (callback (locals, start, end)) + break; + } + p += kve->kve_structsize; + } + munmap (auxmap, memneed); + return 0; +} + +# else + +# define vma_iterate_bsd(locals) (-1) + +# endif + + +/* Iterate over the virtual memory areas of the current process. + If such iteration is supported, the callback is called once for every + virtual memory area, in ascending order, with the following arguments: + - LOCALS is the same argument as passed to vma_iterate. + - START is the address of the first byte in the area, page-aligned. + - END is the address of the last byte in the area plus 1, page-aligned. + Note that it may be 0 for the last area in the address space. + If the callback returns 0, the iteration continues. If it returns 1, + the iteration terminates prematurely. + This function may open file descriptors, but does not call malloc(). + Return 0 if all went well, or -1 in case of error. */ +static int +vma_iterate (struct callback_locals *locals) +{ +# if defined __FreeBSD__ + /* On FreeBSD with procfs (but not GNU/kFreeBSD, which uses linprocfs), the + function vma_iterate_proc does not return the virtual memory areas that + were created by anonymous mmap. See + + So use vma_iterate_proc only as a fallback. */ + int retval = vma_iterate_bsd (locals); + if (retval == 0) + return 0; + + return vma_iterate_proc (locals); +# else + /* On the other platforms, try the /proc approach first, and the sysctl() + as a fallback. */ + int retval = vma_iterate_proc (locals); + if (retval == 0) + return 0; + + return vma_iterate_bsd (locals); +# endif +} + +#endif + +/* =========================== stackvma-mincore.c =========================== */ + +/* mincore() is a system call that allows to inquire the status of a + range of pages of virtual memory. In particular, it allows to inquire + whether a page is mapped at all (except on Mac OS X, where mincore + returns 0 even for unmapped addresses). + As of 2006, mincore() is supported by: possible bits: + - Linux, since Linux 2.4 and glibc 2.2, 1 + - Solaris, since Solaris 9, 1 + - MacOS X, since MacOS X 10.3 (at least), 1 + - FreeBSD, since FreeBSD 6.0, MINCORE_{INCORE,REFERENCED,MODIFIED} + - NetBSD, since NetBSD 3.0 (at least), 1 + - OpenBSD, since OpenBSD 2.6 (at least), 1 + - AIX, since AIX 5.3, 1 + As of 2019, also on + - Hurd. + However, while the API allows to easily determine the bounds of mapped + virtual memory, it does not make it easy to find the bounds of _unmapped_ + virtual memory ranges. We try to work around this, but it may still be + slow. */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ /* || defined __OpenBSD__ */ \ + /* || (defined __APPLE__ && defined __MACH__) */ \ + || defined _AIX || defined __sun + +# include /* getpagesize, mincore */ +# include +# include /* mincore */ + +/* The AIX declaration of mincore() uses 'caddr_t', whereas the other platforms + use 'void *'. */ +# ifdef _AIX +typedef caddr_t MINCORE_ADDR_T; +# else +typedef void* MINCORE_ADDR_T; +# endif + +/* The glibc and musl declaration of mincore() uses 'unsigned char *', whereas + the BSD declaration uses 'char *'. */ +# if __GLIBC__ >= 2 || defined __linux__ || defined __ANDROID__ +typedef unsigned char pageinfo_t; +# else +typedef char pageinfo_t; +# endif + +/* Cache for getpagesize(). */ +static uintptr_t pagesize; + +/* Initialize pagesize. */ +static void +init_pagesize (void) +{ + pagesize = getpagesize (); +} + +/* Test whether the page starting at ADDR is among the address range. + ADDR must be a multiple of pagesize. */ +static int +is_mapped (uintptr_t addr) +{ + pageinfo_t vec[1]; + return mincore ((MINCORE_ADDR_T) addr, pagesize, vec) >= 0; +} + +/* Assuming that the page starting at ADDR is among the address range, + return the start of its virtual memory range. + ADDR must be a multiple of pagesize. */ +static uintptr_t +mapped_range_start (uintptr_t addr) +{ + /* Use a moderately sized VEC here, small enough that it fits on the stack + (without requiring malloc). */ + pageinfo_t vec[1024]; + uintptr_t stepsize = sizeof (vec); + + for (;;) + { + uintptr_t max_remaining; + + if (addr == 0) + return addr; + + max_remaining = addr / pagesize; + if (stepsize > max_remaining) + stepsize = max_remaining; + if (mincore ((MINCORE_ADDR_T) (addr - stepsize * pagesize), + stepsize * pagesize, vec) < 0) + /* Time to search in smaller steps. */ + break; + /* The entire range exists. Continue searching in large steps. */ + addr -= stepsize * pagesize; + } + for (;;) + { + uintptr_t halfstepsize1; + uintptr_t halfstepsize2; + + if (stepsize == 1) + return addr; + + /* Here we know that less than stepsize pages exist starting at addr. */ + halfstepsize1 = (stepsize + 1) / 2; + halfstepsize2 = stepsize / 2; + /* halfstepsize1 + halfstepsize2 = stepsize. */ + + if (mincore ((MINCORE_ADDR_T) (addr - halfstepsize1 * pagesize), + halfstepsize1 * pagesize, vec) < 0) + stepsize = halfstepsize1; + else + { + addr -= halfstepsize1 * pagesize; + stepsize = halfstepsize2; + } + } +} + +/* Assuming that the page starting at ADDR is among the address range, + return the end of its virtual memory range + 1. + ADDR must be a multiple of pagesize. */ +static uintptr_t +mapped_range_end (uintptr_t addr) +{ + /* Use a moderately sized VEC here, small enough that it fits on the stack + (without requiring malloc). */ + pageinfo_t vec[1024]; + uintptr_t stepsize = sizeof (vec); + + addr += pagesize; + for (;;) + { + uintptr_t max_remaining; + + if (addr == 0) /* wrapped around? */ + return addr; + + max_remaining = (- addr) / pagesize; + if (stepsize > max_remaining) + stepsize = max_remaining; + if (mincore ((MINCORE_ADDR_T) addr, stepsize * pagesize, vec) < 0) + /* Time to search in smaller steps. */ + break; + /* The entire range exists. Continue searching in large steps. */ + addr += stepsize * pagesize; + } + for (;;) + { + uintptr_t halfstepsize1; + uintptr_t halfstepsize2; + + if (stepsize == 1) + return addr; + + /* Here we know that less than stepsize pages exist starting at addr. */ + halfstepsize1 = (stepsize + 1) / 2; + halfstepsize2 = stepsize / 2; + /* halfstepsize1 + halfstepsize2 = stepsize. */ + + if (mincore ((MINCORE_ADDR_T) addr, halfstepsize1 * pagesize, vec) < 0) + stepsize = halfstepsize1; + else + { + addr += halfstepsize1 * pagesize; + stepsize = halfstepsize2; + } + } +} + +/* Determine whether an address range [ADDR1..ADDR2] is completely unmapped. + ADDR1 must be <= ADDR2. */ +static int +is_unmapped (uintptr_t addr1, uintptr_t addr2) +{ + uintptr_t count; + uintptr_t stepsize; + + /* Round addr1 down. */ + addr1 = (addr1 / pagesize) * pagesize; + /* Round addr2 up and turn it into an exclusive bound. */ + addr2 = ((addr2 / pagesize) + 1) * pagesize; + + /* This is slow: mincore() does not provide a way to determine the bounds + of the gaps directly. So we have to use mincore() on individual pages + over and over again. Only after we've verified that all pages are + unmapped, we know that the range is completely unmapped. + If we were to traverse the pages from bottom to top or from top to bottom, + it would be slow even in the average case. To speed up the search, we + exploit the fact that mapped memory ranges are larger than one page on + average, therefore we have good chances of hitting a mapped area if we + traverse only every second, or only fourth page, etc. This doesn't + decrease the worst-case runtime, only the average runtime. */ + count = (addr2 - addr1) / pagesize; + /* We have to test is_mapped (addr1 + i * pagesize) for 0 <= i < count. */ + for (stepsize = 1; stepsize < count; ) + stepsize = 2 * stepsize; + for (;;) + { + uintptr_t addr_stepsize; + uintptr_t i; + uintptr_t addr; + + stepsize = stepsize / 2; + if (stepsize == 0) + break; + addr_stepsize = stepsize * pagesize; + for (i = stepsize, addr = addr1 + addr_stepsize; + i < count; + i += 2 * stepsize, addr += 2 * addr_stepsize) + /* Here addr = addr1 + i * pagesize. */ + if (is_mapped (addr)) + return 0; + } + return 1; +} + +# if STACK_DIRECTION < 0 + +/* Info about the gap between this VMA and the previous one. + addr must be < vma->start. */ +static int +mincore_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + /* vma->start - addr <= (vma->start - vma->prev_end) / 2 + is mathematically equivalent to + vma->prev_end <= 2 * addr - vma->start + <==> is_unmapped (2 * addr - vma->start, vma->start - 1). + But be careful about overflow: if 2 * addr - vma->start is negative, + we consider a tiny "guard page" mapping [0, 0] to be present around + NULL; it intersects the range (2 * addr - vma->start, vma->start - 1), + therefore return false. */ + uintptr_t testaddr = addr - (vma->start - addr); + if (testaddr > addr) /* overflow? */ + return 0; + /* Here testaddr <= addr < vma->start. */ + return is_unmapped (testaddr, vma->start - 1); +} + +# endif +# if STACK_DIRECTION > 0 + +/* Info about the gap between this VMA and the next one. + addr must be > vma->end - 1. */ +static int +mincore_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + /* addr - vma->end < (vma->next_start - vma->end) / 2 + is mathematically equivalent to + vma->next_start > 2 * addr - vma->end + <==> is_unmapped (vma->end, 2 * addr - vma->end). + But be careful about overflow: if 2 * addr - vma->end is > ~0UL, + we consider a tiny "guard page" mapping [0, 0] to be present around + NULL; it intersects the range (vma->end, 2 * addr - vma->end), + therefore return false. */ + uintptr_t testaddr = addr + (addr - vma->end); + if (testaddr < addr) /* overflow? */ + return 0; + /* Here vma->end - 1 < addr <= testaddr. */ + return is_unmapped (vma->end, testaddr); +} + +# endif + +static int +mincore_get_vma (uintptr_t address, struct vma_struct *vma) +{ + if (pagesize == 0) + init_pagesize (); + address = (address / pagesize) * pagesize; + vma->start = mapped_range_start (address); + vma->end = mapped_range_end (address); + vma->is_near_this = mincore_is_near_this; + return 0; +} + +#endif + +/* ========================================================================== */ + +/* ---------------------------- stackvma-linux.c ---------------------------- */ + +#if defined __linux__ || defined __ANDROID__ /* Linux */ + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; +# if STACK_DIRECTION < 0 + uintptr_t prev; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ +# if STACK_DIRECTION < 0 + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->vma->prev_end = locals->prev; + locals->retval = 0; + return 1; + } + locals->prev = end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; +# if STACK_DIRECTION < 0 + locals.prev = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-freebsd.c --------------------------- */ + +#elif defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ /* GNU/kFreeBSD, FreeBSD */ + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; + /* The stack appears as multiple adjacents segments, therefore we + merge adjacent segments. */ + uintptr_t curr_start, curr_end; +# if STACK_DIRECTION < 0 + uintptr_t prev_end; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ + if (start == locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end = end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->vma->prev_end = locals->prev_end; + locals->retval = 0; + return 1; + } + locals->prev_end = locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = locals->curr_start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + locals->curr_start = start; locals->curr_end = end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; + locals.curr_start = 0; + locals.curr_end = 0; +# if STACK_DIRECTION < 0 + locals.prev_end = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >= locals.curr_start && address <= locals.curr_end - 1) + { + vma->start = locals.curr_start; + vma->end = locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end = locals.prev_end; +# else + vma->next_start = 0; +# endif + locals.retval = 0; + } + } + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + + /* FreeBSD 6.[01] doesn't allow to distinguish unmapped pages from + mapped but swapped-out pages. See whether it's fixed. */ + if (!is_mapped (0)) + /* OK, mincore() appears to work as expected. */ + return mincore_get_vma (address, vma); + return -1; +} + +/* --------------------------- stackvma-netbsd.c --------------------------- */ + +#elif defined __NetBSD__ /* NetBSD */ + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; + /* The stack appears as multiple adjacents segments, therefore we + merge adjacent segments. */ + uintptr_t curr_start, curr_end; +# if STACK_DIRECTION < 0 + uintptr_t prev_end; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ + if (start == locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end = end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->vma->prev_end = locals->prev_end; + locals->retval = 0; + return 1; + } + locals->prev_end = locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = locals->curr_start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + locals->curr_start = start; locals->curr_end = end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; + locals.curr_start = 0; + locals.curr_end = 0; +# if STACK_DIRECTION < 0 + locals.prev_end = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >= locals.curr_start && address <= locals.curr_end - 1) + { + vma->start = locals.curr_start; + vma->end = locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end = locals.prev_end; +# else + vma->next_start = 0; +# endif + locals.retval = 0; + } + } + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-mquery.c --------------------------- */ + +/* mquery() is a system call that allows to inquire the status of a + range of pages of virtual memory. In particular, it allows to inquire + whether a page is mapped at all, and where is the next unmapped page + after a given address. + As of 2021, mquery() is supported by: + - OpenBSD, since OpenBSD 3.4. + Note that this file can give different results. For example, on + OpenBSD 4.4 / i386 the stack segment (which starts around 0xcdbfe000) + ends at 0xcfbfdfff according to mincore, but at 0xffffffff according to + mquery. */ + +#elif defined __OpenBSD__ /* OpenBSD */ + +# include /* getpagesize, mincore */ +# include +# include /* mincore */ + +/* Cache for getpagesize(). */ +static uintptr_t pagesize; + +/* Initialize pagesize. */ +static void +init_pagesize (void) +{ + pagesize = getpagesize (); +} + +/* Test whether the page starting at ADDR is among the address range. + ADDR must be a multiple of pagesize. */ +static int +is_mapped (uintptr_t addr) +{ + /* Avoid calling mquery with a NULL first argument, because this argument + value has a specific meaning. We know the NULL page is unmapped. */ + if (addr == 0) + return 0; + return mquery ((void *) addr, pagesize, 0, MAP_FIXED, -1, 0) == (void *) -1; +} + +/* Assuming that the page starting at ADDR is among the address range, + return the start of its virtual memory range. + ADDR must be a multiple of pagesize. */ +static uintptr_t +mapped_range_start (uintptr_t addr) +{ + uintptr_t stepsize; + uintptr_t known_unmapped_page; + + /* Look at smaller addresses, in larger and larger steps, to minimize the + number of mquery() calls. */ + stepsize = pagesize; + for (;;) + { + uintptr_t hole; + + if (addr == 0) + abort (); + + if (addr <= stepsize) + { + known_unmapped_page = 0; + break; + } + + hole = (uintptr_t) mquery ((void *) (addr - stepsize), pagesize, + 0, 0, -1, 0); + if (!(hole == (uintptr_t) (void *) -1 || hole >= addr)) + { + /* Some part of [addr - stepsize, addr - 1] is unmapped. */ + known_unmapped_page = hole; + break; + } + + /* The entire range [addr - stepsize, addr - 1] is mapped. */ + addr -= stepsize; + + if (2 * stepsize > stepsize && 2 * stepsize < addr) + stepsize = 2 * stepsize; + } + + /* Now reduce the step size again. + We know that the page at known_unmapped_page is unmapped and that + 0 < addr - known_unmapped_page <= stepsize. */ + while (stepsize > pagesize && stepsize / 2 >= addr - known_unmapped_page) + stepsize = stepsize / 2; + /* Still 0 < addr - known_unmapped_page <= stepsize. */ + while (stepsize > pagesize) + { + uintptr_t hole; + + stepsize = stepsize / 2; + hole = (uintptr_t) mquery ((void *) (addr - stepsize), pagesize, + 0, 0, -1, 0); + if (!(hole == (uintptr_t) (void *) -1 || hole >= addr)) + /* Some part of [addr - stepsize, addr - 1] is unmapped. */ + known_unmapped_page = hole; + else + /* The entire range [addr - stepsize, addr - 1] is mapped. */ + addr -= stepsize; + /* Still 0 < addr - known_unmapped_page <= stepsize. */ + } + + return addr; +} + +/* Assuming that the page starting at ADDR is among the address range, + return the end of its virtual memory range + 1. + ADDR must be a multiple of pagesize. */ +static uintptr_t +mapped_range_end (uintptr_t addr) +{ + uintptr_t end; + + if (addr == 0) + abort (); + + end = (uintptr_t) mquery ((void *) addr, pagesize, 0, 0, -1, 0); + if (end == (uintptr_t) (void *) -1) + end = 0; /* wrap around */ + return end; +} + +/* Determine whether an address range [ADDR1..ADDR2] is completely unmapped. + ADDR1 must be <= ADDR2. */ +static int +is_unmapped (uintptr_t addr1, uintptr_t addr2) +{ + /* Round addr1 down. */ + addr1 = (addr1 / pagesize) * pagesize; + /* Round addr2 up and turn it into an exclusive bound. */ + addr2 = ((addr2 / pagesize) + 1) * pagesize; + + /* Avoid calling mquery with a NULL first argument, because this argument + value has a specific meaning. We know the NULL page is unmapped. */ + if (addr1 == 0) + addr1 = pagesize; + + if (addr1 < addr2) + { + if (mquery ((void *) addr1, addr2 - addr1, 0, MAP_FIXED, -1, 0) + == (void *) -1) + /* Not all the interval [addr1 .. addr2 - 1] is unmapped. */ + return 0; + else + /* The interval [addr1 .. addr2 - 1] is unmapped. */ + return 1; + } + return 1; +} + +# if STACK_DIRECTION < 0 + +/* Info about the gap between this VMA and the previous one. + addr must be < vma->start. */ +static int +mquery_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + /* vma->start - addr <= (vma->start - vma->prev_end) / 2 + is mathematically equivalent to + vma->prev_end <= 2 * addr - vma->start + <==> is_unmapped (2 * addr - vma->start, vma->start - 1). + But be careful about overflow: if 2 * addr - vma->start is negative, + we consider a tiny "guard page" mapping [0, 0] to be present around + NULL; it intersects the range (2 * addr - vma->start, vma->start - 1), + therefore return false. */ + uintptr_t testaddr = addr - (vma->start - addr); + if (testaddr > addr) /* overflow? */ + return 0; + /* Here testaddr <= addr < vma->start. */ + return is_unmapped (testaddr, vma->start - 1); +} + +# endif +# if STACK_DIRECTION > 0 + +/* Info about the gap between this VMA and the next one. + addr must be > vma->end - 1. */ +static int +mquery_is_near_this (uintptr_t addr, struct vma_struct *vma) +{ + /* addr - vma->end < (vma->next_start - vma->end) / 2 + is mathematically equivalent to + vma->next_start > 2 * addr - vma->end + <==> is_unmapped (vma->end, 2 * addr - vma->end). + But be careful about overflow: if 2 * addr - vma->end is > ~0UL, + we consider a tiny "guard page" mapping [0, 0] to be present around + NULL; it intersects the range (vma->end, 2 * addr - vma->end), + therefore return false. */ + uintptr_t testaddr = addr + (addr - vma->end); + if (testaddr < addr) /* overflow? */ + return 0; + /* Here vma->end - 1 < addr <= testaddr. */ + return is_unmapped (vma->end, testaddr); +} + +# endif + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + if (pagesize == 0) + init_pagesize (); + address = (address / pagesize) * pagesize; + vma->start = mapped_range_start (address); + vma->end = mapped_range_end (address); + vma->is_near_this = mquery_is_near_this; + return 0; +} + +/* ---------------------------- stackvma-mach.c ---------------------------- */ + +#elif (defined __APPLE__ && defined __MACH__) /* macOS */ + +#include +#include +#include +#include + +int +sigsegv_get_vma (uintptr_t req_address, struct vma_struct *vma) +{ + uintptr_t prev_address = 0, prev_size = 0; + uintptr_t join_address = 0, join_size = 0; + int more = 1; + vm_address_t address; + vm_size_t size; + task_t task = mach_task_self (); + + for (address = VM_MIN_ADDRESS; more; address += size) + { + mach_port_t object_name; + /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have + 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas + mach_vm_address_t and mach_vm_size_t are always 64 bits large. + MacOS X 10.5 has three vm_region like methods: + - vm_region. It has arguments that depend on whether the current + process is 32-bit or 64-bit. When linking dynamically, this + function exists only in 32-bit processes. Therefore we use it only + in 32-bit processes. + - vm_region_64. It has arguments that depend on whether the current + process is 32-bit or 64-bit. It interprets a flavor + VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is + dangerous since 'struct vm_region_basic_info_64' is larger than + 'struct vm_region_basic_info'; therefore let's write + VM_REGION_BASIC_INFO_64 explicitly. + - mach_vm_region. It has arguments that are 64-bit always. This + function is useful when you want to access the VM of a process + other than the current process. + In 64-bit processes, we could use vm_region_64 or mach_vm_region. + I choose vm_region_64 because it uses the same types as vm_region, + resulting in less conditional code. */ +# if defined __aarch64__ || defined __ppc64__ || defined __x86_64__ + struct vm_region_basic_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + + more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, &info_count, &object_name) + == KERN_SUCCESS); +# else + struct vm_region_basic_info info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + + more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&info, &info_count, &object_name) + == KERN_SUCCESS); +# endif + if (!more) + { + address = join_address + join_size; + size = 0; + } + + if ((uintptr_t) address == join_address + join_size) + join_size += size; + else + { + prev_address = join_address; + prev_size = join_size; + join_address = (uintptr_t) address; + join_size = size; + } + + if (object_name != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), object_name); + +# if STACK_DIRECTION < 0 + if (join_address <= req_address && join_address + join_size > req_address) + { + vma->start = join_address; + vma->end = join_address + join_size; + vma->prev_end = prev_address + prev_size; + vma->is_near_this = simple_is_near_this; + return 0; + } +# else + if (prev_address <= req_address && prev_address + prev_size > req_address) + { + vma->start = prev_address; + vma->end = prev_address + prev_size; + vma->next_start = join_address; + vma->is_near_this = simple_is_near_this; + return 0; + } +# endif + } + +# if STACK_DIRECTION > 0 + if (join_address <= req_address && join_address + size > req_address) + { + vma->start = prev_address; + vma->end = prev_address + prev_size; + vma->next_start = ~0UL; + vma->is_near_this = simple_is_near_this; + return 0; + } +# endif + + return -1; +} + +/* -------------------------------------------------------------------------- */ + +#elif defined _AIX /* AIX */ + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-procfs.h --------------------------- */ + +#elif defined __sgi || defined __sun /* IRIX, Solaris */ + +# include /* errno, EINTR */ +# include /* open, O_RDONLY */ +# include /* size_t */ +# include /* getpagesize, getpid, read, close */ +# include +# include /* mmap, munmap */ +# include /* fstat */ +# include /* memcpy */ + +/* Try to use the newer ("structured") /proc filesystem API, if supported. */ +# define _STRUCTURED_PROC 1 +# include /* prmap_t, optionally PIOC* */ + +# if !defined __sun + +/* Cache for getpagesize(). */ +static uintptr_t pagesize; + +/* Initialize pagesize. */ +static void +init_pagesize (void) +{ + pagesize = getpagesize (); +} + +# endif + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; +# if STACK_DIRECTION < 0 + uintptr_t prev; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ +# if STACK_DIRECTION < 0 + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->vma->prev_end = locals->prev; + locals->retval = 0; + return 1; + } + locals->prev = end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + return 0; +} + +/* Iterate over the virtual memory areas of the current process. + If such iteration is supported, the callback is called once for every + virtual memory area, in ascending order, with the following arguments: + - LOCALS is the same argument as passed to vma_iterate. + - START is the address of the first byte in the area, page-aligned. + - END is the address of the last byte in the area plus 1, page-aligned. + Note that it may be 0 for the last area in the address space. + If the callback returns 0, the iteration continues. If it returns 1, + the iteration terminates prematurely. + This function may open file descriptors, but does not call malloc(). + Return 0 if all went well, or -1 in case of error. */ +/* This code is a simplied copy (no handling of protection flags) of the + code in gnulib's lib/vma-iter.c. */ +static int +vma_iterate (struct callback_locals *locals) +{ + /* Note: Solaris defines a different type prmap_t with + _STRUCTURED_PROC than without! Here's a table of sizeof(prmap_t): + 32-bit 64-bit + _STRUCTURED_PROC = 0 32 56 + _STRUCTURED_PROC = 1 96 104 + Therefore, if the include files provide the newer API, prmap_t has + the bigger size, and thus you MUST use the newer API. And if the + include files provide the older API, prmap_t has the smaller size, + and thus you MUST use the older API. */ + +# if defined PIOCNMAP && defined PIOCMAP + /* We must use the older /proc interface. */ + + char fnamebuf[6+10+1]; + char *fname; + int fd; + int nmaps; + size_t memneed; +# if HAVE_MAP_ANONYMOUS +# define zero_fd -1 +# define map_flags MAP_ANONYMOUS +# else /* !HAVE_MAP_ANONYMOUS */ + int zero_fd; +# define map_flags 0 +# endif + void *auxmap; + uintptr_t auxmap_start; + uintptr_t auxmap_end; + prmap_t* maps; + prmap_t* mp; + + if (pagesize == 0) + init_pagesize (); + + /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */ + fname = fnamebuf + sizeof (fnamebuf) - 1; + *fname = '\0'; + { + unsigned int value = getpid (); + do + *--fname = (value % 10) + '0'; + while ((value = value / 10) > 0); + } + fname -= 6; + memcpy (fname, "/proc/", 6); + + fd = open (fname, O_RDONLY); + if (fd < 0) + return -1; + + if (ioctl (fd, PIOCNMAP, &nmaps) < 0) + goto fail2; + + memneed = (nmaps + 10) * sizeof (prmap_t); + /* Allocate memneed bytes of memory. + We cannot use alloca here, because not much stack space is guaranteed. + We also cannot use malloc here, because a malloc() call may call mmap() + and thus pre-allocate available memory. + So use mmap(), and ignore the resulting VMA. */ + memneed = ((memneed - 1) / pagesize + 1) * pagesize; +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); + if (zero_fd < 0) + goto fail2; +# endif + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + map_flags | MAP_PRIVATE, zero_fd, 0); +# if !HAVE_MAP_ANONYMOUS + close (zero_fd); +# endif + if (auxmap == (void *) -1) + goto fail2; + auxmap_start = (uintptr_t) auxmap; + auxmap_end = auxmap_start + memneed; + maps = (prmap_t *) auxmap; + + if (ioctl (fd, PIOCMAP, maps) < 0) + goto fail1; + + for (mp = maps;;) + { + uintptr_t start, end; + + start = (uintptr_t) mp->pr_vaddr; + end = start + mp->pr_size; + if (start == 0 && end == 0) + break; + mp++; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (locals, start, auxmap_start)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (locals, auxmap_end, end)) + break; + } + else + { + if (callback (locals, start, end)) + break; + } + } + munmap (auxmap, memneed); + close (fd); + return 0; + + fail1: + munmap (auxmap, memneed); + fail2: + close (fd); + return -1; + +# else + /* We must use the newer /proc interface. + Documentation: + https://docs.oracle.com/cd/E23824_01/html/821-1473/proc-4.html + The contents of /proc//map consists of records of type + prmap_t. These are different in 32-bit and 64-bit processes, + but here we are fortunately accessing only the current process. */ + + char fnamebuf[6+10+4+1]; + char *fname; + int fd; + int nmaps; + size_t memneed; +# if HAVE_MAP_ANONYMOUS +# define zero_fd -1 +# define map_flags MAP_ANONYMOUS +# else /* !HAVE_MAP_ANONYMOUS */ + int zero_fd; +# define map_flags 0 +# endif + void *auxmap; + uintptr_t auxmap_start; + uintptr_t auxmap_end; + prmap_t* maps; + prmap_t* maps_end; + prmap_t* mp; + + if (pagesize == 0) + init_pagesize (); + + /* Construct fname = sprintf (fnamebuf+i, "/proc/%u/map", getpid ()). */ + fname = fnamebuf + sizeof (fnamebuf) - 1 - 4; + memcpy (fname, "/map", 4 + 1); + { + unsigned int value = getpid (); + do + *--fname = (value % 10) + '0'; + while ((value = value / 10) > 0); + } + fname -= 6; + memcpy (fname, "/proc/", 6); + + fd = open (fname, O_RDONLY); + if (fd < 0) + return -1; + + { + struct stat statbuf; + if (fstat (fd, &statbuf) < 0) + goto fail2; + nmaps = statbuf.st_size / sizeof (prmap_t); + } + + memneed = (nmaps + 10) * sizeof (prmap_t); + /* Allocate memneed bytes of memory. + We cannot use alloca here, because not much stack space is guaranteed. + We also cannot use malloc here, because a malloc() call may call mmap() + and thus pre-allocate available memory. + So use mmap(), and ignore the resulting VMA. */ + memneed = ((memneed - 1) / pagesize + 1) * pagesize; +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); + if (zero_fd < 0) + goto fail2; +# endif + auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + map_flags | MAP_PRIVATE, zero_fd, 0); +# if !HAVE_MAP_ANONYMOUS + close (zero_fd); +# endif + if (auxmap == (void *) -1) + goto fail2; + auxmap_start = (uintptr_t) auxmap; + auxmap_end = auxmap_start + memneed; + maps = (prmap_t *) auxmap; + + /* Read up to memneed bytes from fd into maps. */ + { + size_t remaining = memneed; + size_t total_read = 0; + char *ptr = (char *) maps; + + do + { + size_t nread = read (fd, ptr, remaining); + if (nread == (size_t)-1) + { + if (errno == EINTR) + continue; + goto fail1; + } + if (nread == 0) + /* EOF */ + break; + total_read += nread; + ptr += nread; + remaining -= nread; + } + while (remaining > 0); + + nmaps = (memneed - remaining) / sizeof (prmap_t); + maps_end = maps + nmaps; + } + + for (mp = maps; mp < maps_end; mp++) + { + uintptr_t start, end; + + start = (uintptr_t) mp->pr_vaddr; + end = start + mp->pr_size; + if (start <= auxmap_start && auxmap_end - 1 <= end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + = [start,auxmap_start-1] u [auxmap_end,end-1]. */ + if (start < auxmap_start) + if (callback (locals, start, auxmap_start)) + break; + if (auxmap_end - 1 < end - 1) + if (callback (locals, auxmap_end, end)) + break; + } + else + { + if (callback (locals, start, end)) + break; + } + } + munmap (auxmap, memneed); + close (fd); + return 0; + + fail1: + munmap (auxmap, memneed); + fail2: + close (fd); + return -1; + +# endif +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; +# if STACK_DIRECTION < 0 + locals.prev = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + +# if defined __sun + return mincore_get_vma (address, vma); +# else + return -1; +# endif +} + +/* -------------------------------------------------------------------------- */ + +#elif defined __CYGWIN__ /* Cygwin */ + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; + /* The stack appears as three adjacents segments, therefore we + merge adjacent segments. */ + uintptr_t curr_start, curr_end; +# if STACK_DIRECTION < 0 + uintptr_t prev_end; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ + if (start == locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end = end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->vma->prev_end = locals->prev_end; + locals->retval = 0; + return 1; + } + locals->prev_end = locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = locals->curr_start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >= locals->curr_start + && locals->address <= locals->curr_end - 1) + { + locals->vma->start = locals->curr_start; + locals->vma->end = locals->curr_end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + locals->curr_start = start; locals->curr_end = end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; + locals.curr_start = 0; + locals.curr_end = 0; +# if STACK_DIRECTION < 0 + locals.prev_end = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >= locals.curr_start && address <= locals.curr_end - 1) + { + vma->start = locals.curr_start; + vma->end = locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end = locals.prev_end; +# else + vma->next_start = 0; +# endif + locals.retval = 0; + } + } + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + + return -1; +} + +/* ---------------------------- stackvma-beos.h ---------------------------- */ + +#elif defined __HAIKU__ /* Haiku */ + +# include /* get_next_area_info */ + +struct callback_locals +{ + uintptr_t address; + struct vma_struct *vma; +# if STACK_DIRECTION < 0 + uintptr_t prev; +# else + int stop_at_next_vma; +# endif + int retval; +}; + +static int +callback (struct callback_locals *locals, uintptr_t start, uintptr_t end) +{ +# if STACK_DIRECTION < 0 + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->vma->prev_end = locals->prev; + locals->retval = 0; + return 1; + } + locals->prev = end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start = start; + locals->stop_at_next_vma = 0; + return 1; + } + if (locals->address >= start && locals->address <= end - 1) + { + locals->vma->start = start; + locals->vma->end = end; + locals->retval = 0; + locals->stop_at_next_vma = 1; + return 0; + } +# endif + return 0; +} + +/* Iterate over the virtual memory areas of the current process. + If such iteration is supported, the callback is called once for every + virtual memory area, in ascending order, with the following arguments: + - LOCALS is the same argument as passed to vma_iterate. + - START is the address of the first byte in the area, page-aligned. + - END is the address of the last byte in the area plus 1, page-aligned. + Note that it may be 0 for the last area in the address space. + If the callback returns 0, the iteration continues. If it returns 1, + the iteration terminates prematurely. + This function may open file descriptors, but does not call malloc(). + Return 0 if all went well, or -1 in case of error. */ +/* This code is a simplied copy (no handling of protection flags) of the + code in gnulib's lib/vma-iter.c. */ +static int +vma_iterate (struct callback_locals *locals) +{ + area_info info; + ssize_t cookie; + + cookie = 0; + while (get_next_area_info (0, &cookie, &info) == B_OK) + { + uintptr_t start, end; + + start = (uintptr_t) info.address; + end = start + info.size; + + if (callback (locals, start, end)) + break; + } + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address = address; + locals.vma = vma; +# if STACK_DIRECTION < 0 + locals.prev = 0; +# else + locals.stop_at_next_vma = 0; +# endif + locals.retval = -1; + + vma_iterate (&locals); + if (locals.retval == 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start = 0; +# endif + vma->is_near_this = simple_is_near_this; + return 0; + } + return -1; +} + +/* -------------------------------------------------------------------------- */ + +#else /* Hurd, Minix, ... */ + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + /* No way. */ + return -1; +} + +#endif diff --git a/lib/stackvma.h b/lib/stackvma.h new file mode 100644 index 0000000..7296801 --- /dev/null +++ b/lib/stackvma.h @@ -0,0 +1,60 @@ +/* Determine the virtual memory area of a given address. + Copyright (C) 2002-2021 Bruno Haible + Copyright (C) 2003-2006 Paolo Bonzini + + 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 . */ + +#ifndef _STACKVMA_H +#define _STACKVMA_H + +#include + +/* Describes a virtual memory area, with some info about the gap between + it and the next or previous virtual memory area. */ +struct vma_struct +{ + uintptr_t start; + uintptr_t end; +#if STACK_DIRECTION < 0 + /* Info about the gap between this VMA and the previous one. + addr must be < vma->start. */ + int (*is_near_this) (uintptr_t addr, struct vma_struct *vma); + /* Private field, not provided by all sigsegv_get_vma implementations. */ + uintptr_t prev_end; +#endif +#if STACK_DIRECTION > 0 + /* Info about the gap between this VMA and the next one. + addr must be > vma->end - 1. */ + int (*is_near_this) (uintptr_t addr, struct vma_struct *vma); + /* Private field, not provided by all sigsegv_get_vma implementations. */ + uintptr_t next_start; +#endif +}; + +/* Determines the virtual memory area to which a given address belongs, + and returns 0. Returns -1 if it cannot be determined. + This function is used to determine the stack extent when a fault occurs. */ +extern int sigsegv_get_vma (uintptr_t address, struct vma_struct *vma); + +/* Defined if sigsegv_get_vma actually works (i.e. does not always fail). */ +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ \ + || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__) \ + || defined _AIX || defined __sgi || defined __sun \ + || defined __CYGWIN__ || defined __HAIKU__ +# define HAVE_STACKVMA 1 +#endif + +#endif /* _STACKVMA_H */ diff --git a/m4/sigaltstack.m4 b/m4/sigaltstack.m4 new file mode 100644 index 0000000..212e9d3 --- /dev/null +++ b/m4/sigaltstack.m4 @@ -0,0 +1,198 @@ +# sigaltstack.m4 serial 12 +dnl Copyright (C) 2002-2021 Bruno Haible +dnl Copyright (C) 2008 Eric Blake +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +AC_DEFUN([SV_SIGALTSTACK], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + AC_CHECK_FUNCS_ONCE([sigaltstack setrlimit]) + + if test "$ac_cv_func_sigaltstack" = yes; then + AC_CHECK_TYPE([stack_t], , + [AC_DEFINE(stack_t, [struct sigaltstack], + [Define to 'struct sigaltstack' if that's the type of the argument to sigaltstack]) + ], + [ +#include +#if HAVE_SYS_SIGNAL_H +# include +#endif + ]) + fi + + AC_CACHE_CHECK([for working sigaltstack], [sv_cv_sigaltstack], [ + if test "$ac_cv_func_sigaltstack" = yes; then + case "$host_os" in + macos* | darwin[[6-9]]* | darwin[[1-9]][[0-9]]*) + # On MacOS X 10.2 or newer, just assume that if it compiles, it will + # work. If we were to perform the real test, 1 Crash Report dialog + # window would pop up. + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[int x = SA_ONSTACK; stack_t ss; sigaltstack ((stack_t*)0, &ss);]])], + [sv_cv_sigaltstack="guessing yes"], + [sv_cv_sigaltstack=no]) + ;; + *) + AC_RUN_IFELSE([ + AC_LANG_SOURCE([[ +#include +#include +#if HAVE_SYS_SIGNAL_H +# include +#endif +#if HAVE_SETRLIMIT +# include +# include +# include +#endif +#ifndef SIGSTKSZ +# define SIGSTKSZ 16384 +#endif +void stackoverflow_handler (int sig) +{ + /* If we get here, the stack overflow was caught. */ + exit (0); +} +volatile int * recurse_1 (volatile int n, volatile int *p) +{ + if (n >= 0) + *recurse_1 (n + 1, p) += n; + return p; +} +int recurse (volatile int n) +{ + int sum = 0; + return *recurse_1 (n, &sum); +} +char mystack[2 * SIGSTKSZ]; +int main () +{ + stack_t altstack; + struct sigaction action; +#if defined 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 + /* Install the alternate stack. Use the midpoint of mystack, to guard + against a buggy interpretation of ss_sp on IRIX. */ + altstack.ss_sp = mystack + SIGSTKSZ; + altstack.ss_size = SIGSTKSZ; + altstack.ss_flags = 0; /* no SS_DISABLE */ + if (sigaltstack (&altstack, NULL) < 0) + exit (1); + /* Install the SIGSEGV handler. */ + sigemptyset (&action.sa_mask); + action.sa_handler = &stackoverflow_handler; + action.sa_flags = SA_ONSTACK; + sigaction (SIGSEGV, &action, (struct sigaction *) NULL); + sigaction (SIGBUS, &action, (struct sigaction *) NULL); + /* Provoke a stack overflow. */ + recurse (0); + exit (2); +}]])], + [sv_cv_sigaltstack=yes], + [sv_cv_sigaltstack=no], + [ + dnl FIXME: Put in some more known values here. + case "$host_os" in + *) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[int x = SA_ONSTACK; stack_t ss; sigaltstack ((stack_t*)0, &ss);]])], + [sv_cv_sigaltstack="guessing yes"], + [sv_cv_sigaltstack=no]) + ;; + esac + ]) + ;; + esac + else + sv_cv_sigaltstack=no + fi + ]) + if test "$sv_cv_sigaltstack" != no; then + AC_DEFINE([HAVE_WORKING_SIGALTSTACK], [1], + [Define if you have the sigaltstack() function and it works.]) + + 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], + [sv_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 (3); +}]])], + [sv_cv_sigaltstack_low_base=yes], + [sv_cv_sigaltstack_low_base=no], + [ + dnl FIXME: Put in some more known values here. + case "$host_os" in + irix5*) sv_cv_sigaltstack_low_base="no" ;; + *) sv_cv_sigaltstack_low_base="guessing yes" ;; + esac + ]) + ]) + if test "$sv_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 + fi +]) diff --git a/m4/stack-direction.m4 b/m4/stack-direction.m4 new file mode 100644 index 0000000..c7a20a2 --- /dev/null +++ b/m4/stack-direction.m4 @@ -0,0 +1,104 @@ +# stack-direction.m4 serial 6 +dnl Copyright (C) 2002-2021 Bruno Haible +dnl Copyright (C) 2002-2021 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +# Determine the stack direction. Define the C macro STACK_DIRECTION. +AC_DEFUN([SV_STACK_DIRECTION], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([for stack direction], [sv_cv_stack_direction_msg], [ + case "$host_cpu" in + dnl See the #define STACK_GROWS_DOWNWARD in gcc-3.1/gcc/config/*/*.h. + a29k | \ + aarch64* | \ + alpha* | \ + arc | \ + arm* | strongarm* | xscale* | \ + avr | avr32 | \ + bfin | \ + c1 | c2 | c32 | c34 | c38 | \ + clipper | \ + cris | \ + d30v | \ + elxsi | \ + fr30 | \ + h8300 | \ + i?86 | x86_64 | \ + i860 | \ + ia64 | \ + m32r | \ + m68* | \ + m88k | \ + mcore | \ + microblaze | \ + mips* | \ + mmix | \ + mn10200 | \ + mn10300 | \ + nios2 | \ + nds32* | \ + ns32k | \ + pdp11 | \ + pj* | \ + powerpc* | rs6000 | \ + riscv* | \ + romp | \ + s390* | \ + sh* | \ + sparc* | \ + v850 | \ + vax | \ + xtensa) + sv_cv_stack_direction=-1 ;; + c4x | \ + dsp16xx | \ + i960 | \ + hppa* | parisc* | \ + stormy16 | \ + we32k) + sv_cv_stack_direction=1 ;; + *) + if test $cross_compiling = no; then + cat > conftest.c < +int +find_stack_direction (int *addr, int depth) +{ + int dir, dummy = 0; + if (! addr) + addr = &dummy; + *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; + dir = depth ? find_stack_direction (addr, depth - 1) : 0; + return dir + dummy; +} +int +main (int argc, char *argv[]) +{ + printf ("%d\n", find_stack_direction (NULL, argc + 20)); + return 0; +} +EOF + AC_TRY_EVAL([ac_link]) + sv_cv_stack_direction=`./conftest` + else + sv_cv_stack_direction=0 + fi + ;; + esac + case $sv_cv_stack_direction in + 1) sv_cv_stack_direction_msg="grows up";; + -1) sv_cv_stack_direction_msg="grows down";; + *) sv_cv_stack_direction_msg="unknown";; + esac + ]) + AC_DEFINE_UNQUOTED([STACK_DIRECTION], [$sv_cv_stack_direction], + [Define as the direction of stack growth for your system. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => spaghetti stack.]) +]) diff --git a/modules/sigsegv b/modules/sigsegv new file mode 100644 index 0000000..2ef8433 --- /dev/null +++ b/modules/sigsegv @@ -0,0 +1,101 @@ +Description: +A simplified variant of GNU libsigsegv. +It implements the most important features of GNU libsigsegv: catching SIGSEGV +and catching stack overflow. It does *not* implement the 'sigsegv_dispatcher' +type (which is not multithread-safe). +It supports all modern Unix-like platforms: Linux, Hurd, FreeBSD, NetBSD, +OpenBSD, macOS, AIX, Solaris, Cygwin, Haiku, even IRIX. It does *not* support +HP-UX, Minix, native Windows; on these platforms the module compiles and +provides a header file, but it does not define HAVE_SIGSEGV_RECOVERY +and HAVE_STACK_OVERFLOW_RECOVERY. +Unlike GNU libsigsegv, which consists of many .h and .c files, this module +compiles to just two object files, rather than a library. + +Files: +lib/sigsegv.in.h +lib/sigsegv.c +lib/stackvma.h +lib/stackvma.c +m4/mmap-anon.m4 +m4/sigaltstack.m4 +m4/stack-direction.m4 +m4/libsigsegv.m4 + +Depends-on: +havelib +host-cpu-c-abi +stdint +getpagesize + +configure.ac: +AC_ARG_WITH([libsigsegv], + [AS_HELP_STRING([--with-libsigsegv], + [use the GNU libsigsegv library, when present, instead of the gnulib module 'sigsegv'])]) +SIGSEGV_H=sigsegv.h +if test "$with_libsigsegv" = yes; then + gl_LIBSIGSEGV + if test "$gl_cv_lib_sigsegv" = yes; then + SIGSEGV_H= + fi +fi +AC_SUBST([SIGSEGV_H]) +AM_CONDITIONAL([GL_GENERATE_SIGSEGV_H], [test -n "$SIGSEGV_H"]) +if test -n "$SIGSEGV_H"; then + dnl Persuade glibc to declare macros designating register + dnl indices: REG_RSP on x86_64, REG_ESP on i386. + dnl Persuade Solaris OpenIndiana to include , + dnl which declares macros designating register indices, such as ESP on i386. + dnl Persuade Solaris OpenIndiana to declare mincore(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + + AC_REQUIRE([AC_CANONICAL_HOST]) + case "$host_os" in + solaris2.11) + AC_DEFINE([SOLARIS11], [1], [Define on Solaris 11 and its derivates.]) + ;; + esac + + gl_FUNC_MMAP_ANON + + dnl Stack direction. + SV_STACK_DIRECTION + + dnl Catching stack overflow requires an alternate signal stack. + dnl The old "install a guard page" trick would be unreliable, because + dnl we don't know where exactly to place the guard page. + SV_SIGALTSTACK + + AC_CHECK_FUNCS_ONCE([getrlimit]) +fi + +Makefile.am: +BUILT_SOURCES += $(SIGSEGV_H) + +if GL_GENERATE_SIGSEGV_H +sigsegv.h: sigsegv.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/sigsegv.in.h; \ + } > $@-t && \ + mv $@-t $@ +else +sigsegv.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += sigsegv.h sigsegv.h-t + +if GL_GENERATE_SIGSEGV_H +lib_SOURCES += sigsegv.c stackvma.c +endif + +Include: + + +Link: +$(LTLIBSIGSEGV) when linking with libtool, $(LIBSIGSEGV) otherwise + +License: +GPLv2+ + +Maintainer: +Bruno Haible -- 2.7.4