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__ && !defined(__ANDROID__)
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
130static int selinux_enabled = -1;
131
132static int
133selinux_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
179static int emutramp_enabled = -1;
180
181static int
182emutramp_enabled_check (void)
183{
184  char *buf = NULL;
185  size_t len = 0;
186  FILE *f;
187  int ret;
188  f = fopen ("/proc/self/status", "r");
189  if (f == NULL)
190    return 0;
191  ret = 0;
192
193  while (getline (&buf, &len, f) != -1)
194    if (!strncmp (buf, "PaX:", 4))
195      {
196        char emutramp;
197        if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
198          ret = (emutramp == 'E');
199        break;
200      }
201  free (buf);
202  fclose (f);
203  return ret;
204}
205
206#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
207                               : (emutramp_enabled = emutramp_enabled_check ()))
208#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
209
210#elif defined (__CYGWIN__) || defined(__INTERIX)
211
212#include <sys/mman.h>
213
214/* Cygwin is Linux-like, but not quite that Linux-like.  */
215#define is_selinux_enabled() 0
216
217#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
218
219#ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
220#define is_emutramp_enabled() 0
221#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
222
223/* Declare all functions defined in dlmalloc.c as static.  */
224static void *dlmalloc(size_t);
225static void dlfree(void*);
226static void *dlcalloc(size_t, size_t) MAYBE_UNUSED;
227static void *dlrealloc(void *, size_t) MAYBE_UNUSED;
228static void *dlmemalign(size_t, size_t) MAYBE_UNUSED;
229static void *dlvalloc(size_t) MAYBE_UNUSED;
230static int dlmallopt(int, int) MAYBE_UNUSED;
231static size_t dlmalloc_footprint(void) MAYBE_UNUSED;
232static size_t dlmalloc_max_footprint(void) MAYBE_UNUSED;
233static void** dlindependent_calloc(size_t, size_t, void**) MAYBE_UNUSED;
234static void** dlindependent_comalloc(size_t, size_t*, void**) MAYBE_UNUSED;
235static void *dlpvalloc(size_t) MAYBE_UNUSED;
236static int dlmalloc_trim(size_t) MAYBE_UNUSED;
237static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED;
238static void dlmalloc_stats(void) MAYBE_UNUSED;
239
240#if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
241/* Use these for mmap and munmap within dlmalloc.c.  */
242static void *dlmmap(void *, size_t, int, int, int, off_t);
243static int dlmunmap(void *, size_t);
244#endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
245
246#define mmap dlmmap
247#define munmap dlmunmap
248
249#include "dlmalloc.c"
250
251#undef mmap
252#undef munmap
253
254#if !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX)
255
256/* A mutex used to synchronize access to *exec* variables in this file.  */
257static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER;
258
259/* A file descriptor of a temporary file from which we'll map
260   executable pages.  */
261static int execfd = -1;
262
263/* The amount of space already allocated from the temporary file.  */
264static size_t execsize = 0;
265
266/* Open a temporary file name, and immediately unlink it.  */
267static int
268open_temp_exec_file_name (char *name)
269{
270  int fd = mkstemp (name);
271
272  if (fd != -1)
273    unlink (name);
274
275  return fd;
276}
277
278/* Open a temporary file in the named directory.  */
279static int
280open_temp_exec_file_dir (const char *dir)
281{
282  static const char suffix[] = "/ffiXXXXXX";
283  size_t lendir = strlen (dir);
284  char *tempname = __builtin_alloca (lendir + sizeof (suffix));
285
286  if (!tempname)
287    return -1;
288
289  memcpy (tempname, dir, lendir);
290  memcpy (tempname + lendir, suffix, sizeof (suffix));
291
292  return open_temp_exec_file_name (tempname);
293}
294
295/* Open a temporary file in the directory in the named environment
296   variable.  */
297static int
298open_temp_exec_file_env (const char *envvar)
299{
300  const char *value = getenv (envvar);
301
302  if (!value)
303    return -1;
304
305  return open_temp_exec_file_dir (value);
306}
307
308#ifdef HAVE_MNTENT
309/* Open a temporary file in an executable and writable mount point
310   listed in the mounts file.  Subsequent calls with the same mounts
311   keep searching for mount points in the same file.  Providing NULL
312   as the mounts file closes the file.  */
313static int
314open_temp_exec_file_mnt (const char *mounts)
315{
316  static const char *last_mounts;
317  static FILE *last_mntent;
318
319  if (mounts != last_mounts)
320    {
321      if (last_mntent)
322	endmntent (last_mntent);
323
324      last_mounts = mounts;
325
326      if (mounts)
327	last_mntent = setmntent (mounts, "r");
328      else
329	last_mntent = NULL;
330    }
331
332  if (!last_mntent)
333    return -1;
334
335  for (;;)
336    {
337      int fd;
338      struct mntent mnt;
339      char buf[MAXPATHLEN * 3];
340
341      if (getmntent_r (last_mntent, &mnt, buf, sizeof (buf)) == NULL)
342	return -1;
343
344      if (hasmntopt (&mnt, "ro")
345	  || hasmntopt (&mnt, "noexec")
346	  || access (mnt.mnt_dir, W_OK))
347	continue;
348
349      fd = open_temp_exec_file_dir (mnt.mnt_dir);
350
351      if (fd != -1)
352	return fd;
353    }
354}
355#endif /* HAVE_MNTENT */
356
357/* Instructions to look for a location to hold a temporary file that
358   can be mapped in for execution.  */
359static struct
360{
361  int (*func)(const char *);
362  const char *arg;
363  int repeat;
364} open_temp_exec_file_opts[] = {
365  { open_temp_exec_file_env, "TMPDIR", 0 },
366  { open_temp_exec_file_dir, "/tmp", 0 },
367  { open_temp_exec_file_dir, "/var/tmp", 0 },
368  { open_temp_exec_file_dir, "/dev/shm", 0 },
369  { open_temp_exec_file_env, "HOME", 0 },
370#ifdef HAVE_MNTENT
371  { open_temp_exec_file_mnt, "/etc/mtab", 1 },
372  { open_temp_exec_file_mnt, "/proc/mounts", 1 },
373#endif /* HAVE_MNTENT */
374};
375
376/* Current index into open_temp_exec_file_opts.  */
377static int open_temp_exec_file_opts_idx = 0;
378
379/* Reset a current multi-call func, then advances to the next entry.
380   If we're at the last, go back to the first and return nonzero,
381   otherwise return zero.  */
382static int
383open_temp_exec_file_opts_next (void)
384{
385  if (open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
386    open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func (NULL);
387
388  open_temp_exec_file_opts_idx++;
389  if (open_temp_exec_file_opts_idx
390      == (sizeof (open_temp_exec_file_opts)
391	  / sizeof (*open_temp_exec_file_opts)))
392    {
393      open_temp_exec_file_opts_idx = 0;
394      return 1;
395    }
396
397  return 0;
398}
399
400/* Return a file descriptor of a temporary zero-sized file in a
401   writable and executable filesystem.  */
402static int
403open_temp_exec_file (void)
404{
405  int fd;
406
407  do
408    {
409      fd = open_temp_exec_file_opts[open_temp_exec_file_opts_idx].func
410	(open_temp_exec_file_opts[open_temp_exec_file_opts_idx].arg);
411
412      if (!open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat
413	  || fd == -1)
414	{
415	  if (open_temp_exec_file_opts_next ())
416	    break;
417	}
418    }
419  while (fd == -1);
420
421  return fd;
422}
423
424/* Map in a chunk of memory from the temporary exec file into separate
425   locations in the virtual memory address space, one writable and one
426   executable.  Returns the address of the writable portion, after
427   storing an offset to the corresponding executable portion at the
428   last word of the requested chunk.  */
429static void *
430dlmmap_locked (void *start, size_t length, int prot, int flags, off_t offset)
431{
432  void *ptr;
433
434  if (execfd == -1)
435    {
436      open_temp_exec_file_opts_idx = 0;
437    retry_open:
438      execfd = open_temp_exec_file ();
439      if (execfd == -1)
440	return MFAIL;
441    }
442
443  offset = execsize;
444
445  if (ftruncate (execfd, offset + length))
446    return MFAIL;
447
448  flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
449  flags |= MAP_SHARED;
450
451  ptr = mmap (NULL, length, (prot & ~PROT_WRITE) | PROT_EXEC,
452	      flags, execfd, offset);
453  if (ptr == MFAIL)
454    {
455      if (!offset)
456	{
457	  close (execfd);
458	  goto retry_open;
459	}
460      ftruncate (execfd, offset);
461      return MFAIL;
462    }
463  else if (!offset
464	   && open_temp_exec_file_opts[open_temp_exec_file_opts_idx].repeat)
465    open_temp_exec_file_opts_next ();
466
467  start = mmap (start, length, prot, flags, execfd, offset);
468
469  if (start == MFAIL)
470    {
471      munmap (ptr, length);
472      ftruncate (execfd, offset);
473      return start;
474    }
475
476  mmap_exec_offset ((char *)start, length) = (char*)ptr - (char*)start;
477
478  execsize += length;
479
480  return start;
481}
482
483/* Map in a writable and executable chunk of memory if possible.
484   Failing that, fall back to dlmmap_locked.  */
485static void *
486dlmmap (void *start, size_t length, int prot,
487	int flags, int fd, off_t offset)
488{
489  void *ptr;
490
491  assert (start == NULL && length % malloc_getpagesize == 0
492	  && prot == (PROT_READ | PROT_WRITE)
493	  && flags == (MAP_PRIVATE | MAP_ANONYMOUS)
494	  && fd == -1 && offset == 0);
495
496#if FFI_CLOSURE_TEST
497  printf ("mapping in %zi\n", length);
498#endif
499
500  if (execfd == -1 && is_emutramp_enabled ())
501    {
502      ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
503      return ptr;
504    }
505
506  if (execfd == -1 && !is_selinux_enabled ())
507    {
508      ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
509
510      if (ptr != MFAIL || (errno != EPERM && errno != EACCES))
511	/* Cool, no need to mess with separate segments.  */
512	return ptr;
513
514      /* If MREMAP_DUP is ever introduced and implemented, try mmap
515	 with ((prot & ~PROT_WRITE) | PROT_EXEC) and mremap with
516	 MREMAP_DUP and prot at this point.  */
517    }
518
519  if (execsize == 0 || execfd == -1)
520    {
521      pthread_mutex_lock (&open_temp_exec_file_mutex);
522      ptr = dlmmap_locked (start, length, prot, flags, offset);
523      pthread_mutex_unlock (&open_temp_exec_file_mutex);
524
525      return ptr;
526    }
527
528  return dlmmap_locked (start, length, prot, flags, offset);
529}
530
531/* Release memory at the given address, as well as the corresponding
532   executable page if it's separate.  */
533static int
534dlmunmap (void *start, size_t length)
535{
536  /* We don't bother decreasing execsize or truncating the file, since
537     we can't quite tell whether we're unmapping the end of the file.
538     We don't expect frequent deallocation anyway.  If we did, we
539     could locate pages in the file by writing to the pages being
540     deallocated and checking that the file contents change.
541     Yuck.  */
542  msegmentptr seg = segment_holding (gm, start);
543  void *code;
544
545#if FFI_CLOSURE_TEST
546  printf ("unmapping %zi\n", length);
547#endif
548
549  if (seg && (code = add_segment_exec_offset (start, seg)) != start)
550    {
551      int ret = munmap (code, length);
552      if (ret)
553	return ret;
554    }
555
556  return munmap (start, length);
557}
558
559#if FFI_CLOSURE_FREE_CODE
560/* Return segment holding given code address.  */
561static msegmentptr
562segment_holding_code (mstate m, char* addr)
563{
564  msegmentptr sp = &m->seg;
565  for (;;) {
566    if (addr >= add_segment_exec_offset (sp->base, sp)
567	&& addr < add_segment_exec_offset (sp->base, sp) + sp->size)
568      return sp;
569    if ((sp = sp->next) == 0)
570      return 0;
571  }
572}
573#endif
574
575#endif /* !(defined(X86_WIN32) || defined(X86_WIN64) || defined(__OS2__)) || defined (__CYGWIN__) || defined(__INTERIX) */
576
577/* Allocate a chunk of memory with the given size.  Returns a pointer
578   to the writable address, and sets *CODE to the executable
579   corresponding virtual address.  */
580void *
581ffi_closure_alloc (size_t size, void **code)
582{
583  void *ptr;
584
585  if (!code)
586    return NULL;
587
588  ptr = dlmalloc (size);
589
590  if (ptr)
591    {
592      msegmentptr seg = segment_holding (gm, ptr);
593
594      *code = add_segment_exec_offset (ptr, seg);
595    }
596
597  return ptr;
598}
599
600/* Release a chunk of memory allocated with ffi_closure_alloc.  If
601   FFI_CLOSURE_FREE_CODE is nonzero, the given address can be the
602   writable or the executable address given.  Otherwise, only the
603   writable address can be provided here.  */
604void
605ffi_closure_free (void *ptr)
606{
607#if FFI_CLOSURE_FREE_CODE
608  msegmentptr seg = segment_holding_code (gm, ptr);
609
610  if (seg)
611    ptr = sub_segment_exec_offset (ptr, seg);
612#endif
613
614  dlfree (ptr);
615}
616
617
618#if FFI_CLOSURE_TEST
619/* Do some internal sanity testing to make sure allocation and
620   deallocation of pages are working as intended.  */
621int main ()
622{
623  void *p[3];
624#define GET(idx, len) do { p[idx] = dlmalloc (len); printf ("allocated %zi for p[%i]\n", (len), (idx)); } while (0)
625#define PUT(idx) do { printf ("freeing p[%i]\n", (idx)); dlfree (p[idx]); } while (0)
626  GET (0, malloc_getpagesize / 2);
627  GET (1, 2 * malloc_getpagesize - 64 * sizeof (void*));
628  PUT (1);
629  GET (1, 2 * malloc_getpagesize);
630  GET (2, malloc_getpagesize / 2);
631  PUT (1);
632  PUT (0);
633  PUT (2);
634  return 0;
635}
636#endif /* FFI_CLOSURE_TEST */
637# else /* ! FFI_MMAP_EXEC_WRIT */
638
639/* On many systems, memory returned by malloc is writable and
640   executable, so just use it.  */
641
642#include <stdlib.h>
643
644void *
645ffi_closure_alloc (size_t size, void **code)
646{
647  if (!code)
648    return NULL;
649
650  return *code = malloc (size);
651}
652
653void
654ffi_closure_free (void *ptr)
655{
656  free (ptr);
657}
658
659# endif /* ! FFI_MMAP_EXEC_WRIT */
660#endif /* FFI_CLOSURES */
661