From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS22989 209.51.188.0/24 X-Spam-Status: No, score=-3.7 required=3.0 tests=AWL,BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS, SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id A8B201F5AE for ; Sun, 16 May 2021 17:02:25 +0000 (UTC) Received: from localhost ([::1]:36438 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1liK9o-0001I1-5n for normalperson@yhbt.net; Sun, 16 May 2021 13:02:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35526) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1liK9U-0001Gm-M1; Sun, 16 May 2021 13:02:04 -0400 Received: from mo4-p00-ob.smtp.rzone.de ([81.169.146.221]:19836) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1liK9M-0003Cs-C5; Sun, 16 May 2021 13:02:04 -0400 ARC-Seal: i=1; a=rsa-sha256; t=1621184508; cv=none; d=strato.com; s=strato-dkim-0002; b=cPKGhNz4eoz4Bx4cWWc8WXCk5asrqnbgij/C71ysxcXB2tuB/Y/8RkHE+hCWTVpypX W68cRl7VoKNLzJgdckujssHakp69zJYo5R/jKe1SKCyq9ovIRBVazdiEcQicJ+19mQlC KhE6ZhyzDhpuwzj0Jq/DlQH7zr9MTtjdxheU9x8VpTQOWIjutjbyFbDkx9oD3TsH5HPM MOMsfhk9ldDo8oeuYPiw4WK/k3DwGSqCFC1vDW/Y+vzSRZBNrnTLD/q9zHxVog+GaZ1S p0OK3cAwFBUWHxwRzMW/+CN9dwn2jOSBG7z1ODrHftxFE1+DrgNNybfYDpmHVdrxAche Djgw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1621184508; s=strato-dkim-0002; d=strato.com; h=Message-ID:Date:Subject:Cc:To:From:Cc:Date:From:Subject:Sender; bh=PvMdNXp/+IsnWL0lBnQ5f65FUC2CClsoRs9d0tsc3QY=; b=mjjyVIzcR2ZeditGiGtPe8uJyh74jaxMdEQFvNHXbNPybF68UHE3SeKkKwocuQOXvX weQ6JYWyET8p5xso+riJTXTOhJFRDJbBtTEhVn3vv4HvPmEna8Kss7PNJNalK4rdxySh xRO0a3wT3gX5UjISwkx8qIOo//tFOqzkh9iy0RZySAsEFjN2aNJKBz/17eKdby6+oPU8 UG0DJsZ6V70jr1hjSfwGtIYV/74jKwpuoPcdOb7qVNJjAvCDGW3c0Pc7nAKnxGKPAyAG MIh1OLWfzrsFd40jBoe4xGnZjo3TfdI9gQxp/6zI2g4nVNrirU57nCUXJ8z3Zj6rt1a1 7+zw== ARC-Authentication-Results: i=1; strato.com; dkim=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1621184508; s=strato-dkim-0002; d=clisp.org; h=Message-ID:Date:Subject:Cc:To:From:Cc:Date:From:Subject:Sender; bh=PvMdNXp/+IsnWL0lBnQ5f65FUC2CClsoRs9d0tsc3QY=; b=IvgDIe8f4NYq7wGFJyC9MPJ0GLpG2qc+dFA0vRrBcaUh1wEVwgWAdMYCeBBT7UbYrE sTVWxIx/s/bXehGHVfiu8NxhjtrTtbrguj0Z0eaFaLFfUIJJfbsZ8CAQz8C979qsK+CR UBr6/rdIemdK+UH+dgak+KDMfIXCfge2ilxQy8VAC3sCX/JAHXaDWR5tdp5UCm/Hvg8K o620R9JaBEw2hjy9rx3hv8EbzaVOqgjxGO231rUf2lK4mGueeQdLgNnjliKmhOEIF5jr Pcf25kbnEgDx0gViDbAwZIB01fitmHloSE/+yYI9w5+ZpjjEz2WYD8kVG0my/TJX0ytP x43w== Authentication-Results: strato.com; dkim=none X-RZG-AUTH: ":Ln4Re0+Ic/6oZXR1YgKryK8brlshOcZlIWs+iCP5vnk6shH+AHjwLuWOHqf3z5NW" X-RZG-CLASS-ID: mo00 Received: from bruno.haible.de by smtp.strato.de (RZmta 47.26.0 DYNA|AUTH) with ESMTPSA id m0b081x4GH1k02e (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (curve X9_62_prime256v1 with 256 ECDH bits, eq. 3072 bits RSA)) (Client did not present a certificate); Sun, 16 May 2021 19:01:46 +0200 (CEST) From: Bruno Haible To: bug-gnulib@gnu.org Subject: new module 'sigsegv' Date: Sun, 16 May 2021 19:01:45 +0200 Message-ID: <15897619.AQZsorJ5YP@omega> User-Agent: KMail/5.1.3 (Linux/4.4.0-206-generic; KDE/5.18.0; x86_64; ; ) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart13627053.fd3Gv6meWk" Content-Transfer-Encoding: 7Bit Received-SPF: none client-ip=81.169.146.221; envelope-from=bruno@clisp.org; helo=mo4-p00-ob.smtp.rzone.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_PASS=-0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: bug-libsigsegv@gnu.org Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: "bug-gnulib" This is a multi-part message in MIME format. --nextPart13627053.fd3Gv6meWk Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" For a long time, I've been dissatisfied with the 'c-stack' module: * It supports only Solaris, and even the Solaris support costs maintenance effort (seen this week: Paul needed to borrow a trick from GNU libsigsegv, in order to support Solaris 11/SPARC). * For the few platforms that it supports, it needs a significant amount of complexity in m4/c-stack.m4 and lib/c-stack.c. * Without additional library, it supports proprietary platforms better than glibc systems. This is something that we should not do, RMS emphasized on gnu-prog-discuss on 2021-01-03 (message "Features that are not supported on GNU wrap-up"). So, GNU libsigsegv should be the natural replacement. But I'm dissatisfied with GNU libsigsegv as well: * It is an extra library. Although it produce less than 10 KB of compiled code, the maintainer of a package needs to pay the price of an external library (in terms of build system cost). * It is an extra library, although it is merely a "normal" application of the sigaltstack() system call. * It has way too many configure tests. This introduces bugs: E.g. When I did the port to OpenIndiana, OpenIndiana was not treated like Solaris 11 due to a mistake in the autoconf test, and the result was a suboptimal port. A compilation error would have been better. In other words, sometimes writing explicit #ifdefs for platforms is better, because it clarifies the expectations. * Part of its API (the dispatcher) is not multithread-safe [so far]. RMS's guidance is to make things easy for applications on glibc systems. GNU libsigsegv exists as a separate library mostly because of Windows platforms and older platforms. To fix this problem, I'm adding a module 'sigsegv'. It's a simplified variant of GNU libsigsegv, as a Gnulib module. 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, see above). 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. I bet that two .c files, with total size of 100 KB, is more acceptable to the Gnulib users than a wealth of 70 include files. It's kind of a "mini-libsigsegv". (I considered calling the module 'mini-libsigsegv', but the .h file is called , and I think the module name should better be consistent with that.) The module is analogous to the many "mini-libunistring" modules (uni*/*) and the "mini-gmp" that was added a year ago. The copyright is currently still mine with a few contributors (notably Eric Blake and Paolo Bonzini). We have other files not copyrighted by the FSF in Gnulib: atanl.c, filevercmp.c, logl.c. If you think the stuff should better be (C) FSF, I have no problem assigning my copyrights on that, as I do with all other Gnulib contributions. The module accepts an option '--with-libsigsegv', so as to force the use of an installed GNU libsigsegv. This is needed e.g. on old Mac OS X 10.5 in 64-bit mode (because the new module uses sigaltstack(), whereas GNU libsigsegv still uses the Mach API.) Bruno 2021-05-16 Bruno Haible sigsegv: Add tests. * tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with modifications. * tests/test-sigsegv-catch-segv2.c: Likewise. * tests/test-sigsegv-catch-stackoverflow1.c: Likewise. * tests/test-sigsegv-catch-stackoverflow2.c: Likewise. * tests/altstack-util.h: Likewise. * tests/mmap-anon-util.h: Likewise. * modules/sigsegv-tests: New file. 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. --nextPart13627053.fd3Gv6meWk Content-Disposition: attachment; filename="0001-sigsegv-New-module.patch" Content-Transfer-Encoding: quoted-printable Content-Type: text/x-patch; charset="UTF-8"; name="0001-sigsegv-New-module.patch" =46rom e54aa6196947ed22ff66bcd714e4fc7bd0c5c3b4 Mon Sep 17 00:00:00 2001 =46rom: 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. =2D-- 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 =2D-- 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=C3=A1draig Brady =20 realloc-gnu: avoid glibc MALLOC_CHECK_ issue diff --git a/lib/sigsegv.c b/lib/sigsegv.c new file mode 100644 index 0000000..312f132 =2D-- /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 =3D LIBSIGSEGV_VERSION; + + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D F= ault handler information =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D */ + +/* 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.ar= m_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.gr= egs[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.gr= egs[REG_ESP] + /* same value as ((ucontext_t *) ucp)->uc_mcontext.gr= egs[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.gr= egs[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.gr= egs[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.nd= s32_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.g= p_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.gr= egs[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.gr= egs[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 *', respectiv= ely. + In userland, this is a 'struct sigcontext *'. */ +# define SIGSEGV_FAULT_STACKPOINTER ((struct sigcontext *) ucp)->si_reg= s.u_regs[14] +# endif + +/* The sip->si_addr field is correct for a normal fault, but unusable in c= ase + of a stack overflow. What I observe (when running + tests/test-sigsegv-catch-stackoverflow1, with a printf right at the beg= inning + of sigsegv_handler) is that sip->si_addr is near 0: + - in 64-bit mode: sip->si_addr =3D 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 =3D 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 mov= e 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. Th= is + time, due to %fp =3D 0, a different fault was triggered. Now it is impo= ssible + 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 sigconte= xt *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 __DragonF= ly__ /* 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.m= c_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 sigcon= text *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 *s= cp +# 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/mconte= xt.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 si= gcontext *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 >=3D 201211 /* OpenBSD version >=3D 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 >=3D 201211 /* OpenBSD version >=3D 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.jm= p_context.gpr[1] +# endif + +#endif + +#if defined __sgi /* IRIX */ + +# define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigconte= xt *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.gr= egs[REG_RSP] + +# elif defined __i386__ +/* 32 bit registers */ + +# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gr= egs[ESP] + +# elif defined __sparc__ || defined __sparc64__ + +# define SIGSEGV_FAULT_STACKPOINTER ((ucontext_t *) ucp)->uc_mcontext.gr= egs[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 =3D 0x000116E8 + siginfo: SIGSEGV SEGV_MAPERR addr=3D0xFFB00000 + Received signal #11, SIGSEGV [caught] + siginfo: SIGSEGV SEGV_MAPERR addr=3D0xFFB00000 + then + Incurred fault #6, FLTBOUNDS %pc =3D 0x000116E8 + siginfo: SIGSEGV SEGV_MAPERR addr=3D0x00000008 + Received signal #11, SIGSEGV [caught] + siginfo: SIGSEGV SEGV_MAPERR addr=3D0x00000008 + + In 64-bit mode: + + Incurred fault #6, FLTBOUNDS %pc =3D 0x100001C58 + siginfo: SIGSEGV SEGV_MAPERR addr=3D0xFFFFFFFF7FF00000 + Received signal #11, SIGSEGV [caught] + siginfo: SIGSEGV SEGV_MAPERR addr=3D0xFFFFFFFF7FF00000 + then + Incurred fault #6, FLTBOUNDS %pc =3D 0x100001C58 + siginfo: SIGSEGV SEGV_MAPERR addr=3D0x00000000 + Received signal #11, SIGSEGV [caught] + siginfo: SIGSEGV SEGV_MAPERR addr=3D0x00000000 + */ +# 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 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +/* 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 __Drag= onFly__ \ + || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__) +# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \ + { int var; var =3D SIGSEGV; { body } var =3D SIGBUS; { body } } +#else +# define SIGSEGV_FOR_ALL_SIGNALS(var,body) \ + { int var; var =3D SIGSEGV; { body } } +#endif + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +/* Determine the virtual memory area of a given address. */ +#include "stackvma.h" + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +/* 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_A= DDRESS) +# error "You need to define SIGSEGV_FAULT_HANDLER_ARGLIST and SIGSEGV_FAUL= T_ADDRESS before you can define HAVE_SIGSEGV_RECOVERY." +#endif +#if !HAVE_SIGSEGV_RECOVERY \ + && (defined SIGSEGV_FAULT_HANDLER_ARGLIST && defined SIGSEGV_FAULT_ADD= RESS) \ + && !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__)) +# if __GNUC__ || (__clang_major__ >=3D 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_STACKPOINTE= R + HAVE_STACKVMA >=3D 2) +# error "You need to define two of SIGSEGV_FAULT_ADDRESS, SIGSEGV_FAULT_ST= ACKPOINTER, HAVE_STACKVMA, before you can define HAVE_STACK_OVERFLOW_RECOVE= RY." +#endif +#if !HAVE_STACK_OVERFLOW_RECOVERY \ + && (defined SIGSEGV_FAULT_ADDRESS + defined SIGSEGV_FAULT_STACKPOINTER= + HAVE_STACKVMA >=3D 2) \ + && !(defined __FreeBSD__ && (defined __sparc__ || defined __sparc64__)= ) \ + && !(defined __NetBSD__ && (defined __sparc__ || defined __sparc64__)) +# if __GNUC__ || (__clang_major__ >=3D 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 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +#if HAVE_STACK_OVERFLOW_RECOVERY + +/* =3D=3D=3D=3D=3D=3D=3D Leaving a signal handler executing on the alterna= te stack =3D=3D=3D=3D=3D=3D=3D */ + +/* Platform dependent: + Leaving a signal handler executing on the alternate stack. */ +static void sigsegv_reset_onstack_flag (void); + +/* -------------------------- leave-sigaltstack.c ------------------------= =2D- */ + +# if defined __GNU__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __Dra= gonFly__ \ + || defined __NetBSD__ || defined __OpenBSD__ + +static void +sigsegv_reset_onstack_flag (void) +{ + stack_t ss; + + if (sigaltstack (NULL, &ss) >=3D 0) + { + ss.ss_flags &=3D ~SS_ONSTACK; + sigaltstack (&ss, NULL); + } +} + +/* --------------------------- leave-setcontext.c ------------------------= =2D-- */ + +# elif defined __sgi || defined __sun /* IRIX, Solaris */ + +# include + +static void +sigsegv_reset_onstack_flag (void) +{ + ucontext_t uc; + + if (getcontext (&uc) >=3D 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 &=3D ~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 ----------------------------= =2D- */ + +# 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 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +# if HAVE_STACKVMA + +/* Address of the last byte belonging to the stack vma. */ +static uintptr_t stack_top =3D 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) >=3D 0) + stack_top =3D vma.end - 1; +} + +# endif /* HAVE_STACKVMA */ + +static stackoverflow_handler_t stk_user_handler =3D (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 =3D (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 =3D (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 de= fine CFG_STACKVMA and HAVE_STACKVMA correctly, or define SIGSEGV_FAULT_STAC= KPOINTER 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 =3D (uintptr_t) (SIGSEGV_FAULT_STACKPOINTER); +# ifdef __ia64 + uintptr_t old_bsp =3D (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 =3D errno; + ret =3D sigsegv_get_vma (stack_top, &vma); + errno =3D saved_errno; + if (ret >=3D 0) + { +# ifndef BOGUS_FAULT_ADDRESS_UPON_STACK_OVERFLOW + /* Heuristic AC: If the fault_address is nearer to the s= tack + 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 segme= nt + is the up-growing bsp segment, and either of the two + stacks can overflow. */ + uintptr_t addr =3D (uintptr_t) address; + +# ifdef __ia64 + if (addr >=3D vma.prev_end && addr <=3D vma.end - 1) +# else +# if STACK_DIRECTION < 0 + if (addr >=3D vma.start + ? (addr <=3D vma.end - 1) + : vma.is_near_this (addr, &vma)) +# else + if (addr <=3D vma.end - 1 + ? (addr >=3D 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 maxim= al size, + and old_sp is near the low end, we consider it a stack + overflow. */ + struct rlimit rl; + + saved_errno =3D errno; + ret =3D getrlimit (RLIMIT_STACK, &rl); + errno =3D saved_errno; + if (ret >=3D 0) + { + uintptr_t current_stack_size =3D vma.end - vma.start; + uintptr_t max_stack_size =3D rl.rlim_cur; + if (current_stack_size <=3D max_stack_size + 4096 + && max_stack_size <=3D current_stack_size + 4096 +# else + { + if (1 +# endif +# ifdef SIGSEGV_FAULT_STACKPOINTER + /* Heuristic BC: If we know old_sp, and it is ne= ither + near the low end, nor in the alternate stack,= then + it's probably not a stack overflow. */ + && ((old_sp >=3D stk_extra_stack + && old_sp <=3D stk_extra_stack + stk_extra_= stack_size) +# if STACK_DIRECTION < 0 + || (old_sp <=3D vma.start + 4096 + && vma.start <=3D old_sp + 4096)) +# else + || (old_sp <=3D vma.end + 4096 + && vma.end <=3D 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 =3D (uintptr_t) address; + + if ((addr <=3D old_sp + 4096 && old_sp <=3D addr + 4096) +# ifdef __ia64 + || (addr <=3D old_bsp + 4096 && old_bsp <=3D addr + 4096) +# endif + ) + { + { + { +# endif /* !HAVE_STACKVMA */ + { +# ifdef SIGSEGV_FAULT_STACKPOINTER + int emergency =3D + (old_sp >=3D stk_extra_stack + && old_sp <=3D stk_extra_stack + stk_extra_st= ack_size); + stackoverflow_context_t context =3D (SIGSEGV_FAU= LT_CONTEXT); +# else + int emergency =3D 0; + stackoverflow_context_t context =3D (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_S= TACKPOINTER) +# error "Insufficient heuristics for detecting a stack overflow. Either = define SIGSEGV_FAULT_STACKPOINTER correctly, or undefine HAVE_STACK_OVERFLO= W_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 =3D (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 =3D errno; + ret =3D sigsegv_get_vma (stack_top, &vma); + errno =3D saved_errno; + if (ret >=3D 0) + { +# if HAVE_GETRLIMIT && defined RLIMIT_STACK + /* Heuristic BC: If the stack size has reached its maximal s= ize, + and old_sp is near the low end, we consider it a stack + overflow. */ + struct rlimit rl; + + saved_errno =3D errno; + ret =3D getrlimit (RLIMIT_STACK, &rl); + errno =3D saved_errno; + if (ret >=3D 0) + { + uintptr_t current_stack_size =3D vma.end - vma.start; + uintptr_t max_stack_size =3D rl.rlim_cur; + if (current_stack_size <=3D max_stack_size + 4096 + && max_stack_size <=3D 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 >=3D stk_extra_stack + && old_sp <=3D stk_extra_stack + stk_extra_stac= k_size) +# if STACK_DIRECTION < 0 + || (old_sp <=3D vma.start + 4096 + && vma.start <=3D old_sp + 4096)) +# else + || (old_sp <=3D vma.end + 4096 + && vma.end <=3D old_sp + 4096)) +# endif +# endif + ) + { +# ifdef SIGSEGV_FAULT_STACKPOINTER + int emergency =3D + (old_sp >=3D stk_extra_stack + && old_sp <=3D stk_extra_stack + stk_extra_stack_= size); + stackoverflow_context_t context =3D (SIGSEGV_FAULT_C= ONTEXT); +# else + int emergency =3D 0; + stackoverflow_context_t context =3D (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 =3D &sigsegv_handler; +# else + action.sa_handler =3D (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, SI= GBUS, + SIGSYS, SIGSTKFLT are not blocked because these are synchronous signa= ls, + 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 =3D SA_SIGINFO; +# else + action.sa_flags =3D 0; +# endif +# if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGALTSTACK /* not BeOS */ + /* Work around Linux 2.2.5 bug: If SA_ONSTACK is specified but sigaltsta= ck() + 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 |=3D 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 =3D 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 =3D (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 =3D handler; + stk_extra_stack =3D (uintptr_t) extra_stack; + stk_extra_stack_size =3D extra_stack_size; + { + stack_t ss; +# if SIGALTSTACK_SS_REVERSED + ss.ss_sp =3D (char *) extra_stack + extra_stack_size - sizeof (void *); + ss.ss_size =3D extra_stack_size - sizeof (void *); +# else + ss.ss_sp =3D extra_stack; + ss.ss_size =3D extra_stack_size; +# endif + ss.ss_flags =3D 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 =3D (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 =3D 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 =2D-- /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__ || de= fined __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 __a= md64__)) +# 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 __s= parc__ || 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 __s= parc__ || 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 */ + +/* -----------------------------------------------------------------------= =2D-- */ + +#if 1 /* really only HAVE_SIGSEGV_RECOVERY */ + +/* + * The mask of bits that are set to zero in a fault address that gets pass= ed + * to a global SIGSEGV handler. + * On some platforms, the precise fault address is not known, only the mem= ory + * 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= =2E" + * In this case, the returned fault address is rounded down to a multiple = of + * getpagesize() =3D sysconf(_SC_PAGESIZE). + * On such platforms, we define SIGSEGV_FAULT_ADDRESS_ALIGNMENT to be an u= pper + * 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 handl= er + * 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 t= he + * program is known. Therefore it cannot use facilities that manipulate gl= obal + * 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 SIGS= EGV. + */ +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_ARG= 2, + * 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__ || d= efined __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 amo= unt + * 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 t= he + * program is known. Therefore it cannot use facilities that manipulate gl= obal + * 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_cont= ext_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 varia= ble + * 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 */ + +/* -----------------------------------------------------------------------= =2D-- */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SIGSEGV_H */ diff --git a/lib/stackvma.c b/lib/stackvma.c new file mode 100644 index 0000000..faa9923 =2D-- /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 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D stackvma-simple.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __Drag= onFly__ \ + || 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, wh= ere + 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 <=3D (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 + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D stackvma-rofile.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ +/* Buffered read-only streams. */ + +#if defined __linux__ || defined __ANDROID__ \ + || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __Drag= onFly__ \ + || 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 mal= loc() + 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 f= ile + appear truncated. + - On DragonFly BSD, the first read() call would fail with errno =3D E= =46BIG. + - On all platforms, if some other thread is doing memory allocations = or + deallocations between two read() calls, there is a high risk that t= he + result of these two read() calls don't fit together, and as a + consequence we will parse gargage and either omit some VMAs or retu= rn + VMAs with nonsensical addresses. + So use mmap(), and ignore the resulting VMA. + The stack-allocated buffer cannot be too large, because this can be cal= led + when we are in the context of an alternate stack of just SIGSTKSZ bytes= =2E */ + +# 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 =3D open (filename, O_RDONLY); + if (fd < 0) + return -1; + rof->position =3D 0; + rof->eof_seen =3D 0; + /* Try the static buffer first. */ + pagesize =3D 0; + rof->buffer =3D rof->stack_allocated_buffer; + size =3D sizeof (rof->stack_allocated_buffer); + rof->auxmap =3D NULL; + rof->auxmap_start =3D 0; + rof->auxmap_end =3D 0; + for (;;) + { + /* Attempt to read the contents in a single system call. */ + if (size > MIN_LEFTOVER) + { + int n =3D read (fd, rof->buffer, size); + if (n < 0 && errno =3D=3D EINTR) + goto retry; +# if defined __DragonFly__ + if (!(n < 0 && errno =3D=3D EFBIG)) +# endif + { + if (n <=3D 0) + /* Empty file. */ + goto fail1; + if (n + MIN_LEFTOVER <=3D size) + { + /* The buffer was sufficiently large. */ + rof->filled =3D 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 =3D read (fd, rof->buffer + rof->filled, size - ro= f->filled); + if (n < 0 && errno =3D=3D EINTR) + goto retry; + if (n < 0) + /* Some error. */ + goto fail1; + if (n + MIN_LEFTOVER > size - rof->filled) + /* Allocate a larger buffer. */ + break; + if (n =3D=3D 0) + { + /* Reached the end of file. */ + close (fd); + return 0; + } + rof->filled +=3D n; + } +# else + close (fd); + return 0; +# endif + } + } + } + /* Allocate a larger buffer. */ + if (pagesize =3D=3D 0) + { + pagesize =3D getpagesize (); + size =3D pagesize; + while (size <=3D MIN_LEFTOVER) + size =3D 2 * size; + } + else + { + size =3D 2 * size; + if (size =3D=3D 0) + /* Wraparound. */ + goto fail1; + if (rof->auxmap !=3D NULL) + munmap (rof->auxmap, rof->auxmap_length); + } + rof->auxmap =3D (void *) mmap ((void *) 0, size, PROT_READ | PROT_WR= ITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (rof->auxmap =3D=3D (void *) -1) + { + close (fd); + return -1; + } + rof->auxmap_length =3D size; + rof->auxmap_start =3D (uintptr_t) rof->auxmap; + rof->auxmap_end =3D rof->auxmap_start + size; + rof->buffer =3D (char *) rof->auxmap; + retry: + /* Restart. */ + if (lseek (fd, 0, SEEK_SET) < 0) + { + close (fd); + fd =3D open (filename, O_RDONLY); + if (fd < 0) + goto fail2; + } + } + fail1: + close (fd); + fail2: + if (rof->auxmap !=3D 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 =3D=3D rof->filled) + { + rof->eof_seen =3D 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 =3D rof_peekchar (rof); + if (c >=3D 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 =3D 0; + unsigned int numdigits =3D 0; + for (;;) + { + int c =3D rof_peekchar (rof); + if (c >=3D '0' && c <=3D '9') + value =3D (value << 4) + (c - '0'); + else if (c >=3D 'A' && c <=3D 'F') + value =3D (value << 4) + (c - 'A' + 10); + else if (c >=3D 'a' && c <=3D 'f') + value =3D (value << 4) + (c - 'a' + 10); + else + break; + rof_getchar (rof); + numdigits++; + } + if (numdigits =3D=3D 0) + return -1; + *valuep =3D value; + return 0; +} + +/* Close a read-only file stream. */ +static void +rof_close (struct rofile *rof) +{ + if (rof->auxmap !=3D NULL) + munmap (rof->auxmap, rof->auxmap_length); +} + +#endif + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D stackvma-vma-iter.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ +/* 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 __Drag= onFly__ \ + || defined __NetBSD__ \ + || defined __CYGWIN__ + +/* Forward declarations. */ +struct callback_locals; +static int callback (struct callback_locals *locals, uintptr_t start, uint= ptr_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") >=3D 0) + { + uintptr_t auxmap_start =3D rof.auxmap_start; + uintptr_t auxmap_end =3D rof.auxmap_end; + + for (;;) + { + uintptr_t start, end; + int c; + + /* Parse one line. First start and end. */ + if (!(rof_scanf_lx (&rof, &start) >=3D 0 + && rof_getchar (&rof) =3D=3D '-' + && rof_scanf_lx (&rof, &end) >=3D 0)) + break; + while (c =3D rof_getchar (&rof), c !=3D -1 && c !=3D '\n') + ; + + if (start <=3D auxmap_start && auxmap_end - 1 <=3D end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + =3D [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") >=3D 0) + { + uintptr_t auxmap_start =3D rof.auxmap_start; + uintptr_t auxmap_end =3D rof.auxmap_end; + + for (;;) + { + uintptr_t start, end; + int c; + + /* Parse one line. First start. */ + if (!(rof_getchar (&rof) =3D=3D '0' + && rof_getchar (&rof) =3D=3D 'x' + && rof_scanf_lx (&rof, &start) >=3D 0)) + break; + while (c =3D rof_peekchar (&rof), c =3D=3D ' ' || c =3D=3D '\t') + rof_getchar (&rof); + /* Then end. */ + if (!(rof_getchar (&rof) =3D=3D '0' + && rof_getchar (&rof) =3D=3D 'x' + && rof_scanf_lx (&rof, &end) >=3D 0)) + break; + while (c =3D rof_getchar (&rof), c !=3D -1 && c !=3D '\n') + ; + + if (start <=3D auxmap_start && auxmap_end - 1 <=3D end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + =3D [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_P= ROC_VMMAP /* FreeBSD >=3D 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[] =3D { 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 =3D 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 =3D 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 =3D getpagesize (); + memneed =3D len; + memneed =3D ((memneed - 1) / pagesize + 1) * pagesize; + auxmap =3D (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (auxmap =3D=3D (void *) -1) + return -1; + auxmap_start =3D (unsigned long) auxmap; + auxmap_end =3D auxmap_start + memneed; + mem =3D (char *) auxmap; + if (sysctl (info_path, 4, mem, &len, NULL, 0) < 0) + { + munmap (auxmap, memneed); + return -1; + } + p =3D mem; + p_end =3D mem + len; + while (p < p_end) + { + struct kinfo_vmentry *kve =3D (struct kinfo_vmentry *) p; + unsigned long start =3D kve->kve_start; + unsigned long end =3D kve->kve_end; + if (start <=3D auxmap_start && auxmap_end - 1 <=3D end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + =3D [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 +=3D 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-aligne= d. + 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 th= at + were created by anonymous mmap. See + + So use vma_iterate_proc only as a fallback. */ + int retval =3D vma_iterate_bsd (locals); + if (retval =3D=3D 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 =3D vma_iterate_proc (locals); + if (retval =3D=3D 0) + return 0; + + return vma_iterate_bsd (locals); +# endif +} + +#endif + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D stackvma-mincore.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */ + +/* 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,REFERENCE= D,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 _unmappe= d_ + 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 __Drag= onFly__ \ + || 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 plat= forms + 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 *', whe= reas + the BSD declaration uses 'char *'. */ +# if __GLIBC__ >=3D 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 =3D 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) >=3D 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 sta= ck + (without requiring malloc). */ + pageinfo_t vec[1024]; + uintptr_t stepsize =3D sizeof (vec); + + for (;;) + { + uintptr_t max_remaining; + + if (addr =3D=3D 0) + return addr; + + max_remaining =3D addr / pagesize; + if (stepsize > max_remaining) + stepsize =3D 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 -=3D stepsize * pagesize; + } + for (;;) + { + uintptr_t halfstepsize1; + uintptr_t halfstepsize2; + + if (stepsize =3D=3D 1) + return addr; + + /* Here we know that less than stepsize pages exist starting at addr= =2E */ + halfstepsize1 =3D (stepsize + 1) / 2; + halfstepsize2 =3D stepsize / 2; + /* halfstepsize1 + halfstepsize2 =3D stepsize. */ + + if (mincore ((MINCORE_ADDR_T) (addr - halfstepsize1 * pagesize), + halfstepsize1 * pagesize, vec) < 0) + stepsize =3D halfstepsize1; + else + { + addr -=3D halfstepsize1 * pagesize; + stepsize =3D 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 sta= ck + (without requiring malloc). */ + pageinfo_t vec[1024]; + uintptr_t stepsize =3D sizeof (vec); + + addr +=3D pagesize; + for (;;) + { + uintptr_t max_remaining; + + if (addr =3D=3D 0) /* wrapped around? */ + return addr; + + max_remaining =3D (- addr) / pagesize; + if (stepsize > max_remaining) + stepsize =3D 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 +=3D stepsize * pagesize; + } + for (;;) + { + uintptr_t halfstepsize1; + uintptr_t halfstepsize2; + + if (stepsize =3D=3D 1) + return addr; + + /* Here we know that less than stepsize pages exist starting at addr= =2E */ + halfstepsize1 =3D (stepsize + 1) / 2; + halfstepsize2 =3D stepsize / 2; + /* halfstepsize1 + halfstepsize2 =3D stepsize. */ + + if (mincore ((MINCORE_ADDR_T) addr, halfstepsize1 * pagesize, vec) <= 0) + stepsize =3D halfstepsize1; + else + { + addr +=3D halfstepsize1 * pagesize; + stepsize =3D halfstepsize2; + } + } +} + +/* Determine whether an address range [ADDR1..ADDR2] is completely unmappe= d. + ADDR1 must be <=3D ADDR2. */ +static int +is_unmapped (uintptr_t addr1, uintptr_t addr2) +{ + uintptr_t count; + uintptr_t stepsize; + + /* Round addr1 down. */ + addr1 =3D (addr1 / pagesize) * pagesize; + /* Round addr2 up and turn it into an exclusive bound. */ + addr2 =3D ((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 bo= ttom, + 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 =3D (addr2 - addr1) / pagesize; + /* We have to test is_mapped (addr1 + i * pagesize) for 0 <=3D i < count= =2E */ + for (stepsize =3D 1; stepsize < count; ) + stepsize =3D 2 * stepsize; + for (;;) + { + uintptr_t addr_stepsize; + uintptr_t i; + uintptr_t addr; + + stepsize =3D stepsize / 2; + if (stepsize =3D=3D 0) + break; + addr_stepsize =3D stepsize * pagesize; + for (i =3D stepsize, addr =3D addr1 + addr_stepsize; + i < count; + i +=3D 2 * stepsize, addr +=3D 2 * addr_stepsize) + /* Here addr =3D 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 <=3D (vma->start - vma->prev_end) / 2 + is mathematically equivalent to + vma->prev_end <=3D 2 * addr - vma->start + <=3D=3D> 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 =3D addr - (vma->start - addr); + if (testaddr > addr) /* overflow? */ + return 0; + /* Here testaddr <=3D 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 + <=3D=3D> 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 =3D addr + (addr - vma->end); + if (testaddr < addr) /* overflow? */ + return 0; + /* Here vma->end - 1 < addr <=3D testaddr. */ + return is_unmapped (vma->end, testaddr); +} + +# endif + +static int +mincore_get_vma (uintptr_t address, struct vma_struct *vma) +{ + if (pagesize =3D=3D 0) + init_pagesize (); + address =3D (address / pagesize) * pagesize; + vma->start =3D mapped_range_start (address); + vma->end =3D mapped_range_end (address); + vma->is_near_this =3D mincore_is_near_this; + return 0; +} + +#endif + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D */ + +/* ---------------------------- stackvma-linux.c -------------------------= =2D-- */ + +#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 >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->vma->prev_end =3D locals->prev; + locals->retval =3D 0; + return 1; + } + locals->prev =3D end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->address >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 1; + return 0; + } +# endif + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address =3D address; + locals.vma =3D vma; +# if STACK_DIRECTION < 0 + locals.prev =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D simple_is_near_this; + return 0; + } + + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-freebsd.c ------------------------= =2D-- */ + +#elif defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __Drago= nFly__ /* 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 =3D=3D locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end =3D end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->vma->prev_end =3D locals->prev_end; + locals->retval =3D 0; + return 1; + } + locals->prev_end =3D locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D locals->curr_start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 1; + return 0; + } +# endif + locals->curr_start =3D start; locals->curr_end =3D end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address =3D address; + locals.vma =3D vma; + locals.curr_start =3D 0; + locals.curr_end =3D 0; +# if STACK_DIRECTION < 0 + locals.prev_end =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >=3D locals.curr_start && address <=3D locals.curr_en= d - 1) + { + vma->start =3D locals.curr_start; + vma->end =3D locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end =3D locals.prev_end; +# else + vma->next_start =3D 0; +# endif + locals.retval =3D 0; + } + } + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D 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 -------------------------= =2D- */ + +#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 =3D=3D locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end =3D end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->vma->prev_end =3D locals->prev_end; + locals->retval =3D 0; + return 1; + } + locals->prev_end =3D locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D locals->curr_start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 1; + return 0; + } +# endif + locals->curr_start =3D start; locals->curr_end =3D end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address =3D address; + locals.vma =3D vma; + locals.curr_start =3D 0; + locals.curr_end =3D 0; +# if STACK_DIRECTION < 0 + locals.prev_end =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >=3D locals.curr_start && address <=3D locals.curr_en= d - 1) + { + vma->start =3D locals.curr_start; + vma->end =3D locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end =3D locals.prev_end; +# else + vma->next_start =3D 0; +# endif + locals.retval =3D 0; + } + } + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D simple_is_near_this; + return 0; + } + + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-mquery.c -------------------------= =2D- */ + +/* 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 =3D 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 =3D=3D 0) + return 0; + return mquery ((void *) addr, pagesize, 0, MAP_FIXED, -1, 0) =3D=3D (voi= d *) -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 =3D pagesize; + for (;;) + { + uintptr_t hole; + + if (addr =3D=3D 0) + abort (); + + if (addr <=3D stepsize) + { + known_unmapped_page =3D 0; + break; + } + + hole =3D (uintptr_t) mquery ((void *) (addr - stepsize), pagesize, + 0, 0, -1, 0); + if (!(hole =3D=3D (uintptr_t) (void *) -1 || hole >=3D addr)) + { + /* Some part of [addr - stepsize, addr - 1] is unmapped. */ + known_unmapped_page =3D hole; + break; + } + + /* The entire range [addr - stepsize, addr - 1] is mapped. */ + addr -=3D stepsize; + + if (2 * stepsize > stepsize && 2 * stepsize < addr) + stepsize =3D 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 <=3D stepsize. */ + while (stepsize > pagesize && stepsize / 2 >=3D addr - known_unmapped_pa= ge) + stepsize =3D stepsize / 2; + /* Still 0 < addr - known_unmapped_page <=3D stepsize. */ + while (stepsize > pagesize) + { + uintptr_t hole; + + stepsize =3D stepsize / 2; + hole =3D (uintptr_t) mquery ((void *) (addr - stepsize), pagesize, + 0, 0, -1, 0); + if (!(hole =3D=3D (uintptr_t) (void *) -1 || hole >=3D addr)) + /* Some part of [addr - stepsize, addr - 1] is unmapped. */ + known_unmapped_page =3D hole; + else + /* The entire range [addr - stepsize, addr - 1] is mapped. */ + addr -=3D stepsize; + /* Still 0 < addr - known_unmapped_page <=3D 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 =3D=3D 0) + abort (); + + end =3D (uintptr_t) mquery ((void *) addr, pagesize, 0, 0, -1, 0); + if (end =3D=3D (uintptr_t) (void *) -1) + end =3D 0; /* wrap around */ + return end; +} + +/* Determine whether an address range [ADDR1..ADDR2] is completely unmappe= d. + ADDR1 must be <=3D ADDR2. */ +static int +is_unmapped (uintptr_t addr1, uintptr_t addr2) +{ + /* Round addr1 down. */ + addr1 =3D (addr1 / pagesize) * pagesize; + /* Round addr2 up and turn it into an exclusive bound. */ + addr2 =3D ((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 =3D=3D 0) + addr1 =3D pagesize; + + if (addr1 < addr2) + { + if (mquery ((void *) addr1, addr2 - addr1, 0, MAP_FIXED, -1, 0) + =3D=3D (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 <=3D (vma->start - vma->prev_end) / 2 + is mathematically equivalent to + vma->prev_end <=3D 2 * addr - vma->start + <=3D=3D> 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 =3D addr - (vma->start - addr); + if (testaddr > addr) /* overflow? */ + return 0; + /* Here testaddr <=3D 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 + <=3D=3D> 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 =3D addr + (addr - vma->end); + if (testaddr < addr) /* overflow? */ + return 0; + /* Here vma->end - 1 < addr <=3D testaddr. */ + return is_unmapped (vma->end, testaddr); +} + +# endif + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + if (pagesize =3D=3D 0) + init_pagesize (); + address =3D (address / pagesize) * pagesize; + vma->start =3D mapped_range_start (address); + vma->end =3D mapped_range_end (address); + vma->is_near_this =3D mquery_is_near_this; + return 0; +} + +/* ---------------------------- stackvma-mach.c --------------------------= =2D- */ + +#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 =3D 0, prev_size =3D 0; + uintptr_t join_address =3D 0, join_size =3D 0; + int more =3D 1; + vm_address_t address; + vm_size_t size; + task_t task =3D mach_task_self (); + + for (address =3D VM_MIN_ADDRESS; more; address +=3D size) + { + mach_port_t object_name; + /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t h= ave + 32 bits in 32-bit processes and 64 bits in 64-bit processes. Wher= eas + 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 cur= rent + 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 th= an + '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 =3D VM_REGION_BASIC_INFO_COUNT_64; + + more =3D (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_= 64, + (vm_region_info_t)&info, &info_count, &object_= name) + =3D=3D KERN_SUCCESS); +# else + struct vm_region_basic_info info; + mach_msg_type_number_t info_count =3D VM_REGION_BASIC_INFO_COUNT; + + more =3D (vm_region (task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&info, &info_count, &object_nam= e) + =3D=3D KERN_SUCCESS); +# endif + if (!more) + { + address =3D join_address + join_size; + size =3D 0; + } + + if ((uintptr_t) address =3D=3D join_address + join_size) + join_size +=3D size; + else + { + prev_address =3D join_address; + prev_size =3D join_size; + join_address =3D (uintptr_t) address; + join_size =3D size; + } + + if (object_name !=3D MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), object_name); + +# if STACK_DIRECTION < 0 + if (join_address <=3D req_address && join_address + join_size > req_= address) + { + vma->start =3D join_address; + vma->end =3D join_address + join_size; + vma->prev_end =3D prev_address + prev_size; + vma->is_near_this =3D simple_is_near_this; + return 0; + } +# else + if (prev_address <=3D req_address && prev_address + prev_size > req_= address) + { + vma->start =3D prev_address; + vma->end =3D prev_address + prev_size; + vma->next_start =3D join_address; + vma->is_near_this =3D simple_is_near_this; + return 0; + } +# endif + } + +# if STACK_DIRECTION > 0 + if (join_address <=3D req_address && join_address + size > req_address) + { + vma->start =3D prev_address; + vma->end =3D prev_address + prev_size; + vma->next_start =3D ~0UL; + vma->is_near_this =3D simple_is_near_this; + return 0; + } +# endif + + return -1; +} + +/* -----------------------------------------------------------------------= =2D-- */ + +#elif defined _AIX /* AIX */ + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + return mincore_get_vma (address, vma); +} + +/* --------------------------- stackvma-procfs.h -------------------------= =2D- */ + +#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 =3D 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 >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->vma->prev_end =3D locals->prev; + locals->retval =3D 0; + return 1; + } + locals->prev =3D end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->address >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 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-aligne= d. + 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 =3D 0 32 56 + _STRUCTURED_PROC =3D 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 =3D=3D 0) + init_pagesize (); + + /* Construct fname =3D sprintf (fnamebuf+i, "/proc/%u", getpid ()). */ + fname =3D fnamebuf + sizeof (fnamebuf) - 1; + *fname =3D '\0'; + { + unsigned int value =3D getpid (); + do + *--fname =3D (value % 10) + '0'; + while ((value =3D value / 10) > 0); + } + fname -=3D 6; + memcpy (fname, "/proc/", 6); + + fd =3D open (fname, O_RDONLY); + if (fd < 0) + return -1; + + if (ioctl (fd, PIOCNMAP, &nmaps) < 0) + goto fail2; + + memneed =3D (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 =3D ((memneed - 1) / pagesize + 1) * pagesize; +# if !HAVE_MAP_ANONYMOUS + zero_fd =3D open ("/dev/zero", O_RDONLY, 0644); + if (zero_fd < 0) + goto fail2; +# endif + auxmap =3D (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 =3D=3D (void *) -1) + goto fail2; + auxmap_start =3D (uintptr_t) auxmap; + auxmap_end =3D auxmap_start + memneed; + maps =3D (prmap_t *) auxmap; + + if (ioctl (fd, PIOCMAP, maps) < 0) + goto fail1; + + for (mp =3D maps;;) + { + uintptr_t start, end; + + start =3D (uintptr_t) mp->pr_vaddr; + end =3D start + mp->pr_size; + if (start =3D=3D 0 && end =3D=3D 0) + break; + mp++; + if (start <=3D auxmap_start && auxmap_end - 1 <=3D end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + =3D [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 =3D=3D 0) + init_pagesize (); + + /* Construct fname =3D sprintf (fnamebuf+i, "/proc/%u/map", getpid ()). = */ + fname =3D fnamebuf + sizeof (fnamebuf) - 1 - 4; + memcpy (fname, "/map", 4 + 1); + { + unsigned int value =3D getpid (); + do + *--fname =3D (value % 10) + '0'; + while ((value =3D value / 10) > 0); + } + fname -=3D 6; + memcpy (fname, "/proc/", 6); + + fd =3D open (fname, O_RDONLY); + if (fd < 0) + return -1; + + { + struct stat statbuf; + if (fstat (fd, &statbuf) < 0) + goto fail2; + nmaps =3D statbuf.st_size / sizeof (prmap_t); + } + + memneed =3D (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 =3D ((memneed - 1) / pagesize + 1) * pagesize; +# if !HAVE_MAP_ANONYMOUS + zero_fd =3D open ("/dev/zero", O_RDONLY, 0644); + if (zero_fd < 0) + goto fail2; +# endif + auxmap =3D (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 =3D=3D (void *) -1) + goto fail2; + auxmap_start =3D (uintptr_t) auxmap; + auxmap_end =3D auxmap_start + memneed; + maps =3D (prmap_t *) auxmap; + + /* Read up to memneed bytes from fd into maps. */ + { + size_t remaining =3D memneed; + size_t total_read =3D 0; + char *ptr =3D (char *) maps; + + do + { + size_t nread =3D read (fd, ptr, remaining); + if (nread =3D=3D (size_t)-1) + { + if (errno =3D=3D EINTR) + continue; + goto fail1; + } + if (nread =3D=3D 0) + /* EOF */ + break; + total_read +=3D nread; + ptr +=3D nread; + remaining -=3D nread; + } + while (remaining > 0); + + nmaps =3D (memneed - remaining) / sizeof (prmap_t); + maps_end =3D maps + nmaps; + } + + for (mp =3D maps; mp < maps_end; mp++) + { + uintptr_t start, end; + + start =3D (uintptr_t) mp->pr_vaddr; + end =3D start + mp->pr_size; + if (start <=3D auxmap_start && auxmap_end - 1 <=3D end - 1) + { + /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1] + =3D [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 =3D address; + locals.vma =3D vma; +# if STACK_DIRECTION < 0 + locals.prev =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D simple_is_near_this; + return 0; + } + +# if defined __sun + return mincore_get_vma (address, vma); +# else + return -1; +# endif +} + +/* -----------------------------------------------------------------------= =2D-- */ + +#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 =3D=3D locals->curr_end) + { + /* Merge adjacent segments. */ + locals->curr_end =3D end; + return 0; + } +# if STACK_DIRECTION < 0 + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->vma->prev_end =3D locals->prev_end; + locals->retval =3D 0; + return 1; + } + locals->prev_end =3D locals->curr_end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D locals->curr_start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->curr_start < locals->curr_end + && locals->address >=3D locals->curr_start + && locals->address <=3D locals->curr_end - 1) + { + locals->vma->start =3D locals->curr_start; + locals->vma->end =3D locals->curr_end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 1; + return 0; + } +# endif + locals->curr_start =3D start; locals->curr_end =3D end; + return 0; +} + +int +sigsegv_get_vma (uintptr_t address, struct vma_struct *vma) +{ + struct callback_locals locals; + locals.address =3D address; + locals.vma =3D vma; + locals.curr_start =3D 0; + locals.curr_end =3D 0; +# if STACK_DIRECTION < 0 + locals.prev_end =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval < 0) + { + if (locals.curr_start < locals.curr_end + && address >=3D locals.curr_start && address <=3D locals.curr_en= d - 1) + { + vma->start =3D locals.curr_start; + vma->end =3D locals.curr_end; +# if STACK_DIRECTION < 0 + vma->prev_end =3D locals.prev_end; +# else + vma->next_start =3D 0; +# endif + locals.retval =3D 0; + } + } + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D simple_is_near_this; + return 0; + } + + return -1; +} + +/* ---------------------------- stackvma-beos.h --------------------------= =2D- */ + +#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 >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->vma->prev_end =3D locals->prev; + locals->retval =3D 0; + return 1; + } + locals->prev =3D end; +# else + if (locals->stop_at_next_vma) + { + locals->vma->next_start =3D start; + locals->stop_at_next_vma =3D 0; + return 1; + } + if (locals->address >=3D start && locals->address <=3D end - 1) + { + locals->vma->start =3D start; + locals->vma->end =3D end; + locals->retval =3D 0; + locals->stop_at_next_vma =3D 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-aligne= d. + 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 =3D 0; + while (get_next_area_info (0, &cookie, &info) =3D=3D B_OK) + { + uintptr_t start, end; + + start =3D (uintptr_t) info.address; + end =3D 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 =3D address; + locals.vma =3D vma; +# if STACK_DIRECTION < 0 + locals.prev =3D 0; +# else + locals.stop_at_next_vma =3D 0; +# endif + locals.retval =3D -1; + + vma_iterate (&locals); + if (locals.retval =3D=3D 0) + { +# if !(STACK_DIRECTION < 0) + if (locals.stop_at_next_vma) + vma->next_start =3D 0; +# endif + vma->is_near_this =3D simple_is_near_this; + return 0; + } + return -1; +} + +/* -----------------------------------------------------------------------= =2D-- */ + +#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 =2D-- /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= =2E */ +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 __Drag= onFly__ \ + || 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 =2D-- /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" =3D yes; then + AC_CHECK_TYPE([stack_t], , + [AC_DEFINE(stack_t, [struct sigaltstack], + [Define to 'struct sigaltstack' if that's the type of the argumen= t 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" =3D 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 dial= og + # window would pop up. + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[int x =3D SA_ONSTACK; stack_t ss; sigaltstack ((stack_t*)0= , &ss);]])], + [sv_cv_sigaltstack=3D"guessing yes"], + [sv_cv_sigaltstack=3Dno]) + ;; + *) + 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 >=3D 0) + *recurse_1 (n + 1, p) +=3D n; + return p; +} +int recurse (volatile int n) +{ + int sum =3D 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 us= er + processes at all. We don't want to kill such systems. */ + struct rlimit rl; + rl.rlim_cur =3D rl.rlim_max =3D 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 =3D mystack + SIGSTKSZ; + altstack.ss_size =3D SIGSTKSZ; + altstack.ss_flags =3D 0; /* no SS_DISABLE */ + if (sigaltstack (&altstack, NULL) < 0) + exit (1); + /* Install the SIGSEGV handler. */ + sigemptyset (&action.sa_mask); + action.sa_handler =3D &stackoverflow_handler; + action.sa_flags =3D 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=3Dyes], + [sv_cv_sigaltstack=3Dno], + [ + dnl FIXME: Put in some more known values here. + case "$host_os" in + *) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[int x =3D SA_ONSTACK; stack_t ss; sigaltstack ((st= ack_t*)0, &ss);]])], + [sv_cv_sigaltstack=3D"guessing yes"], + [sv_cv_sigaltstack=3Dno]) + ;; + esac + ]) + ;; + esac + else + sv_cv_sigaltstack=3Dno + fi + ]) + if test "$sv_cv_sigaltstack" !=3D 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 ad= dress + 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 >=3D stack_lower_bound && addr <=3D 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 =3D mystack + SIGSTKSZ; + altstack.ss_size =3D SIGSTKSZ; + stack_lower_bound =3D (char *) altstack.ss_sp; + stack_upper_bound =3D (char *) altstack.ss_sp + altstack.ss_size - 1; + altstack.ss_flags =3D 0; /* no SS_DISABLE */ + if (sigaltstack (&altstack, NULL) < 0) + exit (2); + /* Install the SIGSEGV handler. */ + sigemptyset (&action.sa_mask); + action.sa_handler =3D &stackoverflow_handler; + action.sa_flags =3D SA_ONSTACK; + if (sigaction (SIGSEGV, &action, (struct sigaction *) NULL) < 0) + exit(3); + /* Provoke a SIGSEGV. */ + raise (SIGSEGV); + exit (3); +}]])], + [sv_cv_sigaltstack_low_base=3Dyes], + [sv_cv_sigaltstack_low_base=3Dno], + [ + dnl FIXME: Put in some more known values here. + case "$host_os" in + irix5*) sv_cv_sigaltstack_low_base=3D"no" ;; + *) sv_cv_sigaltstack_low_base=3D"guessing yes" ;; + esac + ]) + ]) + if test "$sv_cv_sigaltstack_low_base" =3D no; then + AC_DEFINE([SIGALTSTACK_SS_REVERSED], [1], + [Define if sigaltstack() interprets the stack_t.ss_sp field incorr= ectly, + as the highest address of the alternate stack range rather than a= s the + lowest address.]) + fi + fi +]) diff --git a/m4/stack-direction.m4 b/m4/stack-direction.m4 new file mode 100644 index 0000000..c7a20a2 =2D-- /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=3D-1 ;; + c4x | \ + dsp16xx | \ + i960 | \ + hppa* | parisc* | \ + stormy16 | \ + we32k) + sv_cv_stack_direction=3D1 ;; + *) + if test $cross_compiling =3D no; then + cat > conftest.c < +int +find_stack_direction (int *addr, int depth) +{ + int dir, dummy =3D 0; + if (! addr) + addr =3D &dummy; + *addr =3D addr < &dummy ? 1 : addr =3D=3D &dummy ? 0 : -1; + dir =3D 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=3D`./conftest` + else + sv_cv_stack_direction=3D0 + fi + ;; + esac + case $sv_cv_stack_direction in + 1) sv_cv_stack_direction_msg=3D"grows up";; + -1) sv_cv_stack_direction_msg=3D"grows down";; + *) sv_cv_stack_direction_msg=3D"unknown";; + esac + ]) + AC_DEFINE_UNQUOTED([STACK_DIRECTION], [$sv_cv_stack_direction], + [Define as the direction of stack growth for your system. + STACK_DIRECTION > 0 =3D> grows toward higher addresses + STACK_DIRECTION < 0 =3D> grows toward lower addresses + STACK_DIRECTION =3D 0 =3D> spaghetti stack.]) +]) diff --git a/modules/sigsegv b/modules/sigsegv new file mode 100644 index 0000000..2ef8433 =2D-- /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 SIGS= EGV +and catching stack overflow. It does *not* implement the 'sigsegv_dispatch= er' +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* supp= ort +HP-UX, Minix, native Windows; on these platforms the module compiles and +provides a header file, but it does not define HAVE_SIGSEGV_RE= COVERY +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=3Dsigsegv.h +if test "$with_libsigsegv" =3D yes; then + gl_LIBSIGSEGV + if test "$gl_cv_lib_sigsegv" =3D yes; then + SIGSEGV_H=3D + 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 regist= er + 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 i= 386. + 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 +=3D $(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 +=3D sigsegv.h sigsegv.h-t + +if GL_GENERATE_SIGSEGV_H +lib_SOURCES +=3D sigsegv.c stackvma.c +endif + +Include: + + +Link: +$(LTLIBSIGSEGV) when linking with libtool, $(LIBSIGSEGV) otherwise + +License: +GPLv2+ + +Maintainer: +Bruno Haible =2D-=20 2.7.4 --nextPart13627053.fd3Gv6meWk Content-Disposition: attachment; filename="0002-sigsegv-Add-tests.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="0002-sigsegv-Add-tests.patch" >From e3592af1366003104ee4fc4d800ace2d899f06cd Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 16 May 2021 18:29:48 +0200 Subject: [PATCH 2/2] sigsegv: Add tests. * tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with modifications. * tests/test-sigsegv-catch-segv2.c: Likewise. * tests/test-sigsegv-catch-stackoverflow1.c: Likewise. * tests/test-sigsegv-catch-stackoverflow2.c: Likewise. * tests/altstack-util.h: Likewise. * tests/mmap-anon-util.h: Likewise. * modules/sigsegv-tests: New file. --- ChangeLog | 10 ++ modules/sigsegv-tests | 31 +++++ tests/altstack-util.h | 59 +++++++++ tests/mmap-anon-util.h | 97 ++++++++++++++ tests/test-sigsegv-catch-segv1.c | 128 ++++++++++++++++++ tests/test-sigsegv-catch-segv2.c | 151 +++++++++++++++++++++ tests/test-sigsegv-catch-stackoverflow1.c | 148 +++++++++++++++++++++ tests/test-sigsegv-catch-stackoverflow2.c | 209 ++++++++++++++++++++++++++++++ 8 files changed, 833 insertions(+) create mode 100644 modules/sigsegv-tests create mode 100644 tests/altstack-util.h create mode 100644 tests/mmap-anon-util.h create mode 100644 tests/test-sigsegv-catch-segv1.c create mode 100644 tests/test-sigsegv-catch-segv2.c create mode 100644 tests/test-sigsegv-catch-stackoverflow1.c create mode 100644 tests/test-sigsegv-catch-stackoverflow2.c diff --git a/ChangeLog b/ChangeLog index c939655..88f7944 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2021-05-16 Bruno Haible + sigsegv: Add tests. + * tests/test-sigsegv-catch-segv1.c: New file, from GNU libsigsegv with + modifications. + * tests/test-sigsegv-catch-segv2.c: Likewise. + * tests/test-sigsegv-catch-stackoverflow1.c: Likewise. + * tests/test-sigsegv-catch-stackoverflow2.c: Likewise. + * tests/altstack-util.h: Likewise. + * tests/mmap-anon-util.h: Likewise. + * modules/sigsegv-tests: New file. + sigsegv: New module. * lib/sigsegv.in.h: New file, from GNU libsigsegv with modifications. * lib/sigsegv.c: Likewise. diff --git a/modules/sigsegv-tests b/modules/sigsegv-tests new file mode 100644 index 0000000..ed7ed94 --- /dev/null +++ b/modules/sigsegv-tests @@ -0,0 +1,31 @@ +Files: +tests/test-sigsegv-catch-segv1.c +tests/test-sigsegv-catch-segv2.c +tests/test-sigsegv-catch-stackoverflow1.c +tests/test-sigsegv-catch-stackoverflow2.c +tests/altstack-util.h +tests/mmap-anon-util.h +m4/mmap-anon.m4 + +Depends-on: +stdint + +configure.ac: +AC_CHECK_FUNCS_ONCE([setrlimit]) +gl_FUNC_MMAP_ANON + +Makefile.am: +TESTS += \ + test-sigsegv-catch-segv1 \ + test-sigsegv-catch-segv2 \ + test-sigsegv-catch-stackoverflow1 \ + test-sigsegv-catch-stackoverflow2 +check_PROGRAMS += \ + test-sigsegv-catch-segv1 \ + test-sigsegv-catch-segv2 \ + test-sigsegv-catch-stackoverflow1 \ + test-sigsegv-catch-stackoverflow2 +test_sigsegv_catch_segv1_LDADD = $(LDADD) $(LIBSIGSEGV) +test_sigsegv_catch_segv2_LDADD = $(LDADD) $(LIBSIGSEGV) +test_sigsegv_catch_stackoverflow1_LDADD = $(LDADD) $(LIBSIGSEGV) +test_sigsegv_catch_stackoverflow2_LDADD = $(LDADD) $(LIBSIGSEGV) diff --git a/tests/altstack-util.h b/tests/altstack-util.h new file mode 100644 index 0000000..5130645 --- /dev/null +++ b/tests/altstack-util.h @@ -0,0 +1,59 @@ +/* Some auxiliary stuff for defining an alternate stack. + Copyright (C) 2010 Eric Blake + Copyright (C) 2010-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 . */ + +#include /* uintptr_t */ +#include /* for memset */ + +#ifndef SIGSTKSZ +# define SIGSTKSZ 16384 +#endif + +/* glibc says: Users should use SIGSTKSZ as the size of user-supplied + buffers. We want to detect stack overflow of the alternate stack + in a nicer manner than just crashing, so we overallocate in + comparison to what we hand libsigsegv. Also, we intentionally hand + an unaligned pointer, to ensure the alternate stack still ends up + aligned. */ +#define MYSTACK_CRUMPLE_ZONE 8192 +char mystack_storage[SIGSTKSZ + 2 * MYSTACK_CRUMPLE_ZONE + 31]; +char *mystack; /* SIGSTKSZ bytes in the middle of storage. */ + +static void +prepare_alternate_stack (void) +{ + memset (mystack_storage, 's', sizeof mystack_storage); + mystack = (char *) ((uintptr_t) (mystack_storage + MYSTACK_CRUMPLE_ZONE) | 31); +} + +static void +check_alternate_stack_no_overflow (void) +{ + unsigned int i; + + for (i = MYSTACK_CRUMPLE_ZONE; i > 0; i--) + if (*(mystack - i) != 's') + { + printf ("Alternate stack was exceeded by %u bytes!!\n", i); + exit (1); + } + for (i = MYSTACK_CRUMPLE_ZONE; i > 0; i--) + if (*(mystack + SIGSTKSZ - 1 + i) != 's') + { + printf ("Alternate stack was exceeded by %u bytes!!\n", i); + exit (1); + } +} diff --git a/tests/mmap-anon-util.h b/tests/mmap-anon-util.h new file mode 100644 index 0000000..6fb82ef --- /dev/null +++ b/tests/mmap-anon-util.h @@ -0,0 +1,97 @@ +/* Some auxiliary stuff for using mmap & friends. + Copyright (C) 2002-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 . */ + +#if defined _WIN32 && !defined __CYGWIN__ + +/* ------------------------ Windows ------------------------ */ + +# define WIN32_LEAN_AND_MEAN /* avoid including junk */ +# include +# include +# define PROT_NONE PAGE_NOACCESS +# define PROT_READ PAGE_READONLY +# define PROT_READ_WRITE PAGE_READWRITE + +static void * +mmap_zeromap (void *map_addr_hint, size_t map_len) +{ + if (VirtualAlloc ((void *)((uintptr_t) map_addr_hint & -0x10000), + (((uintptr_t) map_addr_hint + map_len - 1) | 0xffff) + 1 + - ((uintptr_t) map_addr_hint & -0x10000), + MEM_RESERVE, PAGE_NOACCESS) + && VirtualAlloc (map_addr_hint, map_len, MEM_COMMIT, PAGE_READWRITE)) + return map_addr_hint; + else + return (void *)(-1); +} + +int +munmap (void *addr, size_t len) +{ + if (VirtualFree (addr, len, MEM_DECOMMIT)) + return 0; + else + return -1; +} + +int +mprotect (void *addr, size_t len, int prot) +{ + DWORD oldprot; + + if (VirtualProtect (addr, len, prot, &oldprot)) + return 0; + else + return -1; +} + +#else + +/* ------------------------ Unix ------------------------ */ + +# include +# include +# include + +# ifndef PROT_NONE +# define PROT_NONE 0 +# endif +# define PROT_READ_WRITE (PROT_READ|PROT_WRITE) + +# if HAVE_MAP_ANONYMOUS +# define zero_fd -1 +# define map_flags MAP_ANONYMOUS | MAP_PRIVATE +# else +# ifndef MAP_FILE +# define MAP_FILE 0 +# endif +static int zero_fd; +# define map_flags MAP_FILE | MAP_PRIVATE +# endif + +static void * +mmap_zeromap (void *map_addr_hint, size_t map_len) +{ +# ifdef __hpux + /* HP-UX 10 mmap() often fails when given a hint. So give the OS complete + freedom about the address range. */ + return (void *) mmap ((void *) 0, map_len, PROT_READ_WRITE, map_flags, zero_fd, 0); +# else + return (void *) mmap (map_addr_hint, map_len, PROT_READ_WRITE, map_flags, zero_fd, 0); +# endif +} + +#endif diff --git a/tests/test-sigsegv-catch-segv1.c b/tests/test-sigsegv-catch-segv1.c new file mode 100644 index 0000000..62eef69 --- /dev/null +++ b/tests/test-sigsegv-catch-segv1.c @@ -0,0 +1,128 @@ +/* Test that the handler is called, with the right fault address. + Copyright (C) 2002-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 . */ + +#include + +/* Specification. */ +#include "sigsegv.h" + +#include +#include + +#if HAVE_SIGSEGV_RECOVERY + +# include "mmap-anon-util.h" +# include + +# if SIGSEGV_FAULT_ADDRESS_ALIGNMENT > 1UL +# include +# define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS (getpagesize () - 1) +# else +# define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS 0 +# endif + +uintptr_t page; + +volatile int handler_called = 0; + +int +handler (void *fault_address, int serious) +{ + handler_called++; + if (handler_called > 10) + abort (); + if (fault_address + != (void *)((page + 0x678) & ~SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS)) + abort (); + if (mprotect ((void *) page, 0x4000, PROT_READ_WRITE) == 0) + return 1; + return 0; +} + +void +crasher (uintptr_t p) +{ + *(volatile int *) (p + 0x678) = 42; +} + +int +main () +{ + int prot_unwritable; + void *p; + + /* Preparations. */ +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); +# endif + +# if defined __linux__ && defined __sparc__ + /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as + PROT_READ | PROT_WRITE. */ + prot_unwritable = PROT_NONE; +# else + prot_unwritable = PROT_READ; +# endif + + /* Setup some mmaped memory. */ + p = mmap_zeromap ((void *) 0x12340000, 0x4000); + if (p == (void *)(-1)) + { + fprintf (stderr, "mmap_zeromap failed.\n"); + exit (2); + } + page = (uintptr_t) p; + + /* Make it read-only. */ + if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0) + { + fprintf (stderr, "mprotect failed.\n"); + exit (2); + } + /* Test whether it's possible to make it read-write after it was read-only. + This is not possible on Cygwin. */ + if (mprotect ((void *) page, 0x4000, PROT_READ_WRITE) < 0 + || mprotect ((void *) page, 0x4000, prot_unwritable) < 0) + { + fprintf (stderr, "mprotect failed.\n"); + exit (2); + } + + /* Install the SIGSEGV handler. */ + sigsegv_install_handler (&handler); + + /* The first write access should invoke the handler and then complete. */ + crasher (page); + /* The second write access should not invoke the handler. */ + crasher (page); + + /* Check that the handler was called only once. */ + if (handler_called != 1) + exit (1); + /* Test passed! */ + printf ("Test passed.\n"); + return 0; +} + +#else + +int +main () +{ + return 77; +} + +#endif diff --git a/tests/test-sigsegv-catch-segv2.c b/tests/test-sigsegv-catch-segv2.c new file mode 100644 index 0000000..dd28517 --- /dev/null +++ b/tests/test-sigsegv-catch-segv2.c @@ -0,0 +1,151 @@ +/* Test that the handler can be exited multiple times. + Copyright (C) 2002-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 . */ + +#include + +/* Specification. */ +#include "sigsegv.h" + +#include +#include + +#if HAVE_SIGSEGV_RECOVERY + +# if defined _WIN32 && !defined __CYGWIN__ + /* Windows doesn't have sigset_t. */ + typedef int sigset_t; +# define sigemptyset(set) +# define sigprocmask(how,set,oldset) +# endif + +# include "mmap-anon-util.h" +# include /* for abort, exit */ +# include +# include + +# if SIGSEGV_FAULT_ADDRESS_ALIGNMENT > 1UL +# include +# define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS (getpagesize () - 1) +# else +# define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS 0 +# endif + +jmp_buf mainloop; +sigset_t mainsigset; + +volatile int pass = 0; +uintptr_t page; + +volatile int handler_called = 0; + +static void +handler_continuation (void *arg1, void *arg2, void *arg3) +{ + longjmp (mainloop, pass); +} + +int +handler (void *fault_address, int serious) +{ + handler_called++; + if (handler_called > 10) + abort (); + if (fault_address + != (void *)((page + 0x678 + 8 * pass) & ~SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS)) + abort (); + pass++; + printf ("Fault %d caught.\n", pass); + sigprocmask (SIG_SETMASK, &mainsigset, NULL); + return sigsegv_leave_handler (handler_continuation, NULL, NULL, NULL); +} + +void +crasher (uintptr_t p) +{ + *(volatile int *) (p + 0x678 + 8 * pass) = 42; +} + +int +main () +{ + int prot_unwritable; + void *p; + sigset_t emptyset; + + /* Preparations. */ +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); +# endif + +# if defined __linux__ && defined __sparc__ + /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as + PROT_READ | PROT_WRITE. */ + prot_unwritable = PROT_NONE; +# else + prot_unwritable = PROT_READ; +# endif + + /* Setup some mmaped memory. */ + p = mmap_zeromap ((void *) 0x12340000, 0x4000); + if (p == (void *)(-1)) + { + fprintf (stderr, "mmap_zeromap failed.\n"); + exit (2); + } + page = (uintptr_t) p; + + /* Make it read-only. */ + if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0) + { + fprintf (stderr, "mprotect failed.\n"); + exit (2); + } + + /* Install the SIGSEGV handler. */ + if (sigsegv_install_handler (&handler) < 0) + exit (2); + + /* Save the current signal mask. */ + sigemptyset (&emptyset); + sigprocmask (SIG_BLOCK, &emptyset, &mainsigset); + + /* Provoke two SIGSEGVs in a row. */ + switch (setjmp (mainloop)) + { + case 0: case 1: + printf ("Doing SIGSEGV pass %d.\n", pass + 1); + crasher (page); + printf ("no SIGSEGV?!\n"); exit (1); + case 2: + break; + default: + abort (); + } + + /* Test passed! */ + printf ("Test passed.\n"); + return 0; +} + +#else + +int +main () +{ + return 77; +} + +#endif diff --git a/tests/test-sigsegv-catch-stackoverflow1.c b/tests/test-sigsegv-catch-stackoverflow1.c new file mode 100644 index 0000000..141dfd4 --- /dev/null +++ b/tests/test-sigsegv-catch-stackoverflow1.c @@ -0,0 +1,148 @@ +/* Test the stack overflow handler. + Copyright (C) 2002-2021 Bruno Haible + Copyright (C) 2010 Eric Blake + + 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 + +#if HAVE_STACK_OVERFLOW_RECOVERY + +# if defined _WIN32 && !defined __CYGWIN__ + /* Windows doesn't have sigset_t. */ + typedef int sigset_t; +# define sigemptyset(set) +# define sigprocmask(how,set,oldset) +# endif + +# include /* needed for NULL on SunOS4 */ +# include /* for abort, exit */ +# include +# include +# if HAVE_SETRLIMIT +# include +# include +# include +# endif +# include "altstack-util.h" + +jmp_buf mainloop; +sigset_t mainsigset; + +volatile int pass = 0; + +volatile char *stack_lower_bound; +volatile char *stack_upper_bound; + +static void +stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3) +{ + int arg = (int) (long) arg1; + longjmp (mainloop, arg); +} + +void +stackoverflow_handler (int emergency, stackoverflow_context_t scp) +{ + char dummy; + volatile char *addr = &dummy; + if (!(addr >= stack_lower_bound && addr <= stack_upper_bound)) + abort (); + pass++; + printf ("Stack overflow %d caught.\n", pass); + sigprocmask (SIG_SETMASK, &mainsigset, NULL); + sigsegv_leave_handler (stackoverflow_handler_continuation, + (void *) (long) (emergency ? -1 : pass), NULL, NULL); +} + +volatile int * +recurse_1 (int n, volatile int *p) +{ + if (n < INT_MAX) + *recurse_1 (n + 1, p) += n; + return p; +} + +int +recurse (volatile int n) +{ + return *recurse_1 (n, &n); +} + +int +main () +{ + sigset_t emptyset; + +# if HAVE_SETRLIMIT && defined RLIMIT_STACK + /* Before starting the endless recursion, try to be friendly to the user's + machine. On some Linux 2.2.x systems, there is no stack limit for user + processes at all. We don't want to kill such systems. */ + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */ + setrlimit (RLIMIT_STACK, &rl); +# endif + + /* Prepare the storage for the alternate stack. */ + prepare_alternate_stack (); + + /* Install the stack overflow handler. */ + if (stackoverflow_install_handler (&stackoverflow_handler, + mystack, SIGSTKSZ) + < 0) + exit (2); + stack_lower_bound = mystack; + stack_upper_bound = mystack + SIGSTKSZ - 1; + + /* Save the current signal mask. */ + sigemptyset (&emptyset); + sigprocmask (SIG_BLOCK, &emptyset, &mainsigset); + + /* Provoke two stack overflows in a row. */ + switch (setjmp (mainloop)) + { + case -1: + printf ("emergency exit\n"); exit (1); + case 0: case 1: + printf ("Starting recursion pass %d.\n", pass + 1); + recurse (0); + printf ("no endless recursion?!\n"); exit (1); + case 2: + break; + default: + abort (); + } + + /* Validate that the alternate stack did not overflow. */ + check_alternate_stack_no_overflow (); + + printf ("Test passed.\n"); + exit (0); +} + +#else + +int +main () +{ + return 77; +} + +#endif diff --git a/tests/test-sigsegv-catch-stackoverflow2.c b/tests/test-sigsegv-catch-stackoverflow2.c new file mode 100644 index 0000000..415b8a4 --- /dev/null +++ b/tests/test-sigsegv-catch-stackoverflow2.c @@ -0,0 +1,209 @@ +/* Test that stack overflow and SIGSEGV are correctly distinguished. + Copyright (C) 2002-2021 Bruno Haible + Copyright (C) 2010 Eric Blake + + 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 +#include + +#if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGSEGV_RECOVERY + +# if defined _WIN32 && !defined __CYGWIN__ + /* Windows doesn't have sigset_t. */ + typedef int sigset_t; +# define sigemptyset(set) +# define sigprocmask(how,set,oldset) +# endif + +# include "mmap-anon-util.h" +# include /* needed for NULL on SunOS4 */ +# include /* for abort, exit */ +# include +# include +# if HAVE_SETRLIMIT +# include +# include +# include +# endif +# include "altstack-util.h" + +jmp_buf mainloop; +sigset_t mainsigset; + +volatile int pass = 0; +uintptr_t page; + +static void +stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3) +{ + int arg = (int) (long) arg1; + longjmp (mainloop, arg); +} + +void +stackoverflow_handler (int emergency, stackoverflow_context_t scp) +{ + pass++; + if (pass <= 2) + printf ("Stack overflow %d caught.\n", pass); + else + { + printf ("Segmentation violation misdetected as stack overflow.\n"); + exit (1); + } + sigprocmask (SIG_SETMASK, &mainsigset, NULL); + sigsegv_leave_handler (stackoverflow_handler_continuation, + (void *) (long) (emergency ? -1 : pass), NULL, NULL); +} + +int +sigsegv_handler (void *address, int emergency) +{ + /* This test is necessary to distinguish stack overflow and SIGSEGV. */ + if (!emergency) + return 0; + + pass++; + if (pass <= 2) + { + printf ("Stack overflow %d missed.\n", pass); + exit (1); + } + else + printf ("Segmentation violation correctly detected.\n"); + sigprocmask (SIG_SETMASK, &mainsigset, NULL); + return sigsegv_leave_handler (stackoverflow_handler_continuation, + (void *) (long) pass, NULL, NULL); +} + +volatile int * +recurse_1 (int n, volatile int *p) +{ + if (n < INT_MAX) + *recurse_1 (n + 1, p) += n; + return p; +} + +int +recurse (volatile int n) +{ + return *recurse_1 (n, &n); +} + +int +main () +{ + int prot_unwritable; + void *p; + sigset_t emptyset; + +# if HAVE_SETRLIMIT && defined RLIMIT_STACK + /* Before starting the endless recursion, try to be friendly to the user's + machine. On some Linux 2.2.x systems, there is no stack limit for user + processes at all. We don't want to kill such systems. */ + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */ + setrlimit (RLIMIT_STACK, &rl); +# endif + + /* Prepare the storage for the alternate stack. */ + prepare_alternate_stack (); + + /* Install the stack overflow handler. */ + if (stackoverflow_install_handler (&stackoverflow_handler, + mystack, SIGSTKSZ) + < 0) + exit (2); + + /* Preparations. */ +# if !HAVE_MAP_ANONYMOUS + zero_fd = open ("/dev/zero", O_RDONLY, 0644); +# endif + +# if defined __linux__ && defined __sparc__ + /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as + PROT_READ | PROT_WRITE. */ + prot_unwritable = PROT_NONE; +# else + prot_unwritable = PROT_READ; +# endif + + /* Setup some mmaped memory. */ + p = mmap_zeromap ((void *) 0x12340000, 0x4000); + if (p == (void *)(-1)) + { + fprintf (stderr, "mmap_zeromap failed.\n"); + exit (2); + } + page = (uintptr_t) p; + + /* Make it read-only. */ + if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0) + { + fprintf (stderr, "mprotect failed.\n"); + exit (2); + } + + /* Install the SIGSEGV handler. */ + if (sigsegv_install_handler (&sigsegv_handler) < 0) + exit (2); + + /* Save the current signal mask. */ + sigemptyset (&emptyset); + sigprocmask (SIG_BLOCK, &emptyset, &mainsigset); + + /* Provoke two stack overflows in a row. */ + switch (setjmp (mainloop)) + { + case -1: + printf ("emergency exit\n"); exit (1); + case 0: case 1: + printf ("Starting recursion pass %d.\n", pass + 1); + recurse (0); + printf ("no endless recursion?!\n"); exit (1); + case 2: + *(volatile int *) (page + 0x678) = 42; + break; + case 3: + *(volatile int *) 0 = 42; + break; + case 4: + break; + default: + abort (); + } + + /* Validate that the alternate stack did not overflow. */ + check_alternate_stack_no_overflow (); + + printf ("Test passed.\n"); + exit (0); +} + +#else + +int +main () +{ + return 77; +} + +#endif -- 2.7.4 --nextPart13627053.fd3Gv6meWk--