c-stack: avoid compiler optimizations when provoking overflow
[gnulib.git] / m4 / c-stack.m4
1 # Check prerequisites for compiling lib/c-stack.c.
2
3 # Copyright (C) 2002, 2003, 2004, 2008 Free Software Foundation, Inc.
4 # This file is free software; the Free Software Foundation
5 # gives unlimited permission to copy and/or distribute it,
6 # with or without modifications, as long as this notice is preserved.
7
8 # Written by Paul Eggert.
9
10 # serial 7
11
12 AC_DEFUN([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC],
13   [# for STACK_DIRECTION
14    AC_REQUIRE([AC_FUNC_ALLOCA])
15    AC_CHECK_FUNCS_ONCE([setrlimit])
16    AC_CHECK_HEADERS_ONCE([ucontext.h])
17
18    AC_CACHE_CHECK([for working C stack overflow detection],
19      [ac_cv_sys_stack_overflow_works],
20      [AC_TRY_RUN(
21         [
22          #include <unistd.h>
23          #include <signal.h>
24          #if HAVE_SETRLIMIT
25          # include <sys/types.h>
26          # include <sys/time.h>
27          # include <sys/resource.h>
28          #endif
29          #ifndef SIGSTKSZ
30          # define SIGSTKSZ 16384
31          #endif
32
33          static union
34          {
35            char buffer[2 * SIGSTKSZ];
36            long double ld;
37            long u;
38            void *p;
39          } alternate_signal_stack;
40
41          static void
42          segv_handler (int signo)
43          {
44            _exit (0);
45          }
46
47          static int
48          c_stack_action ()
49          {
50            stack_t st;
51            struct sigaction act;
52            int r;
53
54            st.ss_flags = 0;
55            /* Use the midpoint to avoid Irix sigaltstack bug.  */
56            st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ;
57            st.ss_size = SIGSTKSZ;
58            r = sigaltstack (&st, 0);
59            if (r != 0)
60              return r;
61
62            sigemptyset (&act.sa_mask);
63            act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
64            act.sa_handler = segv_handler;
65            return sigaction (SIGSEGV, &act, 0);
66          }
67          static volatile int *
68          recurse_1 (volatile int n, volatile int *p)
69          {
70            if (n >= 0)
71              *recurse_1 (n + 1, p) += n;
72            return p;
73          }
74          static int
75          recurse (volatile int n)
76          {
77            int sum = 0;
78            return *recurse_1 (n, &sum);
79          }
80          int
81          main ()
82          {
83            #if HAVE_SETRLIMIT && defined RLIMIT_STACK
84            /* Before starting the endless recursion, try to be friendly
85               to the user's machine.  On some Linux 2.2.x systems, there
86               is no stack limit for user processes at all.  We don't want
87               to kill such systems.  */
88            struct rlimit rl;
89            rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
90            setrlimit (RLIMIT_STACK, &rl);
91            #endif
92
93            return c_stack_action () || recurse (0);
94          }
95         ],
96         [ac_cv_sys_stack_overflow_works=yes],
97         [ac_cv_sys_stack_overflow_works=no],
98         [ac_cv_sys_stack_overflow_works=cross-compiling])])
99
100   if test $ac_cv_sys_stack_overflow_works = yes; then
101    AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], [1],
102      [Define to 1 if extending the stack slightly past the limit causes
103       a SIGSEGV which can be handled on an alternate stack established
104       with sigaltstack.])
105
106     dnl The ss_sp field of a stack_t is, according to POSIX, the lowest address
107     dnl of the memory block designated as an alternate stack. But IRIX 5.3
108     dnl interprets it as the highest address!
109     AC_CACHE_CHECK([for correct stack_t interpretation],
110       [gl_cv_sigaltstack_low_base], [
111       AC_RUN_IFELSE([
112         AC_LANG_SOURCE([[
113 #include <stdlib.h>
114 #include <signal.h>
115 #if HAVE_SYS_SIGNAL_H
116 # include <sys/signal.h>
117 #endif
118 #ifndef SIGSTKSZ
119 # define SIGSTKSZ 16384
120 #endif
121 volatile char *stack_lower_bound;
122 volatile char *stack_upper_bound;
123 static void check_stack_location (volatile char *addr)
124 {
125   if (addr >= stack_lower_bound && addr <= stack_upper_bound)
126     exit (0);
127   else
128     exit (1);
129 }
130 static void stackoverflow_handler (int sig)
131 {
132   char dummy;
133   check_stack_location (&dummy);
134 }
135 int main ()
136 {
137   char mystack[2 * SIGSTKSZ];
138   stack_t altstack;
139   struct sigaction action;
140   /* Install the alternate stack.  */
141   altstack.ss_sp = mystack + SIGSTKSZ;
142   altstack.ss_size = SIGSTKSZ;
143   stack_lower_bound = (char *) altstack.ss_sp;
144   stack_upper_bound = (char *) altstack.ss_sp + altstack.ss_size - 1;
145   altstack.ss_flags = 0; /* no SS_DISABLE */
146   if (sigaltstack (&altstack, NULL) < 0)
147     exit (2);
148   /* Install the SIGSEGV handler.  */
149   sigemptyset (&action.sa_mask);
150   action.sa_handler = &stackoverflow_handler;
151   action.sa_flags = SA_ONSTACK;
152   if (sigaction (SIGSEGV, &action, (struct sigaction *) NULL) < 0)
153     exit(3);
154   /* Provoke a SIGSEGV.  */
155   raise (SIGSEGV);
156   exit (3);
157 }]])],
158       [gl_cv_sigaltstack_low_base=yes],
159       [gl_cv_sigaltstack_low_base=no],
160       [gl_cv_sigaltstack_low_base=cross-compiling])])
161    if test "$gl_cv_sigaltstack_low_base" = no; then
162       AC_DEFINE([SIGALTSTACK_SS_REVERSED], [1],
163         [Define if sigaltstack() interprets the stack_t.ss_sp field
164          incorrectly, as the highest address of the alternate stack range
165          rather than as the lowest address.])
166     fi
167
168    AC_CACHE_CHECK([for precise C stack overflow detection],
169      ac_cv_sys_xsi_stack_overflow_heuristic,
170      [AC_TRY_RUN(
171         [
172          #include <unistd.h>
173          #include <signal.h>
174          #if HAVE_UCONTEXT_H
175          # include <ucontext.h>
176          #endif
177          #if HAVE_SETRLIMIT
178          # include <sys/types.h>
179          # include <sys/time.h>
180          # include <sys/resource.h>
181          #endif
182          #ifndef SIGSTKSZ
183          # define SIGSTKSZ 16384
184          #endif
185
186          static union
187          {
188            char buffer[2 * SIGSTKSZ];
189            long double ld;
190            long u;
191            void *p;
192          } alternate_signal_stack;
193
194          #if STACK_DIRECTION
195          # define find_stack_direction(ptr) STACK_DIRECTION
196          #else
197          static int
198          find_stack_direction (char const *addr)
199          {
200            char dummy;
201            return (! addr ? find_stack_direction (&dummy)
202                    : addr < &dummy ? 1 : -1);
203          }
204          #endif
205
206          static void
207          segv_handler (int signo, siginfo_t *info, void *context)
208          {
209            if (0 < info->si_code)
210              {
211                /* For XSI heuristics to work, we need uc_stack to describe
212                   the interrupted stack (as on Solaris), and not the
213                   currently executing stack (as on Linux).  */
214                ucontext_t const *user_context = context;
215                char const *stack_min = user_context->uc_stack.ss_sp;
216                size_t stack_size = user_context->uc_stack.ss_size;
217                char const *faulting_address = info->si_addr;
218                size_t s = faulting_address - stack_min;
219                size_t page_size = sysconf (_SC_PAGESIZE);
220                if (find_stack_direction (0) < 0)
221                  s += page_size;
222                if (s < stack_size + page_size)
223                  _exit (0);
224              }
225
226            _exit (1);
227          }
228
229          static int
230          c_stack_action ()
231          {
232            stack_t st;
233            struct sigaction act;
234            int r;
235
236            st.ss_flags = 0;
237            /* Use the midpoint to avoid Irix sigaltstack bug.  */
238            st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ;
239            st.ss_size = SIGSTKSZ;
240            r = sigaltstack (&st, 0);
241            if (r != 0)
242              return r;
243
244            sigemptyset (&act.sa_mask);
245            act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
246            act.sa_sigaction = segv_handler;
247            return sigaction (SIGSEGV, &act, 0);
248          }
249          static volatile int *
250          recurse_1 (volatile int n, volatile int *p)
251          {
252            if (n >= 0)
253              *recurse_1 (n + 1, p) += n;
254            return p;
255          }
256          static int
257          recurse (volatile int n)
258          {
259            int sum = 0;
260            return *recurse_1 (n, &sum);
261          }
262          int
263          main ()
264          {
265            #if HAVE_SETRLIMIT && defined RLIMIT_STACK
266            /* Before starting the endless recursion, try to be friendly
267               to the user's machine.  On some Linux 2.2.x systems, there
268               is no stack limit for user processes at all.  We don't want
269               to kill such systems.  */
270            struct rlimit rl;
271            rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
272            setrlimit (RLIMIT_STACK, &rl);
273            #endif
274
275            return c_stack_action () || recurse (0);
276          }
277         ],
278         [ac_cv_sys_xsi_stack_overflow_heuristic=yes],
279         [ac_cv_sys_xsi_stack_overflow_heuristic=no],
280         [ac_cv_sys_xsi_stack_overflow_heuristic=cross-compiling])])
281
282    if test $ac_cv_sys_xsi_stack_overflow_heuristic = yes; then
283      AC_DEFINE(HAVE_XSI_STACK_OVERFLOW_HEURISTIC, 1,
284        [Define to 1 if extending the stack slightly past the limit causes
285         a SIGSEGV, and an alternate stack can be established with sigaltstack,
286         and the signal handler is passed a context that specifies the
287         run time stack.  This behavior is defined by POSIX 1003.1-2001
288         with the X/Open System Interface (XSI) option
289         and is a standardized way to implement a SEGV-based stack
290         overflow detection heuristic.])
291    fi
292   fi])
293
294
295 AC_DEFUN([gl_PREREQ_C_STACK],
296   [AC_REQUIRE([AC_SYS_XSI_STACK_OVERFLOW_HEURISTIC])
297    AC_REQUIRE([gl_LIBSIGSEGV])
298
299    # for STACK_DIRECTION
300    AC_REQUIRE([AC_FUNC_ALLOCA])
301
302    AC_CHECK_FUNCS_ONCE([sigaltstack])
303    AC_CHECK_DECLS([sigaltstack], , , [#include <signal.h>])
304
305    AC_CHECK_HEADERS_ONCE([unistd.h ucontext.h])
306
307    AC_CHECK_TYPES([stack_t], , , [#include <signal.h>])
308
309    dnl c-stack does not need -lsigsegv if the system has XSI heuristics.
310    if test "$gl_cv_lib_sigsegv" = yes \
311        && test $"ac_cv_sys_xsi_stack_overflow_heuristic" != yes ; then
312      AC_SUBST([LIBCSTACK], [$LIBSIGSEGV])
313      AC_SUBST([LTLIBCSTACK], [$LTLIBSIGSEGV])
314    fi
315 ])
316
317 AC_DEFUN([gl_C_STACK],
318 [
319   dnl Prerequisites of lib/c-stack.c.
320   gl_PREREQ_C_STACK
321 ])