(root)/
diffutils-3.10/
gnulib-tests/
test-sigsegv-catch-segv2.c
       1  /* Test that the handler can be exited multiple times.
       2     Copyright (C) 2002-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation; either version 2 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "sigsegv.h"
      23  
      24  #include <stdint.h>
      25  #include <stdio.h>
      26  
      27  #if HAVE_SIGSEGV_RECOVERY
      28  
      29  # if defined _WIN32 && !defined __CYGWIN__
      30    /* Windows doesn't have sigset_t.  */
      31    typedef int sigset_t;
      32  #  define sigemptyset(set)
      33  #  define sigprocmask(how,set,oldset)
      34  # endif
      35  
      36  # include "mmap-anon-util.h"
      37  # include <stdlib.h> /* for abort, exit */
      38  # include <signal.h>
      39  # include <setjmp.h>
      40  
      41  # if SIGSEGV_FAULT_ADDRESS_ALIGNMENT > 1UL
      42  #  include <unistd.h>
      43  #  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS (getpagesize () - 1)
      44  # else
      45  #  define SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS 0
      46  # endif
      47  
      48  jmp_buf mainloop;
      49  sigset_t mainsigset;
      50  
      51  volatile int pass = 0;
      52  uintptr_t page;
      53  
      54  volatile int handler_called = 0;
      55  
      56  static void
      57  handler_continuation (void *arg1, void *arg2, void *arg3)
      58  {
      59    longjmp (mainloop, pass);
      60  }
      61  
      62  int
      63  handler (void *fault_address, int serious)
      64  {
      65    handler_called++;
      66    if (handler_called > 10)
      67      abort ();
      68    if (fault_address
      69        != (void *)((page + 0x678 + 8 * pass) & ~SIGSEGV_FAULT_ADDRESS_ROUNDOFF_BITS))
      70      abort ();
      71    pass++;
      72    printf ("Fault %d caught.\n", pass);
      73    sigprocmask (SIG_SETMASK, &mainsigset, NULL);
      74    return sigsegv_leave_handler (handler_continuation, NULL, NULL, NULL);
      75  }
      76  
      77  void
      78  crasher (uintptr_t p)
      79  {
      80    *(volatile int *) (p + 0x678 + 8 * pass) = 42;
      81  }
      82  
      83  int
      84  main ()
      85  {
      86    int prot_unwritable;
      87    void *p;
      88    sigset_t emptyset;
      89  
      90    /* Preparations.  */
      91  # if !HAVE_MAP_ANONYMOUS
      92    zero_fd = open ("/dev/zero", O_RDONLY, 0644);
      93  # endif
      94  
      95  # if defined __linux__ && defined __sparc__
      96    /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as
      97       PROT_READ | PROT_WRITE.  */
      98    prot_unwritable = PROT_NONE;
      99  # else
     100    prot_unwritable = PROT_READ;
     101  # endif
     102  
     103    /* Setup some mmaped memory.  */
     104    p = mmap_zeromap ((void *) 0x12340000, 0x4000);
     105    if (p == (void *)(-1))
     106      {
     107        fprintf (stderr, "mmap_zeromap failed.\n");
     108        exit (2);
     109      }
     110    page = (uintptr_t) p;
     111  
     112    /* Make it read-only.  */
     113    if (mprotect ((void *) page, 0x4000, prot_unwritable) < 0)
     114      {
     115        fprintf (stderr, "mprotect failed.\n");
     116        exit (2);
     117      }
     118  
     119    /* Install the SIGSEGV handler.  */
     120    if (sigsegv_install_handler (&handler) < 0)
     121      exit (2);
     122  
     123    /* Save the current signal mask.  */
     124    sigemptyset (&emptyset);
     125    sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
     126  
     127    /* Provoke two SIGSEGVs in a row.  */
     128    switch (setjmp (mainloop))
     129      {
     130      case 0: case 1:
     131        printf ("Doing SIGSEGV pass %d.\n", pass + 1);
     132        crasher (page);
     133        printf ("no SIGSEGV?!\n"); exit (1);
     134      case 2:
     135        break;
     136      default:
     137        abort ();
     138      }
     139  
     140    /* Test passed!  */
     141    printf ("Test passed.\n");
     142    return 0;
     143  }
     144  
     145  #else
     146  
     147  int
     148  main ()
     149  {
     150    return 77;
     151  }
     152  
     153  #endif