File: codeexec.m4

package info (click to toggle)
ffcall 2.1-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 5,808 kB
  • sloc: asm: 43,409; ansic: 23,224; sh: 5,383; makefile: 1,517; cpp: 2
file content (362 lines) | stat: -rw-r--r-- 16,259 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
dnl -*- Autoconf -*-
dnl Copyright (C) 1993-2017 Free Software Foundation, Inc.
dnl This file is free software, distributed under the terms of the GNU
dnl General Public License as published by the Free Software Foundation;
dnl either version 2 of the License, or (at your option) any later version.
dnl As a special exception to the GNU General Public License, this file
dnl may be distributed as part of a program that contains a configuration
dnl script generated by Autoconf, under the same distribution terms as
dnl the rest of that program.

dnl From Bruno Haible, Marcus Daniels, Sam Steingold.

AC_PREREQ([2.13])

AC_DEFUN([CE_DOC],[whether code in malloc()ed memory is executable])
AC_DEFUN([FFCALL_CODEEXEC],
[
  AC_REQUIRE([AC_CANONICAL_HOST])
  AC_REQUIRE([gl_HOST_CPU_C_ABI])
  AC_CACHE_CHECK([CE_DOC], [ffcall_cv_codeexec],
    [dnl The test below does not work on platforms with the following ABIs:
     dnl - hppa, because function pointers are actually pointers into(!)
     dnl   a two-pointer struct.
     dnl - hppa64, because function pointers are actually pointers to a
     dnl   four-pointer struct.
     dnl - powerpc on AIX, powerpc64, because function pointers are actually
     dnl   pointers to a three-pointer struct.
     dnl - ia64, because function pointers are actually pointers to a
     dnl   two-pointer struct.
     case "$HOST_CPU_C_ABI--$host_os" in
       hppa--* | hppa64--* | powerpc--aix* | powerpc64--* | ia64--*)
         dnl On these platforms, it's irrelevant whether malloc'ed memory is
         dnl executable, because the trampolines are built without executable
         dnl code.
         ffcall_cv_codeexec="irrelevant"
         ;;
       arm64--freebsd*)
         dnl On this platform, malloc()ed memory is not executable, and the
         dnl test program loops endlessly.
         ffcall_cv_codeexec=no
         ;;
       *)
         AC_TRY_RUN(GL_NOCRASH
           [#include <sys/types.h>
            /* declare malloc() */
            #include <stdlib.h>
            int fun () { return 31415926; }
            int main ()
            { nocrash_init();
             {long size = (char*)&main - (char*)&fun;
              char* funcopy = (char*) malloc(size);
              int i;
              for (i = 0; i < size; i++) { funcopy[i] = ((char*)&fun)[i]; }
              return !((*(int(*)())funcopy)() == 31415926);
            }}
           ],
           [ffcall_cv_codeexec=yes],
           [ffcall_cv_codeexec=no],
           [dnl When cross-compiling, assume the known behaviour.
            dnl If we don't know, assume the worst.
            case "$host_os" in
              cygwin*)
                case "$HOST_CPU_C_ABI" in
                  i386)
                    ffcall_cv_codeexec="guessing yes" ;;
                  x86_64)
                    ffcall_cv_codeexec="guessing no" ;;
                  *)
                    ffcall_cv_codeexec="guessing no" ;;
                esac
                ;;
              darwin*)
                case "$HOST_CPU_C_ABI" in
                  i386 | powerpc)
                    ffcall_cv_codeexec="guessing yes" ;;
                  x86_64)
                    ffcall_cv_codeexec="guessing no" ;;
                  *)
                    ffcall_cv_codeexec="guessing no" ;;
                esac
                ;;
              irix*)
                ffcall_cv_codeexec="guessing no" ;;
              linux*)
                case "$HOST_CPU_C_ABI" in
                  alpha | ia64)
                    ffcall_cv_codeexec="guessing yes" ;;
                  arm | armhf | arm64 | i386 | mips* | s390 | s390x | sparc | sparc64 | x86_64*)
                    ffcall_cv_codeexec="guessing no" ;;
                  *)
                    ffcall_cv_codeexec="guessing no" ;;
                esac
                ;;
              solaris*)
                case "$HOST_CPU_C_ABI" in
                  i386 | sparc | sparc64)
                    ffcall_cv_codeexec="guessing yes" ;;
                  x86_64)
                    ffcall_cv_codeexec="guessing no" ;;
                  *)
                    ffcall_cv_codeexec="guessing no" ;;
                esac
                ;;
              *)
                ffcall_cv_codeexec="guessing no" ;;
            esac
           ])
         ;;
     esac
    ])
  case "$ffcall_cv_codeexec" in
    *yes | irrelevant) AC_DEFINE([CODE_EXECUTABLE], [], CE_DOC) ;;
    *no)  ;;
  esac
])

dnl Test how to use the mprotect function to make memory executable.
dnl Test against the mprotect limitations found in PaX enabled Linux kernels
dnl and HardenedBSD.
AC_DEFUN([FFCALL_CODEEXEC_PAX],
[
  AC_REQUIRE([FFCALL_MMAP])
  AC_REQUIRE([FFCALL_MPROTECT])
  AC_REQUIRE([FFCALL_CODEEXEC])
  AC_REQUIRE([AC_CANONICAL_HOST])
  case "$ffcall_cv_codeexec" in
    *yes | irrelevant) ;;
    *)
      case "$ac_cv_func_mprotect--$cl_cv_func_mprotect_works" in
        yes--*yes)
          AC_CACHE_CHECK([whether mprotect can make malloc()ed memory executable],
            [ffcall_cv_malloc_mprotect_can_exec],
            [dnl On RHEL 6 / CentOS 6 with SELinux enabled, the result of
             dnl this test depends on SELinux flags that can be changed at
             dnl runtime: By default, the result is 'no'. However, when the flag
             dnl allow_execheap is turned on, the result is 'yes'. But the flag
             dnl can be turned off again at any moment.
             if test "$cross_compiling" != yes -a -d /etc/selinux; then
               ffcall_cv_malloc_mprotect_can_exec='determined by SELinux at runtime'
             else
               AC_TRY_RUN(
                 [#include <errno.h>
                  #include <stdlib.h>
                  #ifdef HAVE_UNISTD_H
                   #include <unistd.h>
                  #endif
                  #include <fcntl.h>
                  /* declare getpagesize() and mprotect() */
                  #include <sys/mman.h>
                  #ifndef HAVE_GETPAGESIZE
                   #include <sys/param.h>
                   #define getpagesize() PAGESIZE
                  #else
                   ]AC_LANG_EXTERN[
                   RETGETPAGESIZETYPE getpagesize (void);
                  #endif
                  int
                  main ()
                  {
                    unsigned int pagesize = getpagesize ();
                    char *p = (char *) malloc (50);
                    int ret;
                    if (p == (char*) -1)
                      /* malloc is not working as expected. */
                      return 1;
                    p[5] = 0x77;
                    ret = mprotect (p - ((unsigned int) p & (pagesize - 1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
                    if (ret < 0
                        && (errno == EACCES || errno == ENOMEM
                            #ifdef ENOTSUP
                            || errno == ENOTSUP
                            #endif
                       )   )
                      /* mprotect is forbidden to make malloc()ed pages executable that were writable earlier. */
                      return 2;
                    return 0;
                  }
                 ],
                 [ffcall_cv_malloc_mprotect_can_exec=yes],
                 [dnl When cross-compiling, assume SELinux on Linux.
                  dnl If we don't know, assume the worst.
                  case "$host_os" in
                    linux*)
                      ffcall_cv_malloc_mprotect_can_exec='determined by SELinux at runtime' ;;
                    aix* | cygwin* | darwin* | irix* | solaris*)
                      ffcall_cv_malloc_mprotect_can_exec="guessing yes" ;;
                    *)
                      ffcall_cv_malloc_mprotect_can_exec="guessing no" ;;
                  esac
                 ])
             fi
            ])
          case "$ffcall_cv_malloc_mprotect_can_exec" in
            *yes)      MPROTECT_AFTER_MALLOC_CAN_EXEC=1 ;;
            *no)       MPROTECT_AFTER_MALLOC_CAN_EXEC=0 ;;
            *runtime*) MPROTECT_AFTER_MALLOC_CAN_EXEC='-1' ;;
          esac
          AC_DEFINE_UNQUOTED([HAVE_MPROTECT_AFTER_MALLOC_CAN_EXEC], [$MPROTECT_AFTER_MALLOC_CAN_EXEC],
            [have an mprotect() function that can make malloc()ed memory pages executable])
          case "$ffcall_cv_malloc_mprotect_can_exec" in
            *yes) ;;
            *)
              AC_CACHE_CHECK([whether mprotect can make mmap()ed memory executable],
                [ffcall_cv_mmap_mprotect_can_exec],
                [dnl On RHEL 6 / CentOS 6 with SELinux enabled, the result of
                 dnl this test depends on SELinux flags that can be changed at
                 dnl runtime: By default, the result is 'yes'. However, when the flags
                 dnl allow_execmem and allow_execstack are turned off, the result is
                 dnl 'no'.
                 if test "$cross_compiling" != yes -a -d /etc/selinux; then
                   ffcall_cv_mmap_mprotect_can_exec='determined by SELinux at runtime'
                 else
                   AC_TRY_RUN(
                     [#include <errno.h>
                      #include <stdlib.h>
                      #ifdef HAVE_UNISTD_H
                       #include <unistd.h>
                      #endif
                      #include <fcntl.h>
                      /* declare getpagesize() and mprotect() */
                      #include <sys/mman.h>
                      #ifndef HAVE_GETPAGESIZE
                       #include <sys/param.h>
                       #define getpagesize() PAGESIZE
                      #else
                       ]AC_LANG_EXTERN[
                       RETGETPAGESIZETYPE getpagesize (void);
                      #endif
                      #ifndef MAP_FILE
                       #define MAP_FILE 0
                      #endif
                      #ifndef MAP_VARIABLE
                       #define MAP_VARIABLE 0
                      #endif
                      int
                      main ()
                      {
                        unsigned int pagesize = getpagesize ();
                        char *p;
                        int ret;
                      #if defined HAVE_MMAP_ANON
                        p = (char *) mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_VARIABLE, -1, 0);
                      #elif defined HAVE_MMAP_ANONYMOUS
                        p = (char *) mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_VARIABLE, -1, 0);
                      #elif defined HAVE_MMAP_DEVZERO
                        int zero_fd = open("/dev/zero", O_RDONLY, 0666);
                        if (zero_fd < 0)
                          return 1;
                        p = (char *) mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE | MAP_VARIABLE, zero_fd, 0);
                      #else
                        ??
                      #endif
                        if (p == (char*) -1)
                          /* mmap is not working as expected. */
                          return 1;
                        p[5] = 0x77;
                        ret = mprotect (p, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
                        if (ret < 0
                            && (errno == EACCES || errno == ENOMEM
                                #ifdef ENOTSUP
                                || errno == ENOTSUP
                                #endif
                           )   )
                          /* mprotect is forbidden to make mmap()ed pages executable that were writable earlier. */
                          return 2;
                        return 0;
                      }
                     ],
                     [ffcall_cv_mmap_mprotect_can_exec=yes],
                     [dnl When cross-compiling, assume SELinux on Linux.
                      dnl If we don't know, assume the worst.
                      case "$host_os" in
                        linux*) ffcall_cv_mmap_mprotect_can_exec='determined by SELinux at runtime' ;;
                        *)      ffcall_cv_mmap_mprotect_can_exec="guessing no" ;;
                      esac
                     ])
                 fi
                ])
              case "$ffcall_cv_mmap_mprotect_can_exec" in
                *yes)      MPROTECT_AFTER_MMAP_CAN_EXEC=1 ;;
                *no)       MPROTECT_AFTER_MMAP_CAN_EXEC=0 ;;
                *runtime*) MPROTECT_AFTER_MMAP_CAN_EXEC='-1' ;;
              esac
              AC_DEFINE_UNQUOTED([HAVE_MPROTECT_AFTER_MMAP_CAN_EXEC], [$MPROTECT_AFTER_MMAP_CAN_EXEC],
                [have an mprotect() function that can make mmap()ed memory pages executable])
              case "$ffcall_cv_mmap_mprotect_can_exec" in
                *yes) ;;
                *)
                  AC_CACHE_CHECK([whether a shared mmap can make memory pages executable],
                    [ffcall_cv_mmap_shared_can_exec],
                    [filename="/tmp/trampdata$$.data"
                     AC_TRY_RUN(
                       [#include <fcntl.h>
                        #include <stdlib.h>
                        #ifdef HAVE_UNISTD_H
                         #include <unistd.h>
                        #endif
                        /* declare getpagesize() and mmap() */
                        #include <sys/mman.h>
                        #ifndef HAVE_GETPAGESIZE
                         #include <sys/param.h>
                         #define getpagesize() PAGESIZE
                        #else
                         ]AC_LANG_EXTERN[
                         RETGETPAGESIZETYPE getpagesize (void);
                        #endif
                        #ifndef MAP_FILE
                         #define MAP_FILE 0
                        #endif
                        #ifndef MAP_VARIABLE
                         #define MAP_VARIABLE 0
                        #endif
                        int
                        main ()
                        {
                          unsigned int pagesize = getpagesize ();
                          int fd;
                          char *pw;
                          char *px;
                          fd = open ("$filename", O_CREAT | O_RDWR | O_TRUNC, 0700);
                          if (fd < 0)
                            return 1;
                          if (ftruncate (fd, pagesize) < 0)
                            return 2;
                          pw = (char *) mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE | MAP_VARIABLE, fd, 0);
                          if (pw == (char*) -1)
                            return 3;
                          pw[5] = 0xc3;
                          px = (char *) mmap (NULL, pagesize, PROT_READ | PROT_EXEC, MAP_SHARED | MAP_FILE | MAP_VARIABLE, fd, 0);
                          if (px == (char*) -1)
                            return 4;
                          if ((char)px[5] != (char)0xc3)
                            return 5;
                          /* On i386 and x86_64 this is a 'ret' instruction that we can invoke. */
                        #if (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_) || (defined __x86_64__ || defined __amd64__)
                          ((void (*) (void)) (px + 5)) ();
                        #endif
                          return 0;
                        }
                       ],
                       [ffcall_cv_mmap_shared_can_exec=yes],
                       [dnl When cross-compiling, assume yes, since this is the result
                        dnl on all the platforms where we have tested it.
                        ffcall_cv_mmap_shared_can_exec="guessing yes"
                       ])
                     rm -f "$filename"
                    ])
                  case "$ffcall_cv_mmap_shared_can_exec" in
                    *yes)
                      AC_DEFINE([HAVE_MMAP_SHARED_CAN_EXEC], [],
                        [have an mmap() function that, with MAP_SHARED, can make memory pages executable])
                      ;;
                  esac
                  ;;
              esac
              ;;
          esac
          ;;
      esac
      ;;
  esac
])