1 /* -----------------------------------------------------------------------
2 closures.c - Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
3 Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
4 Copyright (c) 2011 Plausible Labs Cooperative, Inc.
5
6 Code to allocate and deallocate memory for closures.
7
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 ``Software''), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15
16 The above copyright notice and this permission notice shall be included
17 in all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 ----------------------------------------------------------------------- */
28
29 #if defined __linux__ && !defined _GNU_SOURCE
30 #define _GNU_SOURCE 1
31 #endif
32
33 #include <ffi.h>
34 #include <ffi_common.h>
35
36 #if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
37 # if __gnu_linux__
38 /* This macro indicates it may be forbidden to map anonymous memory
39 with both write and execute permission. Code compiled when this
40 option is defined will attempt to map such pages once, but if it
41 fails, it falls back to creating a temporary file in a writable and
42 executable filesystem and mapping pages from it into separate
43 locations in the virtual memory space, one location writable and
44 another executable. */
45 # define FFI_MMAP_EXEC_WRIT 1
46 # define HAVE_MNTENT 1
47 # endif
48 # if defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)
49 /* Windows systems may have Data Execution Protection (DEP) enabled,
50 which requires the use of VirtualMalloc/VirtualFree to alloc/free
51 executable memory. */
52 # define FFI_MMAP_EXEC_WRIT 1
53 # endif
54 #endif
55
56 #if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
57 # ifdef __linux__
58 /* When defined to 1 check for SELinux and if SELinux is active,
59 don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
60 might cause audit messages. */
61 # define FFI_MMAP_EXEC_SELINUX 1
62 # endif
63 #endif
64
65 #if FFI_CLOSURES
66
67 # if FFI_EXEC_TRAMPOLINE_TABLE
68
69 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
70
71 # elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
72
73 #define USE_LOCKS 1
74 #define USE_DL_PREFIX 1
75 #ifdef __GNUC__
76 #ifndef USE_BUILTIN_FFS
77 #define USE_BUILTIN_FFS 1
78 #endif
79 #endif
80
81 /* We need to use mmap, not sbrk. */
82 #define HAVE_MORECORE 0
83
84 /* We could, in theory, support mremap, but it wouldn't buy us anything. */
85 #define HAVE_MREMAP 0
86
87 /* We have no use for this, so save some code and data. */
88 #define NO_MALLINFO 1
89
90 /* We need all allocations to be in regular segments, otherwise we
91 lose track of the corresponding code address. */
92 #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
93
94 /* Don't allocate more than a page unless needed. */
95 #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
96
97 #if FFI_CLOSURE_TEST
98 /* Don't release single pages, to avoid a worst-case scenario of
99 continuously allocating and releasing single pages, but release
100 pairs of pages, which should do just as well given that allocations
101 are likely to be small. */
102 #define DEFAULT_TRIM_THRESHOLD ((size_t)malloc_getpagesize)
103 #endif
104
105 #include <sys/types.h>
106 #include <sys/stat.h>
107 #include <fcntl.h>
108 #include <errno.h>
109 #ifndef _MSC_VER
110 #include <unistd.h>
111 #endif
112 #include <string.h>
113 #include <stdio.h>
114 #if !defined(X86_WIN32) && !defined(X86_WIN64)
115 #ifdef HAVE_MNTENT
116 #include <mntent.h>
117 #endif /* HAVE_MNTENT */
118 #include <sys/param.h>
119 #include <pthread.h>
120
121 /* We don't want sys/mman.h to be included after we redefine mmap and
122 dlmunmap. */
123 #include <sys/mman.h>
124 #define LACKS_SYS_MMAN_H 1
125
126 #if FFI_MMAP_EXEC_SELINUX
127 #include <sys/statfs.h>
128 #include <stdlib.h>
129
130 static int selinux_enabled = -1;
131
132 static int
selinux_enabled_check(void)133 selinux_enabled_check (void)
134 {
135 struct statfs sfs;
136 FILE *f;
137 char *buf = NULL;
138 size_t len = 0;
139
140 if (statfs ("/selinux", &sfs) >= 0
141 && (unsigned int) sfs.f_type == 0xf97cff8cU)
142 return 1;
143 f = fopen ("/proc/mounts", "r");
144 if (f == NULL)
145 return 0;
146 while (getline (&buf, &len, f) >= 0)
147 {
148 char *p = strchr (buf, ' ');
149 if (p == NULL)
150 break;
151 p = strchr (p + 1, ' ');
152 if (p == NULL)
153 break;
154 if (strncmp (p + 1, "selinuxfs ", 10) == 0)
155 {
156 free (buf);
157 fclose (f);
158 return 1;
159 }
160 }
161 free (buf);
162 fclose (f);
163 return 0;
164 }
165
166 #define is_selinux_enabled() (selinux_enabled >= 0 ? selinux_enabled \
167 : (selinux_enabled = selinux_enabled_check ()))
168
169 #else
170
171 #define is_selinux_enabled() 0
172
173 #endif /* !FFI_MMAP_EXEC_SELINUX */
174
175 /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
176 #ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
177 #include <stdlib.h>
178
179 static int emutramp_enabled = -1;
180
181 static int
emutramp_enabled_check(void)182 emutramp_enabled_check (void)
183 {
184 if (getenv ("FFI_DISABLE_EMUTRAMP") == NULL)
185 return 1;
186 else
187 return 0;
188 }
189
190 #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
191 : (emutramp_enabled = emutramp_enabled_check ()))
192 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
193
194 #elif defined (__CYGWIN__) || defined(__INTERIX)
195
196 #include <sys/mman.h>
197
198 /* Cygwin is Linux-like, but not quite that Linux-like. */
199 #define is_selinux_enabled() 0
200
201 #endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
202
203 #ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
204 #define is_emutramp_enabled() 0
205 #endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
206
207 /* Declare all functions defined in dlmalloc.c as static. */
208 static void *dlmalloc(size_t);
209 static void dlfree(void*);
210 static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
211 static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
212 static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
213 static void *dlvalloc(size_t) MAYBE_UNUSED;
214 static int dlmallopt(int, int) MAYBE_UNUSED;
215 static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
216 static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
217 static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
218 static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
219 static void *dlpvalloc(size_t) MAYBE_UNUSED;
220 static int dlmalloc_trim(size_t) MAYBE_UNUSED;
221 static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
222 static void dlmalloc_stats(void) MAYBE_UNUSED;
223
224 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
225 /* Use these for mmap and munmap within dlmalloc.c. */
226 static void *dlmmap(void *, size_t, int, int, int, off_t);
227 static int dlmunmap(void *, size_t);
228 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
229
230 #define mmap dlmmap
231 #define munmap dlmunmap
232
233 #include "dlmalloc.c"
234
235 #undef mmap
236 #undef munmap
237
238 #if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
239
240 /* A mutex used to synchronize access to *exec* variables in this file. */
241 static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
242
243 /* A file descriptor of a temporary file from which we'll map
244 executable pages. */
245 static int execfd = -1;
246
247 /* The amount of space already allocated from the temporary file. */
248 static size_t execsize = 0;
249
250 /* Open a temporary file name, and immediately unlink it. */
251 static int
open_temp_exec_file_name(char * name)252 open_temp_exec_file_name (char *name)
253 {
254 int fd = mkstemp (name);
255
256 if (fd != -1)
257 unlink (name);
258
259 return fd;
260 }
261
262 /* Open a temporary file in the named directory. */
263 static int
open_temp_exec_file_dir(const char * dir)264 open_temp_exec_file_dir (const char *dir)
265 {
266 static const char suffix[] = "/ffiXXXXXX";
267 int lendir = strlen (dir);
268 char *tempname = __builtin_alloca (lendir + sizeof (suffix));
269
270 if (!tempname)
271 return -1;
272
273 memcpy (tempname, dir, lendir);
274 memcpy (tempname + lendir, suffix, sizeof (suffix));
275
276 return open_temp_exec_file_name (tempname);
277 }
278
279 /* Open a temporary file in the directory in the named environment
280 variable. */
281 static int
open_temp_exec_file_env(const char * envvar)282 open_temp_exec_file_env (const char *envvar)
283 {
284 const char *value = getenv (envvar);
285
286 if (!value)
287 return -1;
288
289 return open_temp_exec_file_dir (value);
290 }
291
292 #ifdef HAVE_MNTENT
293 /* Open a temporary file in an executable and writable mount point
294 listed in the mounts file. Subsequent calls with the same mounts
295 keep searching for mount points in the same file. Providing NULL
296 as the mounts file closes the file. */
297 static int
open_temp_exec_file_mnt(const char * mounts)298 open_temp_exec_file_mnt (const char *mounts)
299 {
300 static const char *last_mounts;
301 static FILE *last_mntent;
302
303 if (mounts != last_mounts)
304 {
305 if (last_mntent)
306 endmntent (last_mntent);
307
308 last_mounts = mounts;
309
310 if (mounts)
311 last_mntent = setmntent (mounts, "r");
312 else
313 last_mntent = NULL;
314 }
315
316 if (!last_mntent)
317 return -1;
318
319 for (;;)
320 {
321 int fd;
322 struct mntent mnt;
323 char buf[MAXPATHLEN * 3];
324
325 if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
326 return -1;
327
328 if (hasmntopt (&mnt, "ro")
329 || hasmntopt (&mnt, "noexec")
330 || access (mnt.mnt_dir, W_OK))
331 continue;
332
333 fd = open_temp_exec_file_dir (mnt.mnt_dir);
334
335 if (fd != -1)
336 return fd;
337 }
338 }
339 #endif /* HAVE_MNTENT */
340
341 /* Instructions to look for a location to hold a temporary file that
342 can be mapped in for execution. */
343 static struct
344 {
345 int (*func)(const char *);
346 const char *arg;
347 int repeat;
348 } open_temp_exec_file_opts[] = {
349 { open_temp_exec_file_env, "TMPDIR", 0 },
350 { open_temp_exec_file_dir, "/tmp", 0 },
351 { open_temp_exec_file_dir, "/var/tmp", 0 },
352 { open_temp_exec_file_dir, "/dev/shm", 0 },
353 { open_temp_exec_file_env, "HOME", 0 },
354 #ifdef HAVE_MNTENT
355 { open_temp_exec_file_mnt, "/etc/mtab", 1 },
356 { open_temp_exec_file_mnt, "/proc/mounts", 1 },
357 #endif /* HAVE_MNTENT */
358 };
359
360 /* Current index into open_temp_exec_file_opts. */
361 static int open_temp_exec_file_opts_idx = 0;
362
363 /* Reset a current multi-call func, then advances to the next entry.
364 If we're at the last, go back to the first and return nonzero,
365 otherwise return zero. */
366 static int
open_temp_exec_file_opts_next(void)367 open_temp_exec_file_opts_next (void)
368 {
369 if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
370 open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
371
372 open_temp_exec_file_opts_idx++;
373 if (open_temp_exec_file_opts_idx
374 == (sizeof (open_temp_exec_file_opts)
375 / sizeof (*open_temp_exec_file_opts)))
376 {
377 open_temp_exec_file_opts_idx = 0;
378 return 1;
379 }
380
381 return 0;
382 }
383
384 /* Return a file descriptor of a temporary zero-sized file in a
385 writable and exexutable filesystem. */
386 static int
open_temp_exec_file(void)387 open_temp_exec_file (void)
388 {
389 int fd;
390
391 do
392 {
393 fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
394 (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
395
396 if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
397 || fd == -1)
398 {
399 if (open_temp_exec_file_opts_next ())
400 break;
401 }
402 }
403 while (fd == -1);
404
405 return fd;
406 }
407
408 /* Map in a chunk of memory from the temporary exec file into separate
409 locations in the virtual memory address space, one writable and one
410 executable. Returns the address of the writable portion, after
411 storing an offset to the corresponding executable portion at the
412 last word of the requested chunk. */
413 static void *
dlmmap_locked(void * start,size_t length,int prot,int flags,off_t offset)414 dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
415 {
416 void *ptr;
417
418 if (execfd == -1)
419 {
420 open_temp_exec_file_opts_idx = 0;
421 retry_open:
422 execfd = open_temp_exec_file ();
423 if (execfd == -1)
424 return MFAIL;
425 }
426
427 offset = execsize;
428
429 if (ftruncate (execfd, offset + length))
430 return MFAIL;
431
432 flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
433 flags |= MAP_SHARED;
434
435 ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
436 flags, execfd, offset);
437 if (ptr == MFAIL)
438 {
439 if (!offset)
440 {
441 close (execfd);
442 goto retry_open;
443 }
444 ftruncate (execfd, offset);
445 return MFAIL;
446 }
447 else if (!offset
448 && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
449 open_temp_exec_file_opts_next ();
450
451 start = mmap (start, length, prot, flags, execfd, offset);
452
453 if (start == MFAIL)
454 {
455 munmap (ptr, length);
456 ftruncate (execfd, offset);
457 return start;
458 }
459
460 mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
461
462 execsize += length;
463
464 return start;
465 }
466
467 /* Map in a writable and executable chunk of memory if possible.
468 Failing that, fall back to dlmmap_locked. */
469 static void *
dlmmap(void * start,size_t length,int prot,int flags,int fd,off_t offset)470 dlmmap (void *start, size_t length, int prot,
471 int flags, int fd, off_t offset)
472 {
473 void *ptr;
474
475 assert (start == NULL && length % malloc_getpagesize == 0
476 && prot == (PROT_READ | PROT_WRITE)
477 && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
478 && fd == -1 && offset == 0);
479
480 #if FFI_CLOSURE_TEST
481 printf ("mapping in %zi\n", length);
482 #endif
483
484 if (execfd == -1 && is_emutramp_enabled ())
485 {
486 ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
487 return ptr;
488 }
489
490 if (execfd == -1 && !is_selinux_enabled ())
491 {
492 ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
493
494 if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
495 /* Cool, no need to mess with separate segments. */
496 return ptr;
497
498 /* If MREMAP_DUP is ever introduced and implemented, try mmap
499 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
500 MREMAP_DUP and prot at this point. */
501 }
502
503 if (execsize == 0 || execfd == -1)
504 {
505 pthread_mutex_lock (&open_temp_exec_file_mutex);
506 ptr = dlmmap_locked (start, length, prot, flags, offset);
507 pthread_mutex_unlock (&open_temp_exec_file_mutex);
508
509 return ptr;
510 }
511
512 return dlmmap_locked (start, length, prot, flags, offset);
513 }
514
515 /* Release memory at the given address, as well as the corresponding
516 executable page if it's separate. */
517 static int
dlmunmap(void * start,size_t length)518 dlmunmap (void *start, size_t length)
519 {
520 /* We don't bother decreasing execsize or truncating the file, since
521 we can't quite tell whether we're unmapping the end of the file.
522 We don't expect frequent deallocation anyway. If we did, we
523 could locate pages in the file by writing to the pages being
524 deallocated and checking that the file contents change.
525 Yuck. */
526 msegmentptr seg = segment_holding (gm, start);
527 void *code;
528
529 #if FFI_CLOSURE_TEST
530 printf ("unmapping %zi\n", length);
531 #endif
532
533 if (seg && (code = add_segment_exec_offset (start, seg)) != start)
534 {
535 int ret = munmap (code, length);
536 if (ret)
537 return ret;
538 }
539
540 return munmap (start, length);
541 }
542
543 #if FFI_CLOSURE_FREE_CODE
544 /* Return segment holding given code address. */
545 static msegmentptr
segment_holding_code(mstate m,char * addr)546 segment_holding_code (mstate m, char* addr)
547 {
548 msegmentptr sp = &m->seg;
549 for (;;) {
550 if (addr >= add_segment_exec_offset (sp->base, sp)
551 && addr < add_segment_exec_offset (sp->base, sp) + sp->size)
552 return sp;
553 if ((sp = sp->next) == 0)
554 return 0;
555 }
556 }
557 #endif
558
559 #endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
560
561 /* Allocate a chunk of memory with the given size. Returns a pointer
562 to the writable address, and sets *CODE to the executable
563 corresponding virtual address. */
564 void *
ffi_closure_alloc(size_t size,void ** code)565 ffi_closure_alloc (size_t size, void **code)
566 {
567 void *ptr;
568
569 if (!code)
570 return NULL;
571
572 ptr = dlmalloc (size);
573
574 if (ptr)
575 {
576 msegmentptr seg = segment_holding (gm, ptr);
577
578 *code = add_segment_exec_offset (ptr, seg);
579 }
580
581 return ptr;
582 }
583
584 /* Release a chunk of memory allocated with ffi_closure_alloc. If
585 FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
586 writable or the executable address given. Otherwise, only the
587 writable address can be provided here. */
588 void
ffi_closure_free(void * ptr)589 ffi_closure_free (void *ptr)
590 {
591 #if FFI_CLOSURE_FREE_CODE
592 msegmentptr seg = segment_holding_code (gm, ptr);
593
594 if (seg)
595 ptr = sub_segment_exec_offset (ptr, seg);
596 #endif
597
598 dlfree (ptr);
599 }
600
601
602 #if FFI_CLOSURE_TEST
603 /* Do some internal sanity testing to make sure allocation and
604 deallocation of pages are working as intended. */
main()605 int main ()
606 {
607 void *p[3];
608 #define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
609 #define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
610 GET (0, malloc_getpagesize / 2);
611 GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
612 PUT (1);
613 GET (1, 2 * malloc_getpagesize);
614 GET (2, malloc_getpagesize / 2);
615 PUT (1);
616 PUT (0);
617 PUT (2);
618 return 0;
619 }
620 #endif /* FFI_CLOSURE_TEST */
621 # else /* ! FFI_MMAP_EXEC_WRIT */
622
623 /* On many systems, memory returned by malloc is writable and
624 executable, so just use it. */
625
626 #include <stdlib.h>
627
628 void *
ffi_closure_alloc(size_t size,void ** code)629 ffi_closure_alloc (size_t size, void **code)
630 {
631 if (!code)
632 return NULL;
633
634 return *code = malloc (size);
635 }
636
637 void
ffi_closure_free(void * ptr)638 ffi_closure_free (void *ptr)
639 {
640 free (ptr);
641 }
642
643 # endif /* ! FFI_MMAP_EXEC_WRIT */
644 #endif /* FFI_CLOSURES */
645