Fix last ChangeLog entry.
[gnulib.git] / tests / test-sigsegv-catch-stackoverflow1.c
blobcb20bceb8e66c7f2228a34270ac60103d919d545
1 /* Test the stack overflow handler.
2 Copyright (C) 2002-2025 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible and Eric Blake. */
19 #include <config.h>
21 /* On GNU/Hurd, when compiling with -D_FORTIFY_SOURCE=2, avoid an error
22 "*** longjmp causes uninitialized stack frame ***: terminated".
23 Cf. <https://sourceware.org/bugzilla/show_bug.cgi?id=32522> */
24 #ifdef __GNU__
25 # undef _FORTIFY_SOURCE
26 # undef __USE_FORTIFY_LEVEL
27 #endif
29 /* Specification. */
30 #include "sigsegv.h"
32 #include <stdio.h>
33 #include <limits.h>
35 /* Skip this test when an address sanitizer is in use. */
36 #ifndef __has_feature
37 # define __has_feature(a) 0
38 #endif
39 #if defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer)
40 # undef HAVE_STACK_OVERFLOW_RECOVERY
41 #endif
43 #if HAVE_STACK_OVERFLOW_RECOVERY
45 # if defined _WIN32 && !defined __CYGWIN__
46 /* Windows doesn't have sigset_t. */
47 typedef int sigset_t;
48 # define sigemptyset(set)
49 # define sigprocmask(how,set,oldset)
50 # endif
52 # include <stddef.h> /* needed for NULL on SunOS4 */
53 # include <stdlib.h> /* for abort, exit */
54 # include <signal.h>
55 # include <setjmp.h>
56 # if HAVE_SETRLIMIT
57 # include <sys/types.h>
58 # include <sys/time.h>
59 # include <sys/resource.h>
60 # endif
61 # include "altstack-util.h"
63 static jmp_buf mainloop;
64 static sigset_t mainsigset;
66 static volatile int pass = 0;
68 static volatile char *stack_lower_bound;
69 static volatile char *stack_upper_bound;
71 static void
72 stackoverflow_handler_continuation (void *arg1, void *arg2, void *arg3)
74 #if defined __NetBSD__ && defined __i386__
75 /* On NetBSD 10.0/i386, when built as part of a testdir-all (but not as part
76 of a testdir for just the module 'sigsegv'!) this program crashes. The
77 cause is that:
78 - The alternate stack is not aligned (which is intentional, see
79 altstack-util.h) and NetBSD does not align the stack pointer while
80 switching to the alternate stack.
81 - When %esp is not aligned, the dynamic linker crashes in function
82 _rtld_bind while resolving the symbol 'longjmp'.
83 We would around this by aligning the stack pointer, to a multiple of 8. */
84 int *argp;
85 __asm__ __volatile__ ("movl %1,%0" : "=r" (argp) : "r" (&arg1));
86 unsigned long sp;
87 __asm__ __volatile__ ("movl %%esp,%0" : "=r" (sp));
88 sp &= ~7UL;
89 __asm__ __volatile__ ("movl %0,%%esp" : : "r" (sp));
90 int arg = *argp;
91 #else
92 int arg = (int) (long) arg1;
93 #endif
94 longjmp (mainloop, arg);
97 static void
98 stackoverflow_handler (int emergency, stackoverflow_context_t scp)
100 char dummy;
101 volatile char *addr = &dummy;
102 if (!(addr >= stack_lower_bound && addr <= stack_upper_bound))
103 abort ();
104 pass++;
105 printf ("Stack overflow %d caught.\n", pass);
106 sigprocmask (SIG_SETMASK, &mainsigset, NULL);
107 sigsegv_leave_handler (stackoverflow_handler_continuation,
108 (void *) (long) (emergency ? -1 : pass), NULL, NULL);
111 static volatile int *
112 recurse_1 (int n, volatile int *p)
114 if (n < INT_MAX)
115 *recurse_1 (n + 1, p) += n;
116 return p;
119 static int
120 recurse (volatile int n)
122 return *recurse_1 (n, &n);
126 main ()
128 sigset_t emptyset;
130 # if HAVE_SETRLIMIT && defined RLIMIT_STACK
131 /* Before starting the endless recursion, try to be friendly to the user's
132 machine. On some Linux 2.2.x systems, there is no stack limit for user
133 processes at all. We don't want to kill such systems. */
134 struct rlimit rl;
135 rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
136 setrlimit (RLIMIT_STACK, &rl);
137 # endif
139 /* Prepare the storage for the alternate stack. */
140 prepare_alternate_stack ();
142 /* Install the stack overflow handler. */
143 if (stackoverflow_install_handler (&stackoverflow_handler,
144 mystack, MYSTACK_SIZE)
145 < 0)
146 exit (2);
147 stack_lower_bound = mystack;
148 stack_upper_bound = mystack + MYSTACK_SIZE - 1;
150 /* Save the current signal mask. */
151 sigemptyset (&emptyset);
152 sigprocmask (SIG_BLOCK, &emptyset, &mainsigset);
154 /* Provoke two stack overflows in a row. */
155 switch (setjmp (mainloop))
157 case -1:
158 printf ("emergency exit\n"); exit (1);
159 case 0: case 1:
160 printf ("Starting recursion pass %d.\n", pass + 1);
161 recurse (0);
162 printf ("no endless recursion?!\n"); exit (1);
163 case 2:
164 break;
165 default:
166 abort ();
169 /* Validate that the alternate stack did not overflow. */
170 check_alternate_stack_no_overflow ();
172 printf ("Test passed.\n");
173 exit (0);
176 #else
179 main ()
181 return 77;
184 #endif