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