1 /* Test that stack overflow and SIGSEGV are correctly distinguished.
2 Copyright (C) 2002-2024 Bruno Haible <bruno@clisp.org>
3 Copyright (C) 2010 Eric Blake <eblake@redhat.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
27 /* Skip this test when an address sanitizer is in use. */
29 # define __has_feature(a) 0
31 #if defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer)
32 # undef HAVE_STACK_OVERFLOW_RECOVERY
35 #if HAVE_STACK_OVERFLOW_RECOVERY && HAVE_SIGSEGV_RECOVERY
37 #if defined _WIN32 && !defined __CYGWIN__
38 /* Windows doesn't have sigset_t. */
40 # define sigemptyset(set)
41 # define sigprocmask(how,set,oldset)
44 #include "mmap-anon-util.h"
45 #include <stddef.h> /* needed for NULL on SunOS4 */
46 #include <stdlib.h> /* for abort, exit */
50 # include <sys/types.h>
51 # include <sys/time.h>
52 # include <sys/resource.h>
54 #include "altstack-util.h"
56 static jmp_buf mainloop
;
57 static sigset_t mainsigset
;
59 static volatile int pass
= 0;
60 static uintptr_t page
;
61 static int *volatile null_pointer
/* = NULL */;
64 stackoverflow_handler_continuation (void *arg1
, void *arg2
, void *arg3
)
66 int arg
= (int) (long) arg1
;
67 longjmp (mainloop
, arg
);
71 stackoverflow_handler (int emergency
, stackoverflow_context_t scp
)
75 printf ("Stack overflow %d caught.\n", pass
);
78 printf ("Segmentation violation misdetected as stack overflow.\n");
81 sigprocmask (SIG_SETMASK
, &mainsigset
, NULL
);
82 sigsegv_leave_handler (stackoverflow_handler_continuation
,
83 (void *) (long) (emergency
? -1 : pass
), NULL
, NULL
);
87 sigsegv_handler (void *address
, int emergency
)
89 /* This test is necessary to distinguish stack overflow and SIGSEGV. */
96 printf ("Stack overflow %d missed.\n", pass
);
100 printf ("Segmentation violation correctly detected.\n");
101 sigprocmask (SIG_SETMASK
, &mainsigset
, NULL
);
102 return sigsegv_leave_handler (stackoverflow_handler_continuation
,
103 (void *) (long) pass
, NULL
, NULL
);
106 static volatile int *
107 recurse_1 (int n
, volatile int *p
)
110 *recurse_1 (n
+ 1, p
) += n
;
115 recurse (volatile int n
)
117 return *recurse_1 (n
, &n
);
127 #if HAVE_SETRLIMIT && defined RLIMIT_STACK
128 /* Before starting the endless recursion, try to be friendly to the user's
129 machine. On some Linux 2.2.x systems, there is no stack limit for user
130 processes at all. We don't want to kill such systems. */
132 rl
.rlim_cur
= rl
.rlim_max
= 0x100000; /* 1 MB */
133 setrlimit (RLIMIT_STACK
, &rl
);
136 /* Prepare the storage for the alternate stack. */
137 prepare_alternate_stack ();
139 /* Install the stack overflow handler. */
140 if (stackoverflow_install_handler (&stackoverflow_handler
,
146 #if !HAVE_MMAP_ANON && !HAVE_MMAP_ANONYMOUS && HAVE_MMAP_DEVZERO
147 zero_fd
= open ("/dev/zero", O_RDONLY
, 0644);
150 #if defined __linux__ && defined __sparc__
151 /* On Linux 2.6.26/SPARC64, PROT_READ has the same effect as
152 PROT_READ | PROT_WRITE. */
153 prot_unwritable
= PROT_NONE
;
155 prot_unwritable
= PROT_READ
;
158 /* Setup some mmapped memory. */
159 p
= mmap_zeromap ((void *) 0x12340000, 0x4000);
160 if (p
== (void *)(-1))
162 fprintf (stderr
, "mmap_zeromap failed.\n");
165 page
= (uintptr_t) p
;
167 /* Make it read-only. */
168 if (mprotect ((void *) page
, 0x4000, prot_unwritable
) < 0)
170 fprintf (stderr
, "mprotect failed.\n");
174 /* Install the SIGSEGV handler. */
175 if (sigsegv_install_handler (&sigsegv_handler
) < 0)
178 /* Save the current signal mask. */
179 sigemptyset (&emptyset
);
180 sigprocmask (SIG_BLOCK
, &emptyset
, &mainsigset
);
182 /* Provoke two stack overflows in a row. */
183 switch (setjmp (mainloop
))
186 printf ("emergency exit\n"); exit (1);
188 printf ("Starting recursion pass %d.\n", pass
+ 1);
190 printf ("no endless recursion?!\n"); exit (1);
192 *(volatile int *) (page
+ 0x678) = 42;
203 /* Validate that the alternate stack did not overflow. */
204 check_alternate_stack_no_overflow ();
206 printf ("Test passed.\n");